From d24c9bb89c6afb9030f3f5b2b9d467e82e9b6210 Mon Sep 17 00:00:00 2001 From: Quinten Kock Date: Sat, 9 May 2026 12:58:38 +0200 Subject: [PATCH] Very basic .(git)ignore handling --- package-lock.json | 1 + package.json | 7 ++++--- src/app/foldernav.ts | 37 +++++++++++++++++++++++++++++++++++++ src/app/quickopen.ts | 25 ++++++++++++++++--------- 4 files changed, 58 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3d12b5d..c84de95 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41,6 +41,7 @@ "eslint": "^9.39.1", "eslint-plugin-import": "^2.32.0", "globals": "^16.5.0", + "ignore": "^7.0.5", "prettier": "^3.6.2", "tailwindcss": "^4.1.17", "typescript": "~5.9.3", diff --git a/package.json b/package.json index 45446df..81cd536 100644 --- a/package.json +++ b/package.json @@ -20,8 +20,8 @@ "license": "GPL-3.0-or-later", "devDependencies": { "@codemirror/language-data": "^6.5.2", - "@codemirror/theme-one-dark": "^6.1.3", "@codemirror/lsp-client": "^6.2.0", + "@codemirror/theme-one-dark": "^6.1.3", "@electron-forge/cli": "^7.10.2", "@electron-forge/maker-deb": "^7.10.2", "@electron-forge/maker-rpm": "^7.10.2", @@ -46,6 +46,7 @@ "eslint": "^9.39.1", "eslint-plugin-import": "^2.32.0", "globals": "^16.5.0", + "ignore": "^7.0.5", "prettier": "^3.6.2", "tailwindcss": "^4.1.17", "typescript": "~5.9.3", @@ -55,8 +56,8 @@ "vite": "^7.2.2" }, "dependencies": { + "@lydell/node-pty": "^1.1.0", "chokidar": "^5.0.0", - "electron-squirrel-startup": "^1.0.1", - "@lydell/node-pty": "^1.1.0" + "electron-squirrel-startup": "^1.0.1" } } diff --git a/src/app/foldernav.ts b/src/app/foldernav.ts index c98409c..8cbb000 100644 --- a/src/app/foldernav.ts +++ b/src/app/foldernav.ts @@ -3,6 +3,7 @@ const v = van.tags; import type { FolderTree } from "../types/global"; import { addEditor } from "./editorgrid"; import { OpenFile } from "./filestate"; +import ignore from "ignore"; import * as u from "./utils"; @@ -14,6 +15,37 @@ van.derive(() => { } }); +const ignoreInstance = van.state(null); + +async function updateIgnoreInstance() { + const root = folderTreeState.val?.path; + if (!root) { + ignoreInstance.val = null; + return; + } + + const ig = ignore(); + const files = [".gitignore", ".ignore"]; + for (const file of files) { + const data = await window.electronAPI.readFile(`${root}/${file}`).catch(() => (null as any)); + if (data && data.content) { + ig.add(data.content); + } + } + ignoreInstance.val = ig; +} + +van.derive(updateIgnoreInstance); + +export function shouldIgnore(filePath: string) { + const root = folderTreeState.val?.path; + if (!root || !ignoreInstance.val) return false; + if (!filePath.startsWith(root)) return false; + + const relativePath = filePath.slice(root.length).replace(/^[/\\]/, ""); + return ignoreInstance.val.ignores(relativePath); +} + async function openFolder() { const folderTree = await window.electronAPI.openFolder().catch(alert); if (!folderTree) return; @@ -50,6 +82,11 @@ window.electronAPI.onFsEvent(async (ev: { event: string; path: string }) => { } } + // If ignore files are changed, update ignore instance + if (ev.event === "change" && (ev.path.endsWith(".gitignore") || ev.path.endsWith(".ignore"))) { + updateIgnoreInstance(); + } + // If a file changed on disk and it's open, show disk version panels if (ev.event === "change" || ev.event === "add" || ev.event === "unlink") { const openFile = OpenFile.findOpenFile(ev.path); diff --git a/src/app/quickopen.ts b/src/app/quickopen.ts index e4f9b90..4774f3c 100644 --- a/src/app/quickopen.ts +++ b/src/app/quickopen.ts @@ -1,7 +1,7 @@ import van, { State } from "vanjs-core"; import { OpenFile } from "./filestate"; import { addEditor } from "./editorgrid"; -import { allFiles } from "./foldernav"; +import { allFiles, shouldIgnore } from "./foldernav"; const v = van.tags; @@ -10,8 +10,15 @@ export const QuickOpen = (() => { const query = van.state(""); const files = van.state([]); const filteredFiles = van.derive(() => { - if (!query.val) return files.val.slice(0, 100); - return files.val.filter(f => f.toLowerCase().includes(query.val.toLowerCase())).slice(0, 100); + let result = files.val; + if (query.val) { + result = result.filter(f => f.toLowerCase().includes(query.val.toLowerCase())); + } + + return result + .map(path => ({ path, ignored: shouldIgnore(path) })) + .sort((a, b) => (a.ignored === b.ignored ? 0 : a.ignored ? 1 : -1)) + .slice(0, 100); }); const selectedIndex = van.state(0); @@ -28,9 +35,9 @@ export const QuickOpen = (() => { }; const openSelectedFile = async () => { - const path = filteredFiles.val[selectedIndex.val]; - if (path) { - const file = await OpenFile.openFile(path); + const item = filteredFiles.val[selectedIndex.val]; + if (item) { + const file = await OpenFile.openFile(item.path); if (file) { addEditor(file); } @@ -96,11 +103,11 @@ export const QuickOpen = (() => { v.div( { class: "max-h-96 overflow-y-auto" }, () => v.ul( - filteredFiles.val.map((path, i) => v.div( + filteredFiles.val.map(({ path, ignored }, i) => v.div( { - class: () => `p-2 cursor-pointer hover:bg-blue-100 dark:hover:bg-blue-900 ${selectedIndex.val === i ? "bg-blue-200 dark:bg-blue-800" : ""}`, + class: () => `p-2 cursor-pointer hover:bg-blue-100 dark:hover:bg-blue-900 ${selectedIndex.val === i ? "bg-blue-200 dark:bg-blue-800" : ""} ${ignored ? "opacity-50" : ""}`, onclick: () => { - selectedIndex.val = filteredFiles.val.indexOf(path); + selectedIndex.val = i; openSelectedFile(); }, },