@@ -37,6 +37,7 @@ import { parseSourceCodeForDefinitionsTopLevel } from "../services/tree-sitter"
3737import { CheckpointStorage } from "../shared/checkpoints"
3838import { ApiConfiguration } from "../shared/api"
3939import { findLastIndex } from "../shared/array"
40+ import { getSymbolDocumentation as getSymbolDocumentationUtil } from "./tools/getSymbolDocumentation"
4041import { combineApiRequests } from "../shared/combineApiRequests"
4142import { combineCommandSequences } from "../shared/combineCommandSequences"
4243import {
@@ -3553,254 +3554,12 @@ export class Cline extends EventEmitter<ClineEvents> {
35533554 /**
35543555 * Gets documentation for a symbol by name, optionally scoped to a specific file.
35553556 * Uses VS Code's language services to find symbol definitions and hover information.
3557+ *
3558+ * Implementation is delegated to the standalone utility function for better testability
3559+ * and separation of concerns.
35563560 */
35573561 async getSymbolDocumentation ( symbolName : string , filePath ?: string ) : Promise < string > {
3558- // Show output channel
3559- try {
3560- console . log ( `[DEBUG] getSymbolDocumentation: ${ symbolName } , ${ filePath } ` )
3561- // STEP 1: If file path is provided, try to find the symbol in that file first
3562- if ( filePath ) {
3563- const uri = vscode . Uri . file ( path . resolve ( cwd , filePath ) )
3564-
3565- try {
3566- // Try to open the document
3567- const document = await vscode . workspace . openTextDocument ( uri )
3568-
3569- // STEP 1a: Check if symbol is defined in the document
3570- const documentSymbols = await vscode . commands . executeCommand < vscode . DocumentSymbol [ ] > (
3571- "vscode.executeDocumentSymbolProvider" ,
3572- uri ,
3573- )
3574-
3575- // Helper function to search recursively through DocumentSymbol hierarchy
3576- const findSymbolInHierarchy = (
3577- symbols ?: vscode . DocumentSymbol [ ] ,
3578- ) : vscode . DocumentSymbol | undefined => {
3579- if ( ! symbols ) return undefined
3580-
3581- for ( const symbol of symbols ) {
3582- if ( symbol . name === symbolName ) {
3583- return symbol
3584- }
3585-
3586- const found = findSymbolInHierarchy ( symbol . children )
3587- if ( found ) return found
3588- }
3589-
3590- return undefined
3591- }
3592-
3593- // Look for the symbol in document symbols
3594- const foundSymbol = findSymbolInHierarchy ( documentSymbols )
3595-
3596- if ( foundSymbol ) {
3597- // Found the symbol definition in document
3598- const hoverResults = await vscode . commands . executeCommand < vscode . Hover [ ] > (
3599- "vscode.executeHoverProvider" ,
3600- uri ,
3601- foundSymbol . selectionRange . start ,
3602- )
3603-
3604- let hoverText = ""
3605- if ( hoverResults && hoverResults . length > 0 ) {
3606- // Extract the text from hover results
3607- for ( const content of hoverResults [ 0 ] . contents ) {
3608- if ( typeof content === "string" ) {
3609- hoverText += content + "\n"
3610- } else {
3611- // MarkdownString
3612- hoverText += content . value + "\n"
3613- }
3614- }
3615- }
3616-
3617- return `Symbol: ${ symbolName }
3618- Location: ${ uri . fsPath } :${ foundSymbol . selectionRange . start . line + 1 } :${ foundSymbol . selectionRange . start . character + 1 }
3619- Kind: ${ vscode . SymbolKind [ foundSymbol . kind ] }
3620- Status: Defined in file
3621-
3622- Documentation:
3623- ${ hoverText || "No documentation available for this symbol." } `
3624- }
3625-
3626- // STEP 1b: If not defined in document, search for references to the symbol
3627- // Find all occurrences of the symbol in text
3628- const text = document . getText ( )
3629- const occurrences : vscode . Position [ ] = [ ]
3630- const regex = new RegExp ( `\\b${ symbolName } \\b` , "g" )
3631-
3632- console . log ( `[DEBUG] Searching for symbol: ${ symbolName } ` )
3633-
3634- let match
3635- while ( ( match = regex . exec ( text ) ) !== null ) {
3636- const pos = document . positionAt ( match . index )
3637- occurrences . push ( pos )
3638- }
3639-
3640- console . log ( `[DEBUG] Found ${ occurrences . length } occurrences in ${ filePath } ` )
3641-
3642- // Try each occurrence to see if we can get hover or definition info
3643- for ( const position of occurrences ) {
3644- // Skip occurrences in import statements
3645- const line = document . lineAt ( position . line ) . text
3646- const lineText = document . lineAt ( position . line ) . text
3647- console . log ( `[DEBUG] Checking occurrence at line ${ position . line + 1 } : ${ lineText } ` )
3648-
3649- // if (/^\s*(import|from|require|use|include|using)/.test(line)) {
3650- // continue
3651- // }
3652-
3653- // Try hover first
3654- const hoverResults = await vscode . commands . executeCommand < vscode . Hover [ ] > (
3655- "vscode.executeHoverProvider" ,
3656- uri ,
3657- position ,
3658- )
3659-
3660- console . log ( `[DEBUG] hover results: ${ hoverResults ?. length } ` )
3661-
3662- if ( hoverResults && hoverResults . length > 0 ) {
3663- // Extract the text from hover results
3664- let hoverText = ""
3665- for ( const content of hoverResults [ 0 ] . contents ) {
3666- if ( typeof content === "string" ) {
3667- hoverText += content + "\n"
3668- } else {
3669- // MarkdownString
3670- hoverText += content . value + "\n"
3671- }
3672- }
3673-
3674- if ( hoverText . trim ( ) ) {
3675- // Try to get definition as well
3676- const definitions = await vscode . commands . executeCommand < vscode . Location [ ] > (
3677- "vscode.executeDefinitionProvider" ,
3678- uri ,
3679- position ,
3680- )
3681-
3682- console . log ( `[DEBUG] definition results length: ${ definitions ?. length } ` )
3683-
3684- let defLocationStr = `${ uri . fsPath } :${ position . line + 1 } :${ position . character + 1 } `
3685- let definedInFile = "Referenced in file"
3686-
3687- if ( definitions && definitions . length > 0 ) {
3688- const def = definitions [ 0 ]
3689- defLocationStr = `${ def . uri . fsPath } :${ def . range . start . line + 1 } :${ def . range . start . character + 1 } `
3690- definedInFile = "Imported/Referenced"
3691- }
3692-
3693- return `Symbol: ${ symbolName }
3694- Location: ${ defLocationStr }
3695- Status: ${ definedInFile }
3696- Referenced in: ${ filePath }
3697-
3698- Documentation:
3699- ${ hoverText . trim ( ) } `
3700- }
3701- }
3702-
3703- // If hover didn't work, try getting the definition directly
3704- const definitions = await vscode . commands . executeCommand < vscode . Location [ ] > (
3705- "vscode.executeDefinitionProvider" ,
3706- uri ,
3707- position ,
3708- )
3709-
3710- if ( definitions && definitions . length > 0 ) {
3711- const def = definitions [ 0 ]
3712- const defHoverResults = await vscode . commands . executeCommand < vscode . Hover [ ] > (
3713- "vscode.executeHoverProvider" ,
3714- def . uri ,
3715- def . range . start ,
3716- )
3717-
3718- if ( defHoverResults && defHoverResults . length > 0 ) {
3719- // Extract the text from hover results
3720- let hoverText = ""
3721- for ( const content of defHoverResults [ 0 ] . contents ) {
3722- if ( typeof content === "string" ) {
3723- hoverText += content + "\n"
3724- } else {
3725- // MarkdownString
3726- hoverText += content . value + "\n"
3727- }
3728- }
3729-
3730- if ( hoverText . trim ( ) ) {
3731- return `Symbol: ${ symbolName }
3732- Location: ${ def . uri . fsPath } :${ def . range . start . line + 1 } :${ def . range . start . character + 1 }
3733- Status: Imported/Referenced
3734- Referenced in: ${ filePath }
3735-
3736- Documentation:
3737- ${ hoverText . trim ( ) } `
3738- }
3739- }
3740-
3741- // We found a definition but no hover info
3742- return `Symbol: ${ symbolName }
3743- Location: ${ def . uri . fsPath } :${ def . range . start . line + 1 } :${ def . range . start . character + 1 }
3744- Status: Imported/Referenced
3745- Referenced in: ${ filePath }
3746-
3747- No documentation is available for this symbol.`
3748- }
3749- }
3750-
3751- // Couldn't find the symbol in the file
3752- return `No symbol '${ symbolName } ' found in file '${ filePath } '.`
3753- } catch ( error ) {
3754- return `Error: Could not analyze file '${ filePath } ': ${ error . message } `
3755- }
3756- }
3757-
3758- // STEP 2: If no file path or symbol not found in specified file, try workspace symbols
3759- const workspaceSymbols = await vscode . commands . executeCommand < vscode . SymbolInformation [ ] > (
3760- "vscode.executeWorkspaceSymbolProvider" ,
3761- symbolName ,
3762- )
3763-
3764- if ( workspaceSymbols && workspaceSymbols . length > 0 ) {
3765- // Found at least one matching symbol in workspace
3766- const symbol = workspaceSymbols [ 0 ]
3767- const uri = symbol . location . uri
3768- const position = symbol . location . range . start
3769-
3770- const hoverResults = await vscode . commands . executeCommand < vscode . Hover [ ] > (
3771- "vscode.executeHoverProvider" ,
3772- uri ,
3773- position ,
3774- )
3775-
3776- let hoverText = ""
3777- if ( hoverResults && hoverResults . length > 0 ) {
3778- // Extract the text from hover results
3779- for ( const content of hoverResults [ 0 ] . contents ) {
3780- if ( typeof content === "string" ) {
3781- hoverText += content + "\n"
3782- } else {
3783- // MarkdownString
3784- hoverText += content . value + "\n"
3785- }
3786- }
3787- }
3788-
3789- return `Symbol: ${ symbolName }
3790- Location: ${ uri . fsPath } :${ position . line + 1 } :${ position . character + 1 }
3791- Kind: ${ vscode . SymbolKind [ symbol . kind ] }
3792- Container: ${ symbol . containerName || "Global Scope" }
3793- ${ filePath ? `Not directly referenced in: ${ filePath } ` : "" }
3794-
3795- Documentation:
3796- ${ hoverText . trim ( ) || "No documentation available for this symbol." } `
3797- }
3798-
3799- // STEP 3: No results found
3800- return `No symbol found for '${ symbolName } '${ filePath ? ` in or referenced by file '${ filePath } '` : "" } .`
3801- } catch ( error ) {
3802- return `Error retrieving documentation for symbol '${ symbolName } ': ${ error . message } `
3803- }
3562+ return getSymbolDocumentationUtil ( symbolName , filePath , cwd )
38043563 }
38053564
38063565 async getEnvironmentDetails ( includeFileDetails : boolean = false ) {
0 commit comments