Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Invoke `editor.action.peekLocations` command from a code lens provided by a language server

I am trying to make a language server to create a code lens that opens an overlay to other files in VS Code. I tried the SO answer to How to implement overlay in VS Code Extension? (using the peekLocations command documented here) but in my own attempts I keep getting this unhelpful error when I click on the code lens:

argument does not match one of these constraints: arg instanceof constraint, arg.constructor === constraint, nor constraint(arg) === true

Perhaps there is an issue in LSP implementation I use (the OCaml lsp library), but to make this question language-agnostic, here is the relevant JSON response from my server that creates the code lens:

[Trace - 7:50:16 PM] Received response 'textDocument/codeLens - (12)' in 10ms.
[
    {
        "command": {
            "arguments": [
                "file:///home/test/testfile.test",
                {
                    "character": 3,
                    "line": 9
                },
                [
                    {
                        "range": {
                            "end": {
                                "character": 2,
                                "line": 1
                            },
                            "start": {
                                "character": 1,
                                "line": 1
                            }
                        },
                        "uri": "file:///home/test/testfile2.test"
                    },
                    {
                        "range": {
                            "end": {
                                "character": 2,
                                "line": 1
                            },
                            "start": {
                                "character": 1,
                                "line": 1
                            }
                        },
                        "uri": "file:///home/test/testfile2.test"
                    },
                    {
                        "range": {
                            "end": {
                                "character": 2,
                                "line": 1
                            },
                            "start": {
                                "character": 1,
                                "line": 1
                            }
                        },
                        "uri": "file:///home/test/testfile2.test"
                    }
                ],
                "gotoAndPeek"
            ],
            "command": "editor.action.peekLocations",
            "title": "test"
        },
        "range": {
            "end": {
                "character": 9,
                "line": 9
            },
            "start": {
                "character": 3,
                "line": 9
            }
        }
    }
]

What is wrong with the "arguments" field, or anything else in this response?

like image 550
Li-yao Xia Avatar asked Jan 22 '26 22:01

Li-yao Xia


1 Answers

The answer is that the peekLocations function expects objects of specific types (Uri, Position, Location[]), whereas when an LSP puts a command in a code lens, the arguments are JSON values (strings, arrays, and dictionaries) which are thus the wrong type. The workaround I found is to define my own command as a wrapper that decodes the JSON values into the expected objects before passing them to peekLocations.

type RawPosition = {line: number, character: number}
type RawRange = {start: RawPosition, end: RawPosition}
type RawLocation = {uri: string, range: RawRange}

const mkPosition = (raw : RawPosition) => new vscode.Position(raw.line, raw.character)
const mkRange = (raw : RawRange) => new vscode.Range(mkPosition(raw.start), mkPosition(raw.end))
const mkLocation = (raw : RawLocation) => new vscode.Location(Uri.parse(raw.uri), mkRange(raw.range))

// New command "myextension.peekLocations" as a wrapper around "editor.action.peekLocations"
const disposable = vscode.commands.registerCommand(context, "myextension.peekLocations", async (rawUri, rawPosition, rawLocations) => {
    const uri = Uri.parse(rawUri)
    const position = mkPosition(rawPosition)
    const locations = rawLocations.map(mkLocation)
    await vscode.commands.executeCommand("editor.action.peekLocations", uri, position, locations, "peek")
});
context.subscriptions.push(disposable);
like image 125
Li-yao Xia Avatar answered Jan 25 '26 06:01

Li-yao Xia



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!