89 lines
3.2 KiB
JavaScript
89 lines
3.2 KiB
JavaScript
import { homedir } from "node:os";
|
|
import { dirname, join } from "node:path";
|
|
import { readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const scriptDirectory = dirname(fileURLToPath(import.meta.url));
|
|
|
|
async function exclusionRules() {
|
|
const rulesPath = join(scriptDirectory, "file-exclusions.json");
|
|
const rules = JSON.parse(await readFile(rulesPath, "utf8"));
|
|
if (!Array.isArray(rules.defaults) || !Array.isArray(rules.custom)) {
|
|
throw new Error("file-exclusions.json must contain defaults and custom arrays");
|
|
}
|
|
return {
|
|
defaults: [...new Set(rules.defaults)],
|
|
custom: [...new Set(rules.custom)].filter(
|
|
(entry) => !rules.defaults.includes(entry),
|
|
),
|
|
};
|
|
}
|
|
|
|
function settingsPath() {
|
|
if (process.platform === "win32") {
|
|
const appData = process.env.APPDATA;
|
|
if (!appData) throw new Error("APPDATA is not set");
|
|
return join(appData, "Zed", "settings.json");
|
|
}
|
|
return join(homedir(), ".config", "zed", "settings.json");
|
|
}
|
|
|
|
function findArrayRange(source) {
|
|
const property = /"file_scan_exclusions"\s*:/g.exec(source);
|
|
if (!property) throw new Error("file_scan_exclusions is missing from settings.json");
|
|
|
|
const start = source.indexOf("[", property.index + property[0].length);
|
|
if (start === -1) throw new Error("file_scan_exclusions is not an array");
|
|
|
|
let inString = false;
|
|
let escaped = false;
|
|
let depth = 0;
|
|
for (let index = start; index < source.length; index += 1) {
|
|
const character = source[index];
|
|
if (inString) {
|
|
if (escaped) escaped = false;
|
|
else if (character === "\\") escaped = true;
|
|
else if (character === '"') inString = false;
|
|
continue;
|
|
}
|
|
if (character === '"') inString = true;
|
|
else if (character === "[") depth += 1;
|
|
else if (character === "]" && --depth === 0) return { start, end: index + 1 };
|
|
}
|
|
throw new Error("file_scan_exclusions array is not closed");
|
|
}
|
|
|
|
function formatArray(values, indent, newline) {
|
|
const itemIndent = `${indent} `;
|
|
const items = values.map((value) => `${itemIndent}${JSON.stringify(value)}`);
|
|
return `[${newline}${items.join(`,${newline}`)}${newline}${indent}]`;
|
|
}
|
|
|
|
async function replaceAtomically(path, contents) {
|
|
const temporaryPath = join(dirname(path), `.settings.json.${process.pid}.tmp`);
|
|
try {
|
|
await writeFile(temporaryPath, contents, "utf8");
|
|
await rename(temporaryPath, path);
|
|
} finally {
|
|
await rm(temporaryPath, { force: true });
|
|
}
|
|
}
|
|
|
|
const path = settingsPath();
|
|
const { defaults, custom } = await exclusionRules();
|
|
const source = await readFile(path, "utf8");
|
|
const newline = source.includes("\r\n") ? "\r\n" : "\n";
|
|
const range = findArrayRange(source);
|
|
const current = JSON.parse(source.slice(range.start, range.end));
|
|
const customIsActive = current.some((entry) => !defaults.includes(entry));
|
|
const next = customIsActive ? defaults : [...defaults, ...custom];
|
|
const lineStart = source.lastIndexOf("\n", range.start) + 1;
|
|
const indent = source.slice(lineStart, range.start).match(/^\s*/)?.[0] ?? "";
|
|
const updated =
|
|
source.slice(0, range.start) +
|
|
formatArray(next, indent, newline) +
|
|
source.slice(range.end);
|
|
|
|
await replaceAtomically(path, updated);
|
|
console.log(`Excluded files are now ${customIsActive ? "shown" : "hidden"}.`);
|