From 2300559081a4b8cf48c5185f6a74b7f8f6d51ac0 Mon Sep 17 00:00:00 2001 From: Mathew Pareles Date: Sat, 8 Mar 2025 01:01:16 -0800 Subject: [PATCH] links to code --- .../contrib/void/browser/chatThreadService.ts | 146 ++++++++---------- .../react/src/markdown/ChatMarkdownRender.tsx | 31 ++-- 2 files changed, 77 insertions(+), 100 deletions(-) diff --git a/src/vs/workbench/contrib/void/browser/chatThreadService.ts b/src/vs/workbench/contrib/void/browser/chatThreadService.ts index 205568d4..44bfefe0 100644 --- a/src/vs/workbench/contrib/void/browser/chatThreadService.ts +++ b/src/vs/workbench/contrib/void/browser/chatThreadService.ts @@ -293,7 +293,7 @@ class ChatThreadService extends Disposable implements IChatThreadService { // should probably re-use code from void/src/vs/base/common/marshalling.ts instead. but this is simple enough private _convertThreadDataFromStorage(threadsStr: string): ChatThreads { return JSON.parse(threadsStr, (key, value) => { - if (value && typeof value === 'object' && value.$mid === 1) { //$mid is the MarshalledId. $mid === 1 means it is a URI + if (value && typeof value === 'object' && value.$mid === 1) { // $mid is the MarshalledId. $mid === 1 means it is a URI return URI.from(value); } return value; @@ -587,22 +587,27 @@ class ChatThreadService extends Disposable implements IChatThreadService { // process codespan to understand what we are searching for // TODO account for more complicated patterns eg `ITextEditorService.openEditor()` - const filePattern = /^[^\s.]+\.[^\s.]+$/; - const functionPattern = /^[^\s(]+\([^)]*\)$/; + const functionOrMethodPattern = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/; // `fUnCt10n_name` + const functionParensPattern = /^([^\s(]+)\([^)]*\)$/; // `functionName( args )` let target = _codespanStr // the string to search for let codespanType: 'file' | 'function-or-class' | 'unsearchable' = 'unsearchable'; - if (filePattern.test(target)) { + if (target.includes('.')) { codespanType = 'file' target = _codespanStr - } else if (functionPattern.test(target)) { - const match = target.match(functionPattern) - if (match && match[0]) { + } else if (functionOrMethodPattern.test(target)) { + + codespanType = 'function-or-class' + target = _codespanStr + + } else if (functionParensPattern.test(target)) { + const match = target.match(functionParensPattern) + if (match && match[1]) { codespanType = 'function-or-class' - target = match[0] + target = match[1] } } @@ -618,7 +623,6 @@ class ChatThreadService extends Disposable implements IChatThreadService { .reverse() - if (codespanType === 'file') { @@ -650,96 +654,79 @@ class ChatThreadService extends Disposable implements IChatThreadService { try { const matches = model.findMatches( - target.split('(')[0].trim(), // remove parameters + target, false, // searchOnlyEditableRange false, // isRegex true, // matchCase - null, // wordSeparators + ' ', // wordSeparators true // captureMatches ); const firstThree = matches.slice(0, 3); // take first 3 occurences, attempt to goto definition on them - for (const match of firstThree) { const position = new Position(match.range.startLineNumber, match.range.startColumn); const definitionProviders = this._languageFeaturesService.definitionProvider.ordered(model); for (const provider of definitionProviders) { - const definitions = await provider.provideDefinition(model, position, CancellationToken.None); - if (!definitions) continue; - const locationLinks: LocationLink[] = []; + const _definitions = await provider.provideDefinition(model, position, CancellationToken.None); - if (Array.isArray(definitions)) { - // Handle Location[] or LocationLink[] - for (const def of definitions) { - if ('uri' in def) { - locationLinks.push({ - uri: def.uri, - range: def.range, - targetSelectionRange: def.range, - originSelectionRange: undefined - }); - } else { - locationLinks.push(def); + if (!_definitions) continue; + + const definitions = Array.isArray(_definitions) ? _definitions : [_definitions]; + + for (const definition of definitions) { + + return { + uri: definition.uri, + selection: { + startLineNumber: definition.range.startLineNumber, + startColumn: definition.range.startColumn, + endLineNumber: definition.range.endLineNumber, + endColumn: definition.range.endColumn, } - } - } else { - // Handle single Location - locationLinks.push({ - uri: definitions.uri, - range: definitions.range, - targetSelectionRange: definitions.range, - originSelectionRange: undefined - }); - } + }; - const definition = locationLinks[0]; - if (!definition) continue; + // const defModelRef = await this._textModelService.createModelReference(definition.uri); + // const defModel = defModelRef.object.textEditorModel; - // Load definition file model - const defModelRef = await this._textModelService.createModelReference(definition.uri); - const defModel = defModelRef.object.textEditorModel; + // try { + // const symbolProviders = this._languageFeaturesService.documentSymbolProvider.ordered(defModel); - try { - const symbolProviders = this._languageFeaturesService.documentSymbolProvider.ordered(defModel); + // for (const symbolProvider of symbolProviders) { + // const symbols = await symbolProvider.provideDocumentSymbols( + // defModel, + // CancellationToken.None + // ); - for (const symbolProvider of symbolProviders) { - const symbols = await symbolProvider.provideDocumentSymbols( - defModel, - CancellationToken.None - ); + // if (symbols) { + // const symbol = symbols.find(s => { + // const symbolRange = s.range; + // return symbolRange.startLineNumber <= definition.range.startLineNumber && + // symbolRange.endLineNumber >= definition.range.endLineNumber && + // (symbolRange.startLineNumber !== definition.range.startLineNumber || symbolRange.startColumn <= definition.range.startColumn) && + // (symbolRange.endLineNumber !== definition.range.endLineNumber || symbolRange.endColumn >= definition.range.endColumn); + // }); - if (symbols) { - const symbol = symbols.find(s => { - const symbolRange = s.range; - return symbolRange.startLineNumber <= definition.range.startLineNumber && - symbolRange.endLineNumber >= definition.range.endLineNumber && - (symbolRange.startLineNumber !== definition.range.startLineNumber || symbolRange.startColumn <= definition.range.startColumn) && - (symbolRange.endLineNumber !== definition.range.endLineNumber || symbolRange.endColumn >= definition.range.endColumn); - }); - - - console.log('@@@ symbol', symbol?.name, symbol?.kind) - - // if we got to a class/function get the full range and return - if (symbol?.kind === SymbolKind.Function || symbol?.kind === SymbolKind.Class) { - return { - uri: definition.uri, - selection: { - startLineNumber: definition.range.startLineNumber, - startColumn: definition.range.startColumn, - endLineNumber: definition.range.endLineNumber, - endColumn: definition.range.endColumn, - } - }; - } - } - } - } finally { - defModelRef.dispose(); + // // if we got to a class/function get the full range and return + // if (symbol?.kind === SymbolKind.Function || symbol?.kind === SymbolKind.Method || symbol?.kind === SymbolKind.Class) { + // return { + // uri: definition.uri, + // selection: { + // startLineNumber: definition.range.startLineNumber, + // startColumn: definition.range.startColumn, + // endLineNumber: definition.range.endLineNumber, + // endColumn: definition.range.endColumn, + // } + // }; + // } + // } + // } + // } finally { + // defModelRef.dispose(); + // } } } } @@ -763,10 +750,9 @@ class ChatThreadService extends Disposable implements IChatThreadService { const links = thread.state.linksOfMessageIdx?.[messageIdx] if (!links) return undefined; - const location = links[codespanStr] - if (!location) return undefined; + const link = links[codespanStr] - return location + return link } async addCodespanLink({ newLinkText, newLinkLocation, messageIdx, threadId }: { newLinkText: string, newLinkLocation: CodespanLocationLink, messageIdx: number, threadId: string }) { diff --git a/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx b/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx index d2b7cc56..c5f8ff42 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx @@ -50,26 +50,20 @@ const CodespanWithLink = ({ text, rawText, chatMessageLocation }: { text: string const [didComputeCodespanLink, setDidComputeCodespanLink] = useState(false) - console.log('rerender', didComputeCodespanLink ? 1 : 0) - let link = undefined - if (rawText.endsWith("`")) { // if codespan was completed // get link from cache link = chatThreadService.getCodespanLink({ codespanStr: text, messageIdx, threadId }) if (link === undefined) { - // generate link and add to cache - chatThreadService.generateCodespanLink(text) + (chatThreadService.generateCodespanLink(text) .then(link => { - chatThreadService.addCodespanLink({ newLinkText: text, newLinkLocation: link, messageIdx, threadId }) - - setDidComputeCodespanLink(true) - + setDidComputeCodespanLink(true) // rerender }) + ) } } @@ -83,22 +77,22 @@ const CodespanWithLink = ({ text, rawText, chatMessageLocation }: { text: string // open the file commandSerivce.executeCommand('vscode.open', link.uri).then(() => { - console.log('click:', selection, link.uri) - // select the text - if (!selection) return; + setTimeout(() => { + if (!selection) return; - const editor = editorService.getActiveCodeEditor() - if (!editor) return; + const editor = editorService.getActiveCodeEditor() + if (!editor) return; - editor.setSelection(selection) - editor.revealRange(selection, ScrollType.Immediate) + editor.setSelection(selection) + editor.revealRange(selection, ScrollType.Immediate) + + }, 50) // needed when document was just opened and needs to initialize }) } - return