pyright-langserver-for-pycharm
pyright-langserver-for-pycharm copied to clipboard
`Literal` autocompletion inserts an extra quote
What happened?
When I accept an autocompleted Literal value it inserts an ' or " at the end
Steps to reproduce
Command = Literal['first', 'second']
s: Command = '' # accept the autocomplete with the caret inside the quotes - Result: 'first''
Relevant log output or stack trace
No response
Operating system
None
Can reproduce:
from typing import Literal
T = Literal['first', 'second']
v: T
v = ''
2024-05-09 18:34:12,233 [ 820111] FINE - #c.i.p.l.i.c.Lsp4jServerConnector - --> PyrightLSDescriptor@project:
{
"jsonrpc": "2.0",
"id": "61",
"method": "textDocument/completion",
"params": {
"context": { "triggerKind": 1 },
"textDocument": { "uri": "file:///<project>/.py" },
"position": { "line": 6, "character": 5 }
}
}
2024-05-09 18:34:12,235 [ 820113] FINE - #c.i.p.l.i.c.Lsp4jServerConnector - <-- PyrightLSDescriptor@project:
{
"jsonrpc": "2.0",
"id": "61",
"result": {
"items": [
...,
{
"label": "'second'", "kind": 21, "sortText": "03.9999.'second'",
"textEdit": {
"range": {
"start": { "line": 6, "character": 4 },
"end": { "line": 6, "character": 6 }
},
"newText": "'second'"
}
}
],
"isIncomplete": true
}
}
2024-05-09 18:34:12,724 [ 820602] FINE - #c.i.p.l.i.c.Lsp4jServerConnector - --> PyrightLSDescriptor@project:
{
"jsonrpc": "2.0",
"method": "textDocument/didChange",
"params": {
"textDocument": { "version": 48, "uri": "file:///<project>/.py" },
"contentChanges": [
{
"range": {
"start": { "line": 6, "character": 5 },
"end": { "line": 6, "character": 5 }
},
"text": "second'"
}
]
}
}
I believe this is not a problem with the plugin (I overrode none of the default behaviours, as you can see here), but a problem with how PyCharm interprets the suggestions.
The first request was triggered by pressing Ctrl Space in the middle of the two quotes:
{
"method": "textDocument/completion",
"params": {
// | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
// | v | | = | | ' | ' |
"position": { "line": 6, "character": 5 }
}
}
The corresponding response then gave back two items, both of which have the same textEdit range spanning from character 4 up to 6 (which presumably means both quotes are replaced) and a newText value having both leading and trailing quotes:
{
"result": {
"items": [
...,
{
"label": "'second'",
"textEdit": {
"range": {
// | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
// | v | | = | | ' | ' |
"start": { "line": 6, "character": 4 },
"end": { "line": 6, "character": 6 }
},
"newText": "'second'"
}
}
],
"isIncomplete": true
}
}
On accept, PyCharm sent this request, telling that it added second' (with no leading quote but one trailing) at (the position right before) character 5, leaving the trailing quote intact:
{
"method": "textDocument/didChange",
"params": {
"contentChanges": [
{
"range": {
// | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
// | v | | = | | ' | ' |
// =====================================================
// | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | 1 | 2 |
// | v | | = | | ' | s | e | c | o | n | d | ' | ' |
"start": { "line": 6, "character": 5 },
"end": { "line": 6, "character": 5 }
},
"text": "second'"
}
]
}
}
I'll see what I can do.
Progress update: I'm stuck. The best I can do for now is monkeypatching.
Setup:
from typing import Literal, TypedDict
class D(TypedDict):
first: int
second: str
v: Literal['first', 'second']
d: D
v = '|'
v = '''|'''
d = {'|': ''}
d = {'''|''': ''}
Default behavior:
v = 'first'|'
v = '''first'|'''
d = {'second'|': ''}
d = {'''second'|''': ''}
Desired behaviour:
v = 'first'|
v = '''first'''|
d = {'second'|: ''}
d = {'''second'''|: ''}
Monkeypatched behaviour, version 1 (remove the trailing quote for this one case, fallback to default):
v = 'first|'
v = '''first|'''
d = {'second|': ''}
d = {'''second|''': ''}
Monkeypatched behaviour, version 2 (override default entirely: replace the range with replacement, then put caret at the end of replacement):
v = 'first'|
v = '''first'|''
d = {'second'|: ''}
d = {'''second'|'': ''}
Both are nowhere near my expectations. Regardless, I'm pushing them for review.
It turns out PyCharm's behaviour is the same as monkeypatch 1 (no completions for Literals):
d = {'second|': ''}
d = {'''second|''': ''}
...and VSCode's is the same as monkeypatch 2:
v = 'first'|
v = '''first'|''
d = {'second'|: ''}
d = {'''second'|'': ''}
Still, it doesn't change the fact that I consider both of these subpar. This calls for an upstream issue.
I would still appreciate if you could align it with pycharms behavior for now. The Jet brains teams takes ages to fix something
@johannesschaffer #52 it is, then. Could you install the fix and play with it for some time first? I already did some testing on my own, but natural workflows tend to be better bug finders.
Sorry, I don't know how to install a plugin other than via the marketplace
See the README. The workflow to choose is the one linked from the PR.