LSP: Fails to apply fixes on quick edit & save
Info
- OS and language server: MacOS Catalina, LSP-eslint
Repro
File: test.js
const a = 1;
- Place cursor after semicolon
- Press backspace to delete semicolon and immediately save document
Expected: Semicolon is added by fix-on-save feature of eslint server Actual: Semicolon doesn’t get added. Saving document again does add the semicolon.
Works as expected in VSCode.
(Of course, repro requires semi rule to be enabled. I can provide repo with everything set up if needed.)
Logs
Log from LSP (I’ve modified logging code to also log request/notification params):
LSP: --> textDocument/didChange
LSP: {'contentChanges': [{'text': 'const a = 1\n'}], 'textDocument': {'version': 3, 'uri': 'file:///test.js'}}
LSP: ==> textDocument/willSaveWaitUntil
LSP: {'reason': 1, 'textDocument': {'uri': 'file:///test.js'}}
LSP: timeout on textDocument/willSaveWaitUntil
LSP: <-- textDocument/publishDiagnostics
LSP: {'diagnostics': [{'range': {'start': {'line': 0, 'character': 6}, 'end': {'line': 0, 'character': 7}}, 'code': 'no-unused-vars', 'source': 'eslint', 'message': "'a' is assigned a value but never used.", 'severity': 1}, {'range': {'start': {'line': 0, 'character': 11}, 'end': {'line': 0, 'character': 11}}, 'code': 'semi', 'source': 'eslint', 'message': 'Missing semicolon.', 'severity': 1}], 'uri': 'file:///test.js'}
LSP: <-- eslint/status
LSP: {'state': 1}
LSP: Unhandled notification eslint/status
LSP: [{'range': {'start': {'line': 0, 'character': 11}, 'end': {'line': 0, 'character': 11}}, 'newText': ';'}]
LSP: --> textDocument/codeAction
LSP: {'range': {'start': {'line': 0, 'character': 11}, 'end': {'line': 0, 'character': 11}}, 'context': {'diagnostics': [{'range': {'start': {'line': 0, 'character': 11}, 'end': {'line': 0, 'character': 11}}, 'code': 'semi', 'source': 'eslint', 'message': 'Missing semicolon.', 'severity': 1}]}, 'textDocument': {'uri': 'file:///test.js'}}
LSP: [{'kind': 'quickfix', 'title': 'Fix this semi problem', 'command': {'title': 'Fix this semi problem', 'command': 'eslint.applySingleFix', 'arguments': ['semi']}}, {'kind': 'quickfix', 'title': 'Disable semi for this line', 'command': {'title': 'Disable semi for this line', 'command': 'eslint.applyDisableLine', 'arguments': ['semi']}}, {'kind': 'quickfix', 'title': 'Disable semi for the entire file', 'command': {'title': 'Disable semi for the entire file', 'command': 'eslint.applyDisableFile', 'arguments': ['semi']}}, {'kind': 'quickfix', 'title': 'Show documentation for semi', 'command': {'title': 'Show documentation for semi', 'command': 'eslint.openRuleDoc', 'arguments': ['semi']}}]
LSP: --> textDocument/didSave
LSP: {'textDocument': {'uri': 'file:///test.js'}}
Log from VSCode:
[Trace - 8:01:04 PM] Sending notification 'textDocument/didChange'.
Params: {
"textDocument": {
"uri": "file:///test.js",
"version": 25
},
"contentChanges": [
{
"text": "const a = 1\n"
}
]
}
[Trace - 8:01:04 PM] Sending request 'textDocument/willSaveWaitUntil - (13)'.
Params: {
"textDocument": {
"uri": "file:///test.js"
},
"reason": 1
}
[Trace - 8:01:04 PM] Received notification 'textDocument/publishDiagnostics'.
Params: {
"uri": "file:///test.js",
"diagnostics": [
{
"message": "'a' is assigned a value but never used.",
"severity": 1,
"source": "eslint",
"range": {
"start": {
"line": 0,
"character": 6
},
"end": {
"line": 0,
"character": 7
}
},
"code": "no-unused-vars"
},
{
"message": "Missing semicolon.",
"severity": 1,
"source": "eslint",
"range": {
"start": {
"line": 0,
"character": 11
},
"end": {
"line": 0,
"character": 11
}
},
"code": "semi"
}
]
}
[Trace - 8:01:04 PM] Received notification 'eslint/status'.
Params: {
"state": 1
}
[Trace - 8:01:04 PM] Received response 'textDocument/willSaveWaitUntil - (13)' in 18ms.
Result: [
{
"range": {
"start": {
"line": 0,
"character": 11
},
"end": {
"line": 0,
"character": 11
}
},
"newText": ";"
}
]
[Trace - 8:01:04 PM] Sending notification 'textDocument/didChange'.
Params: {
"textDocument": {
"uri": "file:///test.js",
"version": 26
},
"contentChanges": [
{
"text": "const a = 1;\n"
}
]
}
[Trace - 8:01:04 PM] Sending notification 'textDocument/didSave'.
Params: {
"textDocument": {
"uri": "file:///test.js",
"version": 26
}
}
[Trace - 8:01:04 PM] Received notification 'textDocument/publishDiagnostics'.
Params: {
"uri": "file:///test.js",
"diagnostics": [
{
"message": "'a' is assigned a value but never used.",
"severity": 1,
"source": "eslint",
"range": {
"start": {
"line": 0,
"character": 6
},
"end": {
"line": 0,
"character": 7
}
},
"code": "no-unused-vars"
}
]
}
[Trace - 8:01:04 PM] Received notification 'eslint/status'.
Params: {
"state": 1
}
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Comments: 24 (24 by maintainers)
I can provide all that (not at the moment though).
But it’s a matter of freezing for 1s when saving vs. instant saving. Saving files normally doesn’t introduce any delay, so the difference is very noticeable when it happens. And it would be a little bit hard to visualize that in a screen recording maybe.
I’d like to tackle this problem unless @tomv564 is already working on it
It was a great start, thank you for that. And then I’ve tried also using same solution in windows.py and that seems to fix the problem completely!
Of course it’s a hack but shouldn’t be hard to turn it into a clean solution.
@rchl could you these changes in plugin/core/rpc.py with eslint? This runs all handlers on the ST3 alternative thread. It seems to work OK on my end. However I don’t use eslint.
I wonder if that’s because
willSaveWaitUntilblocks the thread while eslint wants to send some diagnostic notifications before responding towillSaveWaitUntil…