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
8 changes: 0 additions & 8 deletions vscode-extension/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,14 +292,6 @@ export async function activate(context: vscode.ExtensionContext) {
});
context.subscriptions.push(gitService);
await gitService.setupGitListener(context);

// Register listener for disposal
context.subscriptions.push({
dispose: () => {
lspClient.stop();
},
});

// Index commands initially
await commandIndexer.indexCommands();

Expand Down
23 changes: 17 additions & 6 deletions vscode-extension/src/lsp-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export class DeepLensLspClient implements ISearchProvider {
private readonly context: vscode.ExtensionContext;
private isStopping = false;
private startPromise: Promise<void> | null = null;
private stopPromise: Promise<void> | null = null;
private disposables: vscode.Disposable[] = [];
public onProgress = new vscode.EventEmitter<{
state: 'start' | 'report' | 'end';
Expand Down Expand Up @@ -280,18 +281,28 @@ export class DeepLensLspClient implements ISearchProvider {
}

async stop(): Promise<void> {
this.isStopping = true;
if (this.stopPromise) {
await this.stopPromise;
return;
}

this.stopPromise = (async () => {
this.isStopping = true;

await this.waitForStartCompletion();
await this.stopClientInstance();

await this.waitForStartCompletion();
await this.stopClientInstance();
this.disposeTrackedDisposables();

this.disposeTrackedDisposables();
this.onProgress.dispose();
this.onRipgrepUnavailable.dispose();
})();

this.onProgress.dispose();
await this.stopPromise;
}

private async waitForStartCompletion(): Promise<void> {
if (this.startPromise === undefined) {
if (this.startPromise === null) {
return;
}

Expand Down
12 changes: 9 additions & 3 deletions vscode-extension/src/reference-code-lens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ enum CodeLensType {
IMPLEMENTATION = 'implementation',
}


function isSymbolInformationArray(
symbols: vscode.DocumentSymbol[] | vscode.SymbolInformation[],
): symbols is vscode.SymbolInformation[] {
return symbols.length > 0 && 'containerName' in symbols[0];
}

/**
* CodeLens provider that shows reference and implementation counts above symbols
* Similar to JetBrains Rider's "Code Vision" feature
Expand Down Expand Up @@ -74,9 +81,8 @@ export class ReferenceCodeLensProvider implements vscode.CodeLensProvider {
// Check if we got DocumentSymbol[] (hierarchy) or SymbolInformation[] (flat)
if (symbols.length > 0 && 'children' in symbols[0]) {
this.collectSymbols(symbols as vscode.DocumentSymbol[], codeLenses, document);
} else if (symbols.length > 0 && 'containerName' in symbols[0]) {
// Handle flat SymbolInformation[]
this.collectFlatSymbols(symbols as unknown as vscode.SymbolInformation[], codeLenses, document);
} else if (isSymbolInformationArray(symbols)) {
this.collectFlatSymbols(symbols, codeLenses, document);
}
} catch (error) {
console.error('[DeepLens] Error getting document symbols:', error);
Expand Down
3 changes: 1 addition & 2 deletions vscode-extension/src/search-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1869,8 +1869,7 @@ export class SearchProvider {
}

return {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
label: label as any,
label,
description,
detail,
iconPath: iconPath,
Expand Down
16 changes: 13 additions & 3 deletions vscode-extension/src/services/git-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export interface GitAPI {

export class GitService {
private gitChangeDebounce: NodeJS.Timeout | undefined;
private readonly repoListeners: vscode.Disposable[] = [];
private readonly onRepoChange: () => Promise<void>;

constructor(onRepoChange: () => Promise<void>) {
Expand Down Expand Up @@ -62,11 +63,14 @@ export class GitService {
let lastHead = repository.state.HEAD?.commit || repository.state.HEAD?.name;
logger.log(`Monitoring repository at ${repository.rootUri.fsPath} (Initial HEAD: ${lastHead})`);

repository.state.onDidChange(() => {
const stateDisposable = repository.state.onDidChange(() => {
const currentHead = repository.state.HEAD?.commit || repository.state.HEAD?.name;

if (currentHead !== lastHead) {
logger.log(`Git detected HEAD change in ${repository.rootUri.fsPath}: ${lastHead} -> ${currentHead}`);
const previousHead = lastHead;
logger.log(
`Git detected HEAD change in ${repository.rootUri.fsPath}: ${previousHead} -> ${currentHead}`,
);
lastHead = currentHead;

if (this.gitChangeDebounce) {
Expand All @@ -75,18 +79,24 @@ export class GitService {

this.gitChangeDebounce = setTimeout(async () => {
logger.log(
`[Git Event] Head moved from ${lastHead} to ${currentHead}. Triggering full workspace refresh.`,
`[Git Event] Head moved from ${previousHead} to ${currentHead}. Triggering full workspace refresh.`,
);
await this.onRepoChange();
logger.log('[Git Event] Workspace refresh finished.');
}, 3000);
}
});

this.repoListeners.push(stateDisposable);
}

public dispose(): void {
if (this.gitChangeDebounce) {
clearTimeout(this.gitChangeDebounce);
}
for (const disposable of this.repoListeners) {
disposable.dispose();
}
this.repoListeners.length = 0;
}
}
34 changes: 25 additions & 9 deletions vscode-extension/src/test/benchmark/extension.bench.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as assert from 'node:assert';
import * as fs from 'node:fs';
import * as path from 'node:path';
import * as vscode from 'vscode';
import { SearchOptions, SearchScope } from '../../../../language-server/src/core/types';

const results: { name: string; avgMs: number; totalMs: number }[] = [];

Expand Down Expand Up @@ -54,25 +55,40 @@ suite('Extension Performance Test Suite', () => {
});

test('Command Execution: deeplens.search', async () => {
// Wait for extension to be ready
const extension = vscode.extensions.getExtension('AhmedSamir.deeplens');
await extension?.activate();

// Allow some time for language server to start
await new Promise((r) => setTimeout(r, 2000));

await benchmark(
'Execute Search Command',
async () => {
// We just trigger the command, we can't easily measure until results appear without more complex hooks
// But checking that the command triggers without error is a start.
// In a real scenario, we might want to expose an API to wait for search completion.
await vscode.commands.executeCommand('deeplens.search');

// Close the quick pick if possible (Escape)
await vscode.commands.executeCommand('workbench.action.closeQuickOpen');
},
5,
);
});

test('Search Request Latency: deeplens/search', async () => {
const extension = vscode.extensions.getExtension('AhmedSamir.deeplens');
assert.ok(extension, 'Extension should be present');
const api = extension.isActive ? extension.exports : await extension.activate();

// Allow the LSP process to start and complete initialization.
await new Promise((resolve) => setTimeout(resolve, 2000));

const options: SearchOptions = {
query: 'test',
maxResults: 50,
scope: SearchScope.EVERYTHING,
};

await benchmark(
'Search Request Latency',
async () => {
const results = await api.lspClient.search(options);
assert.ok(Array.isArray(results), 'Search should return an array');
},
10,
);
});
});