Get vscode registerCompletionItemProvider to work in a json file with a `word.` trigger

我只是一个虾纸丫 提交于 2021-02-10 15:46:12

问题


I am using this code to try to register a CompletionProvider in my extension. It is essentially the code from the sample completionProvider sample https://github.com/microsoft/vscode-extension-samples/blob/master/completions-sample/src/extension.ts.

I want it triggered by a . as in "launches." in my extension command in keybindings.json ultimately but it is doing nothing in any json file. Nothing happens, no error.

function activate(context) {

  loadLaunchSettings(context);
  activeContext = context;

  const configCompletionProvider = vscode.languages.registerCompletionItemProvider (
    { language: 'json', scheme: 'file' },   // tried scheme: 'untitled' too
    {
      // eslint-disable-next-line no-unused-vars
      provideCompletionItems(document, position, token, context) {

        // get all text until the `position` and check if it reads `"launches."`

        const linePrefix = document.lineAt(position).text.substr(0, position.character);
        if (!linePrefix.endsWith('\"launches.\"')) {  // tried without the escapes too
          return undefined;
        }

        return [
          new vscode.CompletionItem('log', vscode.CompletionItemKind.Text),
          new vscode.CompletionItem('warn', vscode.CompletionItemKind.Text),
          new vscode.CompletionItem('error', vscode.CompletionItemKind.Text),
        ];
      }
    },
    '.' // trigger
  );

  context.subscriptions.push(configCompletionProvider);
}

In this code:

  {
    "key": "alt+f",
    "command": "launches."   <= provider completion options here
  },

I couldn't find anything helpful and thought I followed the sample closely but no completion suggestions either on typing "launches." or using Ctrl+Space to trigger intellisense.

I do have this setting:

  "editor.quickSuggestions": {
    "comments": true,
    "other": true,
    "strings": true   // <===
  },

And I tried various alternatives presented here to a similar problem: Custom Extension for JSON Completion Does Not Work in Double Quotes


Still baffled - works in a javascript file but not a json file. Is there something special I have to do to get a . recognised as a trigger character in a json file (other than listing it in the vscode.languages.registerCompletionItemProvider as below)?

I have stripped it down to:

function activate(context) {

  loadLaunchSettings(context);
  activeContext = context;

  let docFilter = { scheme: 'file', language: 'json' };

  const configCompletionProvider = vscode.languages.registerCompletionItemProvider (
    // { language: 'json', pattern: './**/test.json' }, // does not work
    // 'javascript',                                    //  works
    // 'json',                                          // does not work
    docFilter,                                          // does not work
    {
       // eslint-disable-next-line no-unused-vars
      provideCompletionItems(document, position, token, context) {

        return [
          new vscode.CompletionItem('howdy1', vscode.CompletionItemKind.Text),
          new vscode.CompletionItem('howdy2', vscode.CompletionItemKind.Text),
          new vscode.CompletionItem('howdy3', vscode.CompletionItemKind.Text),
        ];
      }
    },
    "." // trigger
  );

    context.subscriptions.push(configCompletionProvider);
};
    

回答1:


Based on the answer by Gamma11 about what is a word in JSON, the whole string is considered a word including the " chars.

It works if you adjust the range the completion item should replace, and not look for the current word at the position.

  context.subscriptions.push(vscode.languages.registerCompletionItemProvider (
    { language: 'json', scheme: 'file' },
    // 'json',
    {
      // eslint-disable-next-line no-unused-vars
      provideCompletionItems(document, position, token, context) {

        // get all text until the `position` and check if it reads `"launches.`

        const linePrefix = document.lineAt(position).text.substr(0, position.character);
        if (!linePrefix.endsWith('"launches.')) {
          return undefined;
        }
        let myitem = (text) => {
          let item = new vscode.CompletionItem(text, vscode.CompletionItemKind.Text);
          item.range = new vscode.Range(position, position);
          return item;
        }
        return [
          myitem('log'),
          myitem('warn'),
          myitem('error'),
        ];
      }
    },
    '.' // trigger
  ));

Edit:

What also works but does not look nice is

        return [
          new vscode.CompletionItem('"launches.log"', vscode.CompletionItemKind.Text),
          new vscode.CompletionItem('"launches.warn"', vscode.CompletionItemKind.Text),
          new vscode.CompletionItem('"launches.error"', vscode.CompletionItemKind.Text),
        ];

Edit:

Just to supply a completion on any . typed I just removed the test (endsWith) of what was in front of the ..

To see if the completion provider is called I place a LogPoint breakpoint on the return with the CompletionItems.

The documentation of the CompletionItem is very terse.

From the doc of CompletionItem:

It is sufficient to create a completion item from just a label. In that case the completion item will replace the word until the cursor with the given label or insertText. Otherwise the given edit is used.

Although they talk about an edit in the main text, the textEdit doc tells you it is deprecated and you need to use insertText and range. But the additionalTextEdits are not deprecated (??)

The range property is not very clear how an inserting and replacing range are used and what effect you can achieve by setting it a certain way.

When omitted, the range of the current word is used as replace-range and as insert-range the start of the current word to the current position is used.

And then part of the problem is that " is part of a word for JSON files. And as Gamme11 has pointed out if you, for some odd reason, add these "'s to the label it works in some cases. Setting the insertText with the same content does not work, probably because the default range is chosen incorrectly.

If you set the range yourself you bypass the strange default behavior.

Because we want to insert new stuff at the position of the cursor just set range to an empty range at the cursor position.

  context.subscriptions.push(vscode.languages.registerCompletionItemProvider (
    // { language: 'json', scheme: 'file' },
    'json',
    {
      // eslint-disable-next-line no-unused-vars
      provideCompletionItems(document, position, token, context) {
        let myitem = (text) => {
          let item = new vscode.CompletionItem(text, vscode.CompletionItemKind.Text);
          item.range = new vscode.Range(position, position);
          return item;
        }
        return [
          myitem('howdy1'),
          myitem('howdy2'),
          myitem('howdy3'),
        ];
      }
    },
    '.' // trigger
  ));


来源:https://stackoverflow.com/questions/64584850/get-vscode-registercompletionitemprovider-to-work-in-a-json-file-with-a-word

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!