Pesquisa de site

typescript - Como obter o texto/símbolo atual ao implementar Ir para Definição


Estou trabalhando na minha primeira extensão do vscode usando o Language Server Protocol, eu preciso pegar o texto onde o Clique com o botão direito -> Ir para definição foi acionado

Meu método atual onDefinition recebe apenas o textPosition

export default class DefinitionFinder extends Handler {
    constructor(
        protected connection: Connection,
        private refManager: ReferenceManager)
    {
        super();

        this.connection.onDefinition(async (textPosition) => {
            return this.handleErrors(
                this.getDefinition(textPosition), undefined) as Promise<Definition>;
        });
    }
    
private async getDefinition(textPosition: TextDocumentPositionParams): Promise<Location[]> {
    const text = "funABC";

    // instead of hardcoded value I need to get the text/symbol 
    // that is going to be use to go to definition 

    return this.refManager.getDefinitionLocations(text);
}

O TextDocumentPositionParams contém apenas o documentUri, line(number) e character(number)

Isso significa que a cada chamada de onDefinition eu preciso abrir o documento, ir para a linha e o caractere e obter a palavra atual ?

export interface TextDocumentPositionParams {
    /**
     * The text document.
     */
    textDocument: TextDocumentIdentifier;
    /**
     * The position inside the text document.
     */
    position: Position;
}

export interface TextDocumentIdentifier {
    /**
     * The text document's uri. (string)
     */
    uri: DocumentUri;
}

export declare namespace Position {
    /**
     * Creates a new Position literal from the given line and character.
     * @param line The position's line.
     * @param character The position's character.
     */

Resposta #1

Servidores de linguagem geralmente mantêm um cache de documentos de texto baseado nos eventos de alteração de documento que recebem. Se você estiver escrevendo o servidor de linguagem em TypeScript, você pode simplesmente usar a implementação dos pacotes vscode-languageserver e vscode-languageserver-textdocument. Trechos relevantes do exemplo oficial:

import { TextDocuments } from 'vscode-languageserver';
import { TextDocument } from 'vscode-languageserver-textdocument';

// Create a simple text document manager.
const documents: TextDocuments<TextDocument> = new TextDocuments(TextDocument);

...

// Make the text document manager listen on the connection
// for open, change and close text document events
documents.listen(connection);

Então você pode simplesmente fazer:

documents.get(uri).getText(range)

Resposta #2

É assim que acabei implementando a lógica para obter a palavra/símbolo quando "Ir para Definição" é solicitado em um Servidor de Linguagem

Decidi retornar um LocationLink que também aparece como um hiperlink sobre a palavra/símbolo

this.connection.onDefinition(async (textPosition) => {
    return this.handleErrors(
        this.getDefinitionLink(textPosition), undefined) as Promise<LocationLink[]>;
});

Este método obtém o símbolo do documento e então procura sua definição no refManager

private async getDefinitionLink(textPosition: TextDocumentPositionParams): Promise<LocationLink[]> {
    const symbol = this.getSymbolAtPosition(textPosition.position, textPosition.textDocument.uri);

    return this.refManager.getDefinitionLink(symbol);
}

Esta é a lógica real para extrair a palavra atual, não importa se ela foi clicada no meio dela.

let TokenSeparatorsXML = /[\t= <>"]/;

public getSymbolAtPosition(position: Position, documentUri: string): string {
    const range = {
        start: { line: position.line, character: 0},
        end: { line: position.line, character: Number.MAX_VALUE  }
    };
    //get the whole line 
    const txtDoc = this.refManager.getDocument(documentUri)!;

    const context = txtDoc.getText(range);
    const offset = position.character ;

    let start = offset-1;

    while ((start > 0) && !context[start].match(this.TokenSeparatorsXML))
    {
        start--;
    }

    let end = offset;

    while ((end < context.length) && !context[end].match(this.TokenSeparatorsXML))
    {
        end++;
    }

    const symbol = context.substr(start + 1, end - start - 1);

    console.log(`${start}->${end}- symbol: ${symbol}`);

    return symbol;
}

No meu caso, os separadores de tokens são muito estreitos, mas você deve considerar usar o padrão de palavras https://code.visualstudio.com/api/language-extensions/language-configuration-guide#word-pattern

O código completo da extensão pode ser encontrado aqui - https://github.com/mauriciogracia/petrel-xml