Files
zed-sync/scripts/toggle-file-scan-exclusions.mjs
T

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"}.`);