Search code examples
visual-studio-codevscode-extensionsvscode-snippets

Building an extension, how can I make vscode suggest a commented name of the function below my cursor when I create a new line?


I am working with a Golang and I'm tired of having to copy and paste the name of the func anytime I want to document what the func does. So I decided to build an extension, I have create a functionality that writes the name of func to the line above when I call a command on the func declaration line. This is good but not what I am trying to build.

Here is the code I am working with presently

let disposable = vscode.commands.registerCommand('go-func-doc.helloWorld', function () {
        // The code you place here will be executed every time your command is executed

        const editor = vscode.window.activeTextEditor;
        if (editor) {
            // Get the current position of the cursor
            const position = editor.selection.active;

            // Get the current line
            const currentLine = editor.document.lineAt(position.line).text;

            // Check if the current line contains a function declaration
            const functionRegex = /^func(\s+\([a-zA-Z0-9_\s\*]+\))?\s+(?<functionName>[a-zA-Z_][a-zA-Z0-9_]*)\s*/;
            const match = currentLine.match(functionRegex);

            if (match && match.groups && match.groups['functionName']) {
                // Get the function name
                const functionName = match.groups['functionName'];

                // Create a TextEdit to insert the function name on the line above
                // const edit = new vscode.TextEdit(
                //     new vscode.Range(position.with(position.line - 1, 0), position.with(position.line - 1, 0)),
                //     `// ${functionName} `
                // );

                  // Check if there's an empty line above the function declaration
                const previousLine = editor.document.lineAt(position.line - 1).text;

                if (previousLine.trim() === '') {

                    // Create a TextEdit to insert the suggested function name on the line above
                    const edit = new vscode.TextEdit(
                        new vscode.Range(position.with(position.line - 1, 0), position.with(position.line - 1, 0)),
                        `// ${functionName} `
                    );
  
                
                // Apply the edit to the document
                const workspaceEdit = new vscode.WorkspaceEdit();
                workspaceEdit.set(editor.document.uri, [edit]);
                vscode.workspace.applyEdit(workspaceEdit);
                }

                editor.edit(editBuilder => {
                    editBuilder.insert(position.with(position.line - 1, 0), `// ${functionName} `);
                    const newPosition = position.with(position.line - 1, 6);
                    editor.selection = new vscode.Selection(newPosition, newPosition);
                });

            } else {
                vscode.window.showInformationMessage('No function declaration found on the current line.');
            }
        }

What I want however, is for vscode to suggest the name of the func (example: "// fooBar ") anytime I create a new line above the func. I am reading the API docs but I cant figure out what I should do.


Solution

  • I thought it would be more difficult, but a CompletionProvider - which is what you want - can take a \n as a trigger character!

    Here is a completionProvider file, say providers.js:

    exports.makeCompletionProvider = async function (context) {
    
      const provider1 = vscode.languages.registerCompletionItemProvider(
        'javascript',                              // change to golang here
        {
          provideCompletionItems(document, position, token, context) {
                   
            // I simplified your regex because I am not familiar with golang
            const re = /^func\s+([a-zA-Z_][a-zA-Z0-9_]*)/;
    
            let funcLine;
            let funcName;
            
            if (document.lineAt(position).text === '') {
              funcLine = document.lineAt(new vscode.Position(position.line + 1, position.character)).text;
              funcName = funcLine.match(re)[1];
              if (funcName) {
                const funcNameCompletionItem = new vscode.CompletionItem(`// ${ funcName }`);
                funcNameCompletionItem.kind = vscode.CompletionItemKind.Function;
                return [
                  funcNameCompletionItem
                ];
              }
              else return undefined;
            }
            
            else if (document.lineAt(new vscode.Position(position.line - 1, position.character)).text === '') {
              funcLine = document.lineAt(position).text;
              funcName = funcLine.match(re)[1];
              if (funcName) {
                const funcNameCompletionItem = new vscode.CompletionItem(`// ${ funcName }\n`);
                funcNameCompletionItem.kind = vscode.CompletionItemKind.Function;
                funcNameCompletionItem.command = { command: 'cursorUp', title: 'cursorUp' };
                return [
                  funcNameCompletionItem
                ];
              }
              else return undefined;
            }
    
            else return undefined;
        },
        '\n'  // trigger intellisense/completion with a newline
      );
    };
    

    And in your extension.js :

    const providers = require('./providers');
    
    
    async function activate(context) {
      providers.makeCompletionProvider(context);
    }
    

    The entire else if option can be deleted if you wish. It is only triggered when you insert a newline with the cursor at the beginning of the function line, which may be a rare situation for you.

    completion provider with a newline

    If you use a SnippetString as the CompletionItem you can much fancier with cursor placement and other typical snippet options.