1

I am trying to create copilot like ghost snippet suggestion using vs code TextEditorDecoration, but I am not able to figure out how to fix below issues

  1. White spaces not getting rendered

enter image description here

  1. Not rendering decorations after the last line. Currently, all are shown on the last line only.

enter image description here

My code look something like below.

        const editorDecoration = vscode.window.createTextEditorDecorationType({
            isWholeLine: false,
        });

        vscode.window.activeEditor!.setDecorations(editorDecoration, getDecoration(`let timeout;
    return function() {
        let context = this, args = arguments;
        let later = function() {
            timeout = null;
            if (!immediate) func.apply(context, args);
        };
        let callNow = immediate && !timeout;
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
        if (callNow) func.apply(context, args);
     };
   }`));


 function getDecoration(snippet: string): vscode.DecorationOptions[] {
        const editor = this.activeEditor;
        if (!editor) {
            return [];
        }

        const currentLine = editor.selection.active.line;
        const currentColumn = editor.selection.active.character;

        return snippet.split('\n').map((line, index) => {
            const decoration: vscode.DecorationOptions = {
                range: new vscode.Range(currentLine + index, currentColumn, currentLine + index, currentColumn + line.length),
                renderOptions: {
                    light: {
                        after: {
                            contentText: line,
                            color: 'rgba(0 ,0 , 0, 0.5)'
                        },
                    },
                    dark: {
                        after: {
                            contentText: line,
                            color: 'rgba(255, 255, 255, 0.5)'
                        },
                    },
                },
            };
            return decoration;
        });
    }

2 Answers2

0

For the whitespace part of your question, see MDN before/after content property - I assume that the renderOption.contentText ultimately gets passed to ::after content and so special characters, like whitespace, must be unicoded. See also [decorators] spaced are collapsed in contentText (before/after decoration attachments): where they use successfully use unicode spaces to avoid the collapsing whitespace problem.

This code works:

vscode.window.activeTextEditor.setDecorations(editorDecoration, getDecoration(
    `let timeout;
    return function() {
        \u2003let context = this, args = arguments;
        \u2003let later = function() {\u2003
        \u2003\u2003timeout = null;
        \u2003\u2003if (!immediate) func.apply(context, args);
        \u2003};
        \u2003let callNow = immediate && !timeout;
        \u2003clearTimeout(timeout);
        \u2003timeout = setTimeout(later, wait);
        \u2003if (callNow) func.apply(context, args);
    };`
));
  • Note in the above code all that leading whitespace on each line is irrelevant (because it gets stripped out) so you can format pretty much how you like to make it readable.

to produce this (it isn't selectable code - it is only a decoration):

how text decoration looks

Here is info on fixed-width spaces: Wikipedia unicode spaces. You will have to determine which one you want to use. I used \u2003 which is equal to two spaces (you could look at the useer's indentation settings to see how many spaces they are using for indentation). I tried the unicode for a tab \u0009 and unfortunately it doesn't work - there is no indentation at all for a tab unicode.


For your second question about last-line decorations, I can't find a reference but I remember seeing something similar. A range which extends past the end of the file is not allowed - you can construct the Range but it can't be used in the api to refer to lines that don't yet exist. So all the decoration goes on the same line. You would have to check the lineNumber against the total lines in the file and make an edit to add lines first and then apply your decoration. I can't find a citation for this idea but this seems to be what is happening.

Mark
  • 143,421
  • 24
  • 428
  • 436
0

What I found by reverse engineering copilot implementation and digging into vs code source code. Copilot using the InlineCompletionItem instead of TextEditorDecoration.

This is how we can use it. First add InlineCompletionItem Provider as below shown code.


vscode.languages.registerInlineCompletionItemProvider('*', {
            provideInlineCompletionItems: async (document, position, context, cancellationToken) => {
                
const suggestions =[ "function sum(a,b) \n { return a+b }" ]

                return suggestions.map((suggestion) => {
                    const endPosition = new vscode.Position(position.line, position.character + suggestion.length);
                    return new vscode.InlineCompletionItem(suggestion, new vscode.Range(position, endPosition));
                });
            }
        });

And then you invoke it on the cursor change event using the below command

vscode.window.onDidChangeTextEditorSelection(()=>{
 
vscode.commands.executeCommand('editor.action.inlineSuggest.trigger');
});

Hope this helps someone who trying for something similar.