LanguageClient#textDocument_codeAction() is only applied after timeout
Closed this issue · 4 comments
Describe the bug
When running LanguageClient#textDocument_codeAction() in a typescript project the available actions are shown almost immediately, but after selecting an action, the action is applied only after the language client timeout.
Environment
- neovim/vim version (
nvim --versionorvim --version): neovim v0.3.1 - This plugin version (
git rev-parse --short HEAD): a1b90a7 - This plugin's binary version (
bin/languageclient --version): 0.1.106 - Minimal vimrc content:
My full vimrc is available at https://github.com/marcuskrahl/dotfiles
Relevant parts:
call plug#begin()
Plug 'tpope/vim-surround'
Plug 'tpope/vim-commentary'
Plug 'matze/vim-move'
Plug 'jedverity/feral-vim'
Plug 'editorconfig/editorconfig-vim'
Plug 'autozimu/LanguageClient-neovim', { 'branch': 'next', 'do': 'bash install.sh' }
Plug 'jceb/vim-orgmode' | Plug 'tpope/vim-speeddating'
"FZF
if !empty(glob('/usr/share/vim/vimfiles/plugin/fzf.vim'))
so /usr/share/vim/vimfiles/plugin/fzf.vim
Plug 'junegunn/fzf.vim'
endif
" Typescript dependencies
Plug 'leafgarland/typescript-vim'
call plug#end()
let g:LanguageClient_autoStart = 1
let g:LanguageClient_serverCommands = {}
if executable('javascript-typescript-stdio')
let g:LanguageClient_serverCommands.javascript = ['javascript-typescript-stdio']
let g:LanguageClient_serverCommands.typescript = ['javascript-typescript-stdio']
"let g:LanguageClient_serverCommands.typescript = ['tcp://127.0.0.1:2089']
autocmd FileType javascript setlocal omnifunc=LanguageClient#complete
autocmd FileType typescript setlocal omnifunc=LanguageClient#complete
nnoremap <silent> <leader>lh :call LanguageClient#textDocument_hover()<CR>
nnoremap <silent> <leader>ld :call LanguageClient#textDocument_definition()<CR>
nnoremap <silent> <leader>lr :call LanguageClient#textDocument_rename()<CR>
nnoremap <silent> <leader>la :call LanguageClient#textDocument_codeAction()<CR>
nnoremap <silent> <leader>ls :call LanguageClient#textDocument_documentSymbol()<CR>
nnoremap <silent> <leader>lS :call LanguageClient#workspace_symbol()<CR>
let g:LanguageClient_loggingFile = '/tmp/LanguageClient.log'
let g:LanguageClient_loggingLevel = 'INFO'
let g:LanguageClient_serverStderr = '/tmp/LanguageServer.log'
let g:LanguageClient_waitOutputTimeout = 10
else
echo "javascript-typescript-stdio not installed!\n"
:cq
endif
- Language server link and version: javascript-typescript-stdio 2.9.4
To Reproduce
Steps to reproduce the behavior:
- Fetch example project https://github.com/marcuskrahl/angular6-vim
- Start vim,
nvim -u min-vimrc.vim - Edit
src/app/app.component.ts - go to line 17 and put the cursor on test()
- Trigger code action
- select replace with this.test()
- wait for timeout
- the action is applied
Current behavior
The action is only applied after the language client timeout (g:LanguageClient_waitOutputTimeout). There is an error in the log as well.
If I set the timeout to a lower value (10s -> 2s) the action is successfully applied after this shorter timeout
Expected behavior
The action should be applied as soon as possible
Log
10:35:19 INFO reader-typescript src/vim.rs:389 <= Some("typescript") {"jsonrpc":"2.0","id":23,"result":[{"title":"Add 'this.' to unresolved variable","command":"codeFix","arguments":[{"fileName":"/home/marcus/tmp/angular6/src/app/app.component.ts","textChanges":[{"span":{"start":460,"length":4},"newText":"this.test"}]}]}]}
10:35:19 INFO main src/vim.rs:90 => None {"jsonrpc":"2.0","method":"s:FZF","params":[["codeFix: Add 'this.' to unresolved variable"],"LanguageClient_FZFSinkCommand"],"id":24}
10:35:19 INFO reader-main src/vim.rs:389 <= None {"id": 24, "jsonrpc": "2.0", "result": 0}
10:35:19 INFO main src/languageclient.rs:1173 End textDocument/codeAction
10:35:19 INFO main src/vim.rs:90 => None {"jsonrpc":"2.0","result":[{"arguments":[{"fileName":"/home/marcus/tmp/angular6/src/app/app.component.ts","textChanges":[{"newText":"this.test","span":{"length":4,"start":460}}]}],"command":"codeFix","title":"Add 'this.' to unresolved variable"}],"id":1}
10:35:20 INFO reader-main src/vim.rs:389 <= None {"method": "LanguageClient_FZFSinkCommand", "jsonrpc": "2.0", "params": {"languageId": "typescript", "selection": "codeFix: Add 'this.' to unresolved variable", "buftype": ""}}
10:35:20 INFO main src/languageclient.rs:2309 Begin LanguageClient_FZFSinkCommand
10:35:20 INFO main src/languageclient.rs:46 gather_args: ["selection"] = [String("codeFix: Add \'this.\' to unresolved variable")]
10:35:20 INFO main src/languageclient.rs:1566 Begin workspace/executeCommand
10:35:20 INFO main src/languageclient.rs:29 Some arguments are not available. Requesting from vim. Keys: ["languageId"]. Exps: ["&filetype"]
10:35:20 INFO main src/vim.rs:90 => None {"jsonrpc":"2.0","method":"eval","params":["[&filetype]"],"id":25}
10:35:20 INFO reader-main src/vim.rs:389 <= None {"method": "languageClient/handleCursorMoved", "jsonrpc": "2.0", "params": {"languageId": "typescript", "line": 17, "LSP#visible_line_start()": 0, "LSP#visible_line_end()": 28, "buftype": "", "filename": "/home/marcus/tmp/angular6/src/app/app.component.ts"}}
10:35:20 INFO reader-main src/vim.rs:389 <= None {"id": 25, "jsonrpc": "2.0", "result": ["typescript"]}
10:35:20 INFO main src/languageclient.rs:46 gather_args: [LanguageId] = [String("typescript")]
10:35:20 INFO main src/languageclient.rs:46 gather_args: ["command", "arguments"] = [String("codeFix"), Array([Object({"fileName": String("/home/marcus/tmp/angular6/src/app/app.component.ts"), "textChanges": Array([Object({"newText": String("this.test"), "span": Object({"length": Number(4), "start": Number(460)})})])})])]
10:35:20 INFO main src/vim.rs:90 => Some("typescript") {"jsonrpc":"2.0","method":"workspace/executeCommand","params":{"arguments":[{"fileName":"/home/marcus/tmp/angular6/src/app/app.component.ts","textChanges":[{"newText":"this.test","span":{"length":4,"start":460}}]}],"command":"codeFix"},"id":26}
10:35:20 INFO reader-typescript src/vim.rs:389 <= Some("typescript") {"jsonrpc":"2.0","method":"workspace/applyEdit","id":1,"params":{"edit":{"changes":{"file:///home/marcus/tmp/angular6/src/app/app.component.ts":[{"range":{"start":{"line":17,"character":4},"end":{"line":17,"character":8}},"newText":"this.test"}]}}},"meta":{}}
10:35:30 ERROR main src/vim.rs:71 Error handling message: timed out waiting on channel
Message: {"jsonrpc":"2.0","method":"LanguageClient_FZFSinkCommand","params":{"buftype":"","languageId":"typescript","selection":"codeFix: Add 'this.' to unresolved variable"}}
Error: Timeout
10:35:30 INFO main src/languageclient.rs:2122 Begin languageClient/handleCursorMoved
10:35:30 INFO main src/languageclient.rs:46 gather_args: [Buftype, Filename, Line] = [String(""), String("/home/marcus/tmp/angular6/src/app/app.component.ts"), Number(17)]
10:35:30 INFO main src/languageclient.rs:46 gather_args: ["LSP#visible_line_start()", "LSP#visible_line_end()"] = [Number(0), Number(28)]
10:35:30 INFO main src/languageclient.rs:2211 End languageClient/handleCursorMoved
10:35:30 INFO main src/languageclient.rs:1581 Begin workspace/applyEdit
10:35:30 INFO main src/languageclient.rs:29 Some arguments are not available. Requesting from vim. Keys: ["filename", "line", "character"]. Exps: ["LSP#filename()", "LSP#line()", "LSP#character()"]
10:35:30 INFO main src/vim.rs:90 => None {"jsonrpc":"2.0","method":"eval","params":["[LSP#filename(), LSP#line(), LSP#character()]"],"id":27}
10:35:30 INFO reader-main src/vim.rs:389 <= None {"id": 27, "jsonrpc": "2.0", "result": ["/home/marcus/tmp/angular6/src/app/app.component.ts", 17, 5]}
10:35:30 INFO main src/languageclient.rs:46 gather_args: [Filename, Line, Character] = [String("/home/marcus/tmp/angular6/src/app/app.component.ts"), Number(17), Number(5)]
10:35:30 INFO main src/vim.rs:90 => None {"jsonrpc":"2.0","method":"s:Edit","params":["edit","/home/marcus/tmp/angular6/src/app/app.component.ts"],"id":28}
10:35:30 INFO reader-main src/vim.rs:389 <= None {"id": 28, "jsonrpc": "2.0", "result": 0}
10:35:30 INFO main src/vim.rs:90 => None {"jsonrpc":"2.0","method":"getline","params":[1,"$"],"id":29}
10:35:30 INFO reader-main src/vim.rs:389 <= None {"id": 29, "jsonrpc": "2.0", "result": ["import { Component } from \"@angular/core\"", "import { Router, ActivatedRoute } from \"@angular/router\"", "", "@Component({", " selector: \"app-root\",", " templateUrl: \"./app.component.html\",", " styleUrls: [\"./app.component.css\"],", "})", "export class AppComponent {", " title: number | string = \"app\"", "", " constructor(private router: Router, private activatedRoute: ActivatedRoute) {}", "", " public test(): void {", " console.log(\"this is a test\")", " this.test();", " this.title = 42", " test();", " this.title = 42;", " }", "", " private function3(test: any[]): void {", " this.router.navigate([\".\"])", " }", "", " private function2(n: number, t: string): boolean {", " return n == 42 || t == \"fourtytwo\"", " }", "}"]}
10:35:30 INFO main src/vim.rs:90 => None {"jsonrpc":"2.0","method":"eval","params":["&fixendofline"],"id":30}
10:35:30 INFO reader-main src/vim.rs:389 <= None {"id": 30, "jsonrpc": "2.0", "result": 1}
10:35:30 INFO main src/vim.rs:90 => None {"jsonrpc":"2.0","method":"setline","params":[1,["import { Component } from \"@angular/core\"","import { Router, ActivatedRoute } from \"@angular/router\"","","@Component({"," selector: \"app-root\","," templateUrl: \"./app.component.html\","," styleUrls: [\"./app.component.css\"],","})","export class AppComponent {"," title: number | string = \"app\"",""," constructor(private router: Router, private activatedRoute: ActivatedRoute) {}",""," public test(): void {"," console.log(\"this is a test\")"," this.test();"," this.title = 42"," this.test();"," this.title = 42;"," }",""," private function3(test: any[]): void {"," this.router.navigate([\".\"])"," }",""," private function2(n: number, t: string): boolean {"," return n == 42 || t == \"fourtytwo\""," }","}"]],"id":31}
10:35:30 INFO reader-main src/vim.rs:389 <= None {"id": 31, "jsonrpc": "2.0", "result": 0}
10:35:30 INFO main src/vim.rs:90 => None {"jsonrpc":"2.0","method":"s:Edit","params":["edit","/home/marcus/tmp/angular6/src/app/app.component.ts"],"id":32}
10:35:30 INFO reader-main src/vim.rs:389 <= None {"method": "languageClient/handleTextChanged", "jsonrpc": "2.0", "params": {"languageId": "typescript", "buftype": "", "filename": "/home/marcus/tmp/angular6/src/app/app.component.ts"}}
10:35:30 INFO reader-main src/vim.rs:389 <= None {"id": 32, "jsonrpc": "2.0", "result": 0}
10:35:30 INFO main src/vim.rs:90 => None {"jsonrpc":"2.0","method":"cursor","params":[18,6]}
This is a known limitation of current implementation. At the moment all requests are handled sequentially and blocking other calls. Had to wait for rust async/await before turning them to async handlers.
For my own education, what about futures or tokio/mio crates? Are they a viable option nowadays in Rust for async support?
It is possible to use futures/tokio at the moment. But I don't think this is prime time to onboard as the language is adding syntax support and apis are in flux.