Skip to content
This repository was archived by the owner on Sep 23, 2025. It is now read-only.

Commit d73536b

Browse files
committed
connect the find definitions
1 parent 4b8b478 commit d73536b

File tree

4 files changed

+239
-171
lines changed

4 files changed

+239
-171
lines changed

extension/src/extension.ts

Lines changed: 114 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -42,16 +42,7 @@ interface ResolveSymbolPayload {
4242
}
4343

4444
interface FindReferencesPayload {
45-
symbol: {
46-
name: string;
47-
location: {
48-
file: string;
49-
line: number;
50-
column: number;
51-
context: string;
52-
};
53-
extra: any;
54-
};
45+
symbol: SymbolDef;
5546
}
5647

5748
interface ResponsePayload {
@@ -60,14 +51,41 @@ interface ResponsePayload {
6051
data?: any;
6152
}
6253

54+
// 💡: Corresponds to `dialectic_mcp_server::ide::SymbolRef` in the Rust code
55+
interface SymbolDef {
56+
name: String,
57+
kind?: String,
58+
definedAt: FileRange,
59+
}
60+
61+
// 💡: Corresponds to `dialectic_mcp_server::ide::SymbolRef` in the Rust code
62+
interface SymbolRef {
63+
definition: SymbolDef,
64+
referencedAt: FileLocation,
65+
}
66+
67+
// 💡: Corresponds to `dialectic_mcp_server::ide::FileRange` in the Rust code
68+
interface FileRange {
69+
path: string,
70+
start: FileLocation,
71+
end: FileLocation,
72+
content?: string,
73+
}
74+
75+
// 💡: Corresponds to `dialectic_mcp_server::ide::FileLocation` in the Rust code
76+
interface FileLocation {
77+
line: number, // 💡: 1-based, vscode is 0-based
78+
column: number, // 💡: 1-based, vscode is 0-based
79+
}
80+
6381
// 💡: Daemon client for connecting to message bus
6482
class DaemonClient implements vscode.Disposable {
6583
private socket: net.Socket | null = null;
6684
private reconnectTimer: NodeJS.Timeout | null = null;
6785
private isDisposed = false;
6886
private buffer = '';
6987
private readonly RECONNECT_INTERVAL_MS = 5000; // 5 seconds
70-
88+
7189
// Terminal registry: track active shell PIDs with MCP servers
7290
private activeTerminals: Set<number> = new Set();
7391

@@ -199,7 +217,7 @@ class DaemonClient implements vscode.Disposable {
199217
// Handle Polo messages - MCP server announcing presence
200218
try {
201219
this.outputChannel.appendLine(`[DISCOVERY] MCP server connected in terminal PID ${message.shellPid}`);
202-
220+
203221
// Add to terminal registry for Ask Socratic Shell integration
204222
this.activeTerminals.add(message.shellPid);
205223
this.outputChannel.appendLine(`[REGISTRY] Active terminals: [${Array.from(this.activeTerminals).join(', ')}]`);
@@ -210,7 +228,7 @@ class DaemonClient implements vscode.Disposable {
210228
// Handle Goodbye messages - MCP server announcing departure
211229
try {
212230
this.outputChannel.appendLine(`[DISCOVERY] MCP server disconnected from terminal PID ${message.shellPid}`);
213-
231+
214232
// Remove from terminal registry for Ask Socratic Shell integration
215233
this.activeTerminals.delete(message.shellPid);
216234
this.outputChannel.appendLine(`[REGISTRY] Active terminals: [${Array.from(this.activeTerminals).join(', ')}]`);
@@ -226,10 +244,10 @@ class DaemonClient implements vscode.Disposable {
226244
const symbolPayload = message.payload as ResolveSymbolPayload;
227245

228246
this.outputChannel.appendLine(`[LSP] Resolving symbol: ${symbolPayload.name}`);
229-
247+
230248
// Call VSCode's LSP to find symbol definitions
231249
const symbols = await this.resolveSymbolByName(symbolPayload.name);
232-
250+
233251
this.sendResponse(message.id, {
234252
success: true,
235253
data: symbols
@@ -247,10 +265,10 @@ class DaemonClient implements vscode.Disposable {
247265
const referencesPayload = message.payload as FindReferencesPayload;
248266

249267
this.outputChannel.appendLine(`[LSP] Finding references for symbol: ${referencesPayload.symbol.name}`);
250-
268+
251269
// Call VSCode's LSP to find all references
252270
const references = await this.findAllReferences(referencesPayload.symbol);
253-
271+
254272
this.sendResponse(message.id, {
255273
success: true,
256274
data: references
@@ -346,16 +364,17 @@ class DaemonClient implements vscode.Disposable {
346364
};
347365
}
348366

349-
private sendResponse(messageId: string, response: { success: boolean; error?: string; data?: any }): void {
367+
private sendResponse(messageId: string, response: ResponsePayload): void {
350368
if (!this.socket || this.socket.destroyed) {
351369
this.outputChannel.appendLine(`Cannot send response - socket not connected`);
352370
return;
353371
}
354372

355-
const responseMessage = {
373+
const responseMessage: IPCMessage = {
356374
type: 'response',
357375
payload: response,
358-
id: messageId
376+
id: messageId,
377+
shellPid: 0,
359378
};
360379

361380
try {
@@ -414,7 +433,7 @@ class DaemonClient implements vscode.Disposable {
414433
/**
415434
* Resolve symbol by name using VSCode's LSP
416435
*/
417-
private async resolveSymbolByName(symbolName: string): Promise<any[]> {
436+
private async resolveSymbolByName(symbolName: string): Promise<SymbolDef[]> {
418437
try {
419438
// Get all workspace symbols matching the name
420439
const symbols = await vscode.commands.executeCommand<vscode.SymbolInformation[]>(
@@ -427,19 +446,7 @@ class DaemonClient implements vscode.Disposable {
427446
}
428447

429448
// Convert VSCode symbols to our format
430-
const resolvedSymbols = symbols.map(symbol => ({
431-
name: symbol.name,
432-
location: {
433-
file: vscode.workspace.asRelativePath(symbol.location.uri),
434-
line: symbol.location.range.start.line + 1, // Convert to 1-based
435-
column: symbol.location.range.start.character, // Keep 0-based
436-
context: this.getContextAroundLocation(symbol.location.uri, symbol.location.range.start.line)
437-
},
438-
extra: {
439-
kind: vscode.SymbolKind[symbol.kind],
440-
containerName: symbol.containerName
441-
}
442-
}));
449+
const resolvedSymbols: SymbolDef[] = symbols.map(symbol => this.vscodeSymbolToSymbolDef(symbol));
443450

444451
return resolvedSymbols;
445452
} catch (error) {
@@ -448,67 +455,85 @@ class DaemonClient implements vscode.Disposable {
448455
}
449456
}
450457

458+
private vscodeSymbolToSymbolDef(symbol: vscode.SymbolInformation): SymbolDef {
459+
let definedAt = symbol.location
460+
let result: SymbolDef = {
461+
name: symbol.name,
462+
definedAt: this.vscodeLocationToRange(symbol.location),
463+
};
464+
465+
switch (symbol.kind) {
466+
case vscode.SymbolKind.File: result.kind = "File"; break;
467+
case vscode.SymbolKind.Module: result.kind = "Module"; break;
468+
case vscode.SymbolKind.Namespace: result.kind = "Namespace"; break;
469+
case vscode.SymbolKind.Package: result.kind = "Package"; break;
470+
case vscode.SymbolKind.Class: result.kind = "Class"; break;
471+
case vscode.SymbolKind.Method: result.kind = "Method"; break;
472+
case vscode.SymbolKind.Property: result.kind = "Property"; break;
473+
case vscode.SymbolKind.Field: result.kind = "Field"; break;
474+
case vscode.SymbolKind.Constructor: result.kind = "Constructor"; break;
475+
case vscode.SymbolKind.Enum: result.kind = "Enum"; break;
476+
case vscode.SymbolKind.Interface: result.kind = "Interface"; break;
477+
case vscode.SymbolKind.Function: result.kind = "Function"; break;
478+
case vscode.SymbolKind.Variable: result.kind = "Variable"; break;
479+
case vscode.SymbolKind.Constant: result.kind = "Constant"; break;
480+
case vscode.SymbolKind.String: result.kind = "String"; break;
481+
case vscode.SymbolKind.Number: result.kind = "Number"; break;
482+
case vscode.SymbolKind.Boolean: result.kind = "Boolean"; break;
483+
case vscode.SymbolKind.Array: result.kind = "Array"; break;
484+
case vscode.SymbolKind.Object: result.kind = "Object"; break;
485+
case vscode.SymbolKind.Key: result.kind = "Key"; break;
486+
case vscode.SymbolKind.Null: result.kind = "Null"; break;
487+
case vscode.SymbolKind.EnumMember: result.kind = "EnumMember"; break;
488+
case vscode.SymbolKind.Struct: result.kind = "Struct"; break;
489+
case vscode.SymbolKind.Event: result.kind = "Event"; break;
490+
case vscode.SymbolKind.Operator: result.kind = "Operator"; break;
491+
case vscode.SymbolKind.TypeParameter: result.kind = "TypeParameter"; break;
492+
}
493+
494+
return result;
495+
}
496+
497+
private vscodeLocationToRange(location: vscode.Location): FileRange {
498+
return {
499+
path: location.uri.fsPath,
500+
start: {
501+
line: location.range.start.line + 1,
502+
column: location.range.start.character + 1,
503+
},
504+
end: {
505+
line: location.range.end.line + 1,
506+
column: location.range.end.character + 1,
507+
},
508+
};
509+
}
510+
511+
451512
/**
452513
* Find all references to a symbol using VSCode's LSP
453514
*/
454-
private async findAllReferences(symbol: any): Promise<any[]> {
515+
private async findAllReferences(symbol: SymbolDef): Promise<FileRange[]> {
455516
try {
456517
// Convert relative path back to URI
457518
const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
458519
if (!workspaceFolder) {
459520
throw new Error('No workspace folder found');
460521
}
461522

462-
const fileUri = vscode.Uri.joinPath(workspaceFolder.uri, symbol.location.file);
463-
const position = new vscode.Position(
464-
symbol.location.line - 1, // Convert from 1-based to 0-based
465-
symbol.location.column // Already 0-based
466-
);
467-
468523
// Find all references using LSP
469-
const references = await vscode.commands.executeCommand<vscode.Location[]>(
524+
const locations = await vscode.commands.executeCommand<vscode.Location[]>(
470525
'vscode.executeReferenceProvider',
471-
fileUri,
472-
position
526+
vscode.Uri.joinPath(workspaceFolder.uri, symbol.definedAt.path),
527+
new vscode.Position(symbol.definedAt.start.line - 1, symbol.definedAt.start.column - 1)
473528
);
474529

475-
if (!references || references.length === 0) {
476-
return [];
477-
}
478-
479-
// Convert VSCode locations to our format
480-
const referenceLocations = references.map(ref => ({
481-
file: vscode.workspace.asRelativePath(ref.uri),
482-
line: ref.range.start.line + 1, // Convert to 1-based
483-
column: ref.range.start.character, // Keep 0-based
484-
context: this.getContextAroundLocation(ref.uri, ref.range.start.line)
485-
}));
486-
487-
return referenceLocations;
530+
return locations.map(location => this.vscodeLocationToRange(location));
488531
} catch (error) {
489532
this.outputChannel.appendLine(`Error in findAllReferences: ${error}`);
490533
throw error;
491534
}
492535
}
493536

494-
/**
495-
* Get context around a specific location for display purposes
496-
*/
497-
private getContextAroundLocation(uri: vscode.Uri, line: number): string {
498-
try {
499-
// Try to get the document if it's already open
500-
const document = vscode.workspace.textDocuments.find(doc => doc.uri.toString() === uri.toString());
501-
if (document && line < document.lineCount) {
502-
return document.lineAt(line).text.trim();
503-
}
504-
505-
// If document isn't open, return a placeholder
506-
return `Line ${line + 1}`;
507-
} catch (error) {
508-
return `Line ${line + 1}`;
509-
}
510-
}
511-
512537
dispose(): void {
513538
this.isDisposed = true;
514539
this.clearReconnectTimer();
@@ -689,10 +714,10 @@ async function findQChatTerminal(outputChannel: vscode.OutputChannel, daemonClie
689714
terminals.map(async (terminal) => {
690715
// Extract the shell PID from the terminal (async)
691716
const shellPID = await terminal.processId;
692-
717+
693718
// Log terminal for debugging
694719
outputChannel.appendLine(` Checking terminal: "${terminal.name}" (PID: ${shellPID})`);
695-
720+
696721
// Check if this terminal's shell PID is in our active registry
697722
if (shellPID && activeTerminals.has(shellPID)) {
698723
outputChannel.appendLine(` ✅ Terminal "${terminal.name}" has active MCP server (PID: ${shellPID})`);
@@ -721,17 +746,17 @@ async function findQChatTerminal(outputChannel: vscode.OutputChannel, daemonClie
721746
// 💡: Multiple AI-enabled terminals - show picker UI with memory
722747
if (aiEnabledTerminals.length > 1) {
723748
outputChannel.appendLine(`Multiple AI-enabled terminals found: ${aiEnabledTerminals.length}`);
724-
749+
725750
// Get previously selected terminal PID from workspace state
726751
const lastSelectedPID = context.workspaceState.get<number>('dialectic.lastSelectedTerminalPID');
727752
outputChannel.appendLine(`Last selected terminal PID: ${lastSelectedPID}`);
728-
753+
729754
// Create picker items with terminal info
730755
interface TerminalQuickPickItem extends vscode.QuickPickItem {
731756
terminal: vscode.Terminal;
732757
pid: number | undefined;
733758
}
734-
759+
735760
const terminalItems: TerminalQuickPickItem[] = await Promise.all(
736761
aiEnabledTerminals.map(async (terminal): Promise<TerminalQuickPickItem> => {
737762
const pid = await terminal.processId;
@@ -750,10 +775,10 @@ async function findQChatTerminal(outputChannel: vscode.OutputChannel, daemonClie
750775

751776
// Find the last selected terminal for the quick option
752777
const lastSelectedItem = terminalItems.find(item => item.pid === lastSelectedPID);
753-
778+
754779
// Create picker items with optional "use last" entry at top
755780
const pickerItems: TerminalQuickPickItem[] = [];
756-
781+
757782
// Add "use last terminal" option if we have a previous selection
758783
if (lastSelectedItem) {
759784
pickerItems.push({
@@ -763,7 +788,7 @@ async function findQChatTerminal(outputChannel: vscode.OutputChannel, daemonClie
763788
terminal: lastSelectedItem.terminal,
764789
pid: lastSelectedItem.pid
765790
});
766-
791+
767792
// Add separator
768793
pickerItems.push({
769794
label: '$(dash) All available terminals',
@@ -774,13 +799,13 @@ async function findQChatTerminal(outputChannel: vscode.OutputChannel, daemonClie
774799
kind: vscode.QuickPickItemKind.Separator
775800
});
776801
}
777-
802+
778803
// Add all terminals (keeping natural order)
779804
pickerItems.push(...terminalItems);
780805

781806
// Show the picker to user
782807
const selectedItem = await vscode.window.showQuickPick(pickerItems, {
783-
placeHolder: lastSelectedItem
808+
placeHolder: lastSelectedItem
784809
? 'Select terminal for AI chat (first option = quick access to last used)'
785810
: 'Select terminal for AI chat',
786811
title: 'Multiple AI-enabled terminals found'
@@ -792,13 +817,13 @@ async function findQChatTerminal(outputChannel: vscode.OutputChannel, daemonClie
792817
outputChannel.appendLine('User selected separator or invalid item, ignoring');
793818
return null;
794819
}
795-
820+
796821
outputChannel.appendLine(`User selected terminal: ${selectedItem.terminal.name} (PID: ${selectedItem.pid})`);
797-
822+
798823
// Remember this selection for next time
799824
await context.workspaceState.update('dialectic.lastSelectedTerminalPID', selectedItem.pid);
800825
outputChannel.appendLine(`Saved terminal PID ${selectedItem.pid} as last selected`);
801-
826+
802827
return selectedItem.terminal;
803828
} else {
804829
outputChannel.appendLine('User cancelled terminal selection');
@@ -808,7 +833,7 @@ async function findQChatTerminal(outputChannel: vscode.OutputChannel, daemonClie
808833

809834
// 💡: No AI-enabled terminals found - fall back to old logic for compatibility
810835
outputChannel.appendLine('No AI-enabled terminals found, falling back to name-based detection');
811-
836+
812837
if (terminals.length === 1) {
813838
const terminal = terminals[0];
814839
outputChannel.appendLine(`Using single terminal (fallback): ${terminal.name}`);

0 commit comments

Comments
 (0)