Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 2 additions & 13 deletions src/extension/legacy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { config } from "@/old-extension/config";
import { DiffDocProvider } from "@/old-extension/diffDocProvider";
import { ExtensionState } from "@/old-extension/extensionState";
import { registerMessageHandlers } from "@/old-extension/messageHandler";
import { RepoFileWatcher } from "@/old-extension/repoFileWatcher";
import { createRepoManager } from "@/old-extension/repoManager";
import { webviewBridgeFactory } from "@/old-extension/webviewBridge";
import type { WebviewBridge } from "@/old-extension/webviewBridge";
Expand All @@ -31,22 +30,15 @@ export function createMessageProtocol(ctx: vscode.ExtensionContext) {
attach(panel: vscode.WebviewPanel) {
let isPanelVisible = panel.visible;
let disposed = false;
let bridge: WebviewBridge;
const repoFileWatcher = new RepoFileWatcher(() => {
if (panel.visible) {
bridge.post({ command: "refresh" });
}
});
bridge = webviewBridgeFactory(panel.webview, repoFileWatcher);
const bridge: WebviewBridge = webviewBridgeFactory(panel.webview);
avatarManager.registerBridge(bridge.post);

const { onPanelShown } = registerMessageHandlers(bridge, {
config,
gitClient,
repoManager,
extensionState,
avatarManager,
repoFileWatcher
avatarManager
});
const viewStateListener = panel.onDidChangeViewState(() => {
if (panel.visible === isPanelVisible) {
Expand All @@ -55,8 +47,6 @@ export function createMessageProtocol(ctx: vscode.ExtensionContext) {
if (panel.visible) {
onPanelShown();
bridge.post({ command: "refresh" });
} else {
repoFileWatcher.stop();
}
isPanelVisible = panel.visible;
});
Expand All @@ -70,7 +60,6 @@ export function createMessageProtocol(ctx: vscode.ExtensionContext) {
bridge.dispose();
viewStateListener.dispose();
avatarManager.deregisterBridge();
repoFileWatcher.stop();
}
};
}
Expand Down
38 changes: 38 additions & 0 deletions src/extension/rpc/rpc-notify.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import * as vscode from "vscode";

import { logger } from "@/old-extension/utils/logger";
import type { RpcNotification, RpcNotificationMap, RpcNotificationName } from "@/types";

let _webview: vscode.Webview | undefined;

export const rpcNotify = {
async notify<N extends RpcNotificationName>(
name: N,
message: RpcNotificationMap[N]
): Promise<void> {
if (_webview === undefined) {
logger.log(`Skip RPC notification: ${name}; webview is not initialized`);
return;
}

const payload = {
kind: "rpc.notify",
id: crypto.randomUUID(),
name,
message
} as RpcNotification<N>;

logger.log(`Send RPC notification: ${name}`);
await _webview.postMessage(payload);
}
};

export function initRpcNotify(webview: vscode.Webview): vscode.Disposable {
_webview = webview;

return new vscode.Disposable(() => {
if (_webview === webview) {
_webview = undefined;
}
});
}
38 changes: 38 additions & 0 deletions src/extension/util/debounce.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import type * as vscode from "vscode";

import { logger } from "@/old-extension/utils/logger";

export type FsWatcherEvent = "created" | "deleted";

export function createDebouncer() {
const timers = new Map<string, ReturnType<typeof setTimeout>>();

return {
debounce(
type: FsWatcherEvent,
uri: vscode.Uri,
callback: (type: FsWatcherEvent, uri: vscode.Uri) => Promise<void>
): void {
const key = `${type}:${uri.toString()}`;
const timer = timers.get(key);

if (timer) {
clearTimeout(timer);
}

timers.set(
key,
setTimeout(() => {
timers.delete(key);
void callback(type, uri).catch((error: unknown) => {
logger.log(`Unable to process repository change: ${String(error)}`);
});
}, 100)
);
},
dispose(): void {
timers.values().forEach(clearTimeout);
timers.clear();
}
};
}
9 changes: 9 additions & 0 deletions src/extension/view-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import { extConfig } from "./config";
import { EXTENSION_NAME } from "./constants";
import { createWevbviewHtml } from "./html";
import { createMessageProtocol } from "./legacy";
import { initRpcNotify } from "./rpc/rpc-notify";
import { createRpcServer } from "./rpc/rpc-server";
import { watchGitRepo } from "./watchers/git-repo.watcher";
import { watchGitDir } from "./watchers/git.watcher";

export function createViewCommand(ctx: vscode.ExtensionContext) {
let currentPanel: vscode.WebviewPanel | undefined = undefined;
Expand Down Expand Up @@ -41,12 +44,18 @@ export function createViewCommand(ctx: vscode.ExtensionContext) {

const messageProtocolAttachment = messageProtocol.attach(webPanel);
const rpcListener = rpcServer.attach(webPanel.webview);
const rpcNotifier = initRpcNotify(webPanel.webview);
const gitDirWatcher = watchGitDir();
const gitRepoWatcher = watchGitRepo();

webPanel.webview.html = createWevbviewHtml(ctx, webPanel.webview);

webPanel.onDidDispose(() => {
messageProtocolAttachment.dispose();
rpcListener.dispose();
rpcNotifier.dispose();
gitDirWatcher.dispose();
gitRepoWatcher.dispose();
currentPanel = undefined;
});
currentPanel = webPanel;
Expand Down
96 changes: 96 additions & 0 deletions src/extension/watchers/git-repo.watcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import path from "node:path";

import * as vscode from "vscode";

import { rpcNotify } from "@/extension/rpc/rpc-notify";
import { logger } from "@/old-extension/utils/logger";

const REFRESH_DELAY = 750;
const GIT_DATA = /^(HEAD|config|index|packed-refs|refs(?:\/.*)?)$/;

let selectRepo: ((repo: string) => void) | undefined;
let muteDepth = 0;
let resumeAt = 0;

export function watchGitRepo(): vscode.Disposable {
let repoPath: string | undefined;
let watcher: vscode.FileSystemWatcher | undefined;
let refreshTimer: ReturnType<typeof setTimeout> | undefined;

const stop = () => {
watcher?.dispose();
watcher = undefined;
if (refreshTimer !== undefined) {
clearTimeout(refreshTimer);
refreshTimer = undefined;
}
};

selectRepo = (repo: string) => {
if (repo === repoPath && watcher !== undefined) {
return;
}

stop();
repoPath = repo;
watcher = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(repo, "**/*"));

const refresh = (uri: vscode.Uri) => {
if (muteDepth > 0 || Date.now() < resumeAt) {
return;
}

const relativePath = path.relative(repo, uri.fsPath).split(path.sep).join("/");
if (
relativePath.startsWith("../") ||
(relativePath.startsWith(".git/") && !GIT_DATA.test(relativePath.slice(5)))
) {
return;
}

logger.log(`Git repository file changed: ${uri.fsPath}`);
if (refreshTimer !== undefined) {
clearTimeout(refreshTimer);
}
refreshTimer = setTimeout(() => {
refreshTimer = undefined;
logger.log(`Git repository changed: ${repo}`);
void rpcNotify.notify("repo.updated", { path: repo });
}, REFRESH_DELAY);
};

watcher.onDidCreate(refresh);
watcher.onDidChange(refresh);
watcher.onDidDelete(refresh);
};

return new vscode.Disposable(() => {
selectRepo = undefined;
muteDepth = 0;
resumeAt = 0;
stop();
});
}

export function selectWatchedRepo(repo: string): void {
if (selectRepo === undefined) {
return;
}

selectRepo(repo);
}

export function muteGitRepoWatcher(): void {
muteDepth++;
}

export function unmuteGitRepoWatcher(): void {
if (muteDepth === 0) {
return;
}

muteDepth--;
if (muteDepth === 0) {
resumeAt = Date.now() + 1500;
}
}
37 changes: 37 additions & 0 deletions src/extension/watchers/git.watcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import path from "node:path";

import * as vscode from "vscode";

import { rpcNotify } from "@/extension/rpc/rpc-notify";
import { createDebouncer, type FsWatcherEvent } from "@/extension/util/debounce";
import { logger } from "@/old-extension/utils/logger";

export function watchGitDir(): vscode.Disposable {
const debouncer = createDebouncer();
const watcher = vscode.workspace.createFileSystemWatcher("**/.git", false, true, false);
const createListener = watcher.onDidCreate((uri) =>
debouncer.debounce("created", uri, processGitDir)
);
const deleteListener = watcher.onDidDelete((uri) =>
debouncer.debounce("deleted", uri, processGitDir)
);
return vscode.Disposable.from(watcher, createListener, deleteListener, debouncer);
}

async function processGitDir(type: FsWatcherEvent, uri: vscode.Uri) {
logger.log(`Git directory ${type}: ${uri.fsPath}`);
const repoPath = path.dirname(uri.fsPath);

if (type === "created") {
await rpcNotify.notify("repo.changed", {
type,
repo: {
name: path.basename(repoPath),
path: repoPath
}
});
return;
}

await rpcNotify.notify("repo.changed", { type, path: repoPath });
}
1 change: 0 additions & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ export function activate(ctx: vscode.ExtensionContext) {
if (!vscode.workspace.workspaceFolders || vscode.workspace.workspaceFolders.length <= 0) {
return;
}

logger.init(ctx);

const statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left);
Expand Down
16 changes: 5 additions & 11 deletions src/old-extension/initExtension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ import { gitClientFactory } from "@/backend/gitClient";
import type { GitClient } from "@/backend/gitClient";
import { findGitRepos } from "@/backend/queries/repoSearch";
import { buildExtensionUri } from "@/backend/utils/path";
import { watchGitRepo } from "@/extension/watchers/git-repo.watcher";
import { AvatarManager } from "@/old-extension/avatarManager";
import { config } from "@/old-extension/config";
import { EXTENSION_NAME } from "@/old-extension/constant/const";
import { DiffDocProvider } from "@/old-extension/diffDocProvider";
import { ExtensionState } from "@/old-extension/extensionState";
import { createMaxDepthTracker } from "@/old-extension/maxDepthTracker";
import { registerMessageHandlers } from "@/old-extension/messageHandler";
import { RepoFileWatcher } from "@/old-extension/repoFileWatcher";
import { createRepoManager } from "@/old-extension/repoManager";
import type { RepoManager } from "@/old-extension/repoManager";
import { StatusBarItem } from "@/old-extension/statusBarItem";
Expand Down Expand Up @@ -54,34 +54,28 @@ function registerViewCommand(
}
);

let bridge!: WebviewBridge;
const repoFileWatcher = new RepoFileWatcher(() => {
if (vsPanel.visible) {
bridge.post({ command: "refresh" });
}
});
bridge = webviewBridgeFactory(vsPanel.webview, repoFileWatcher);
const gitRepoWatcher = watchGitRepo();
const bridge: WebviewBridge = webviewBridgeFactory(vsPanel.webview);
avatarManager.registerBridge(bridge.post.bind(bridge));

const { onPanelShown } = registerMessageHandlers(bridge, {
config,
gitClient,
repoManager,
extensionState,
avatarManager,
repoFileWatcher
avatarManager
});

currentPanel = createWebviewPanel({
panel: vsPanel,
bridge,
config,
repoFileWatcher,
extensionPath: ctx.extensionPath,
extensionState,
avatarManager,
repoManager,
onDispose: () => {
gitRepoWatcher.dispose();
currentPanel = undefined;
},
onPanelShown
Expand Down
7 changes: 3 additions & 4 deletions src/old-extension/messageHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ import { loadBranches } from "@/backend/queries/loadBranches";
import { loadCommits } from "@/backend/queries/loadCommits";
import type { GitFileChangeType } from "@/backend/types";
import { abbrevCommit } from "@/backend/utils/string";
import { selectWatchedRepo } from "@/extension/watchers/git-repo.watcher";
import { AvatarManager } from "@/old-extension/avatarManager";
import type { Config } from "@/old-extension/config";
import { encodeDiffDocUri } from "@/old-extension/diffDocProvider";
import { ExtensionState } from "@/old-extension/extensionState";
import { RepoFileWatcher } from "@/old-extension/repoFileWatcher";
import type { RequestMessage, ResponseMessage } from "@/types";

import type { RepoManager } from "./repoManager";
Expand Down Expand Up @@ -65,10 +65,9 @@ export function registerMessageHandlers(
repoManager: RepoManager;
extensionState: ExtensionState;
avatarManager: AvatarManager;
repoFileWatcher: RepoFileWatcher;
}
) {
const { config, gitClient, repoManager, extensionState, avatarManager, repoFileWatcher } = deps;
const { config, gitClient, repoManager, extensionState, avatarManager } = deps;

let currentRepo: string | null = null;

Expand All @@ -79,7 +78,7 @@ export function registerMessageHandlers(
currentRepo = repo;
gitClient.setRepo(repo);
extensionState.setLastActiveRepo(repo);
repoFileWatcher.start(repo);
selectWatchedRepo(repo);
}

function registerAction<T extends RequestMessage["command"]>(
Expand Down
Loading
Loading