Compare commits

..

4 Commits

5 changed files with 63 additions and 16 deletions

1
package-lock.json generated
View File

@ -41,6 +41,7 @@
"eslint": "^9.39.1", "eslint": "^9.39.1",
"eslint-plugin-import": "^2.32.0", "eslint-plugin-import": "^2.32.0",
"globals": "^16.5.0", "globals": "^16.5.0",
"ignore": "^7.0.5",
"prettier": "^3.6.2", "prettier": "^3.6.2",
"tailwindcss": "^4.1.17", "tailwindcss": "^4.1.17",
"typescript": "~5.9.3", "typescript": "~5.9.3",

View File

@ -20,8 +20,8 @@
"license": "GPL-3.0-or-later", "license": "GPL-3.0-or-later",
"devDependencies": { "devDependencies": {
"@codemirror/language-data": "^6.5.2", "@codemirror/language-data": "^6.5.2",
"@codemirror/theme-one-dark": "^6.1.3",
"@codemirror/lsp-client": "^6.2.0", "@codemirror/lsp-client": "^6.2.0",
"@codemirror/theme-one-dark": "^6.1.3",
"@electron-forge/cli": "^7.10.2", "@electron-forge/cli": "^7.10.2",
"@electron-forge/maker-deb": "^7.10.2", "@electron-forge/maker-deb": "^7.10.2",
"@electron-forge/maker-rpm": "^7.10.2", "@electron-forge/maker-rpm": "^7.10.2",
@ -46,6 +46,7 @@
"eslint": "^9.39.1", "eslint": "^9.39.1",
"eslint-plugin-import": "^2.32.0", "eslint-plugin-import": "^2.32.0",
"globals": "^16.5.0", "globals": "^16.5.0",
"ignore": "^7.0.5",
"prettier": "^3.6.2", "prettier": "^3.6.2",
"tailwindcss": "^4.1.17", "tailwindcss": "^4.1.17",
"typescript": "~5.9.3", "typescript": "~5.9.3",
@ -55,8 +56,8 @@
"vite": "^7.2.2" "vite": "^7.2.2"
}, },
"dependencies": { "dependencies": {
"@lydell/node-pty": "^1.1.0",
"chokidar": "^5.0.0", "chokidar": "^5.0.0",
"electron-squirrel-startup": "^1.0.1", "electron-squirrel-startup": "^1.0.1"
"@lydell/node-pty": "^1.1.0"
} }
} }

View File

@ -3,6 +3,7 @@ const v = van.tags;
import type { FolderTree } from "../types/global"; import type { FolderTree } from "../types/global";
import { addEditor } from "./editorgrid"; import { addEditor } from "./editorgrid";
import { OpenFile } from "./filestate"; import { OpenFile } from "./filestate";
import ignore from "ignore";
import * as u from "./utils"; import * as u from "./utils";
@ -14,6 +15,37 @@ van.derive(() => {
} }
}); });
const ignoreInstance = van.state<ignore.Ignore | null>(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() { async function openFolder() {
const folderTree = await window.electronAPI.openFolder().catch(alert); const folderTree = await window.electronAPI.openFolder().catch(alert);
if (!folderTree) return; 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 a file changed on disk and it's open, show disk version panels
if (ev.event === "change" || ev.event === "add" || ev.event === "unlink") { if (ev.event === "change" || ev.event === "add" || ev.event === "unlink") {
const openFile = OpenFile.findOpenFile(ev.path); const openFile = OpenFile.findOpenFile(ev.path);
@ -86,7 +123,7 @@ export const FolderTreeView = () => {
u.InlineButton(refreshFolder, "Refresh current folder", "⟳"), u.InlineButton(refreshFolder, "Refresh current folder", "⟳"),
u.InlineButton(openFolder, "Open another folder", "📁"), u.InlineButton(openFolder, "Open another folder", "📁"),
), ),
folderTreeState.val.children?.map(FsItemView) || [], folderTreeState.val.children?.toSorted((a, b) => a.type.localeCompare(b.type)).map(FsItemView) || [],
); );
}; };
@ -104,9 +141,11 @@ const FsItemView = (tree: FolderTree): HTMLElement => {
tree.name, tree.name,
); );
const isOpen = van.state(false); const isOpen = van.state(false);
const sorted = tree.children?.toSorted((a, b) =>
a.type.localeCompare(b.type));
const children = () => const children = () =>
isOpen.val isOpen.val
? v.ul({ class: "pl-4" }, tree.children?.map(FsItemView)) ? v.ul({ class: "pl-4" }, sorted.map(FsItemView))
: v.div({ ariaBusy: true }); : v.div({ ariaBusy: true });
const folder = v.details( const folder = v.details(
{ {

View File

@ -50,7 +50,7 @@ function jumpToOrigin(view: EditorView, type: { get: typeof getDefinition, capab
: plugin.fromPosition(loc.range.start, target.view.state.doc); : plugin.fromPosition(loc.range.start, target.view.state.doc);
target.view.dispatch({ target.view.dispatch({
selection: {anchor: pos}, selection: {anchor: pos},
scrollIntoView: true, effects: EditorView.scrollIntoView(pos, {y: "start"}),
userEvent: "select.definition", userEvent: "select.definition",
}); });
setTimeout(() => target.focus(), 0); setTimeout(() => target.focus(), 0);

View File

@ -1,7 +1,7 @@
import van, { State } from "vanjs-core"; import van, { State } from "vanjs-core";
import { OpenFile } from "./filestate"; import { OpenFile } from "./filestate";
import { addEditor } from "./editorgrid"; import { addEditor } from "./editorgrid";
import { allFiles } from "./foldernav"; import { allFiles, shouldIgnore } from "./foldernav";
const v = van.tags; const v = van.tags;
@ -10,8 +10,15 @@ export const QuickOpen = (() => {
const query = van.state(""); const query = van.state("");
const files = van.state([]); const files = van.state([]);
const filteredFiles = van.derive(() => { const filteredFiles = van.derive(() => {
if (!query.val) return files.val.slice(0, 100); let result = files.val;
return files.val.filter(f => f.toLowerCase().includes(query.val.toLowerCase())).slice(0, 100); 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); const selectedIndex = van.state(0);
@ -28,9 +35,9 @@ export const QuickOpen = (() => {
}; };
const openSelectedFile = async () => { const openSelectedFile = async () => {
const path = filteredFiles.val[selectedIndex.val]; const item = filteredFiles.val[selectedIndex.val];
if (path) { if (item) {
const file = await OpenFile.openFile(path); const file = await OpenFile.openFile(item.path);
if (file) { if (file) {
addEditor(file); addEditor(file);
} }
@ -59,7 +66,6 @@ export const QuickOpen = (() => {
const open = async () => { const open = async () => {
query.val = ""; query.val = "";
files.val = allFiles.val; files.val = allFiles.val;
console.log("setting allfiles to ", allFiles.val);
isOpen.val = true; isOpen.val = true;
// Focus the input after the DOM is updated // Focus the input after the DOM is updated
@ -97,11 +103,11 @@ export const QuickOpen = (() => {
v.div( v.div(
{ class: "max-h-96 overflow-y-auto" }, { class: "max-h-96 overflow-y-auto" },
() => v.ul( () => 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: () => { onclick: () => {
selectedIndex.val = filteredFiles.val.indexOf(path); selectedIndex.val = i;
openSelectedFile(); openSelectedFile();
}, },
}, },