README.md | 2 +- autoload/handlers.vim | 58 +++++++++++++++++++---------------------------------- autoload/lsp.vim | 36 +++++++++++++++++++++--------------- autoload/lspserver.vim | 21 ++++++++++++++------- autoload/util.vim | 26 ++++++++++++++++++++++++++ doc/lsp.txt | 6 +++--- plugin/lsp.vim | 4 ++-- diff --git a/README.md b/README.md index 75c98a92f8aa8192802b336650bf002d05a6df0d..10f661935860c17a91a0b4edd85d53d865df2338 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -Language Server Protocol (LSP) plugin for Vim9. You need Vim version 8.2.2241 or above to use this plugin. +Language Server Protocol (LSP) plugin for Vim9. You need Vim version 8.2.2342 or above to use this plugin. ## Installation diff --git a/autoload/handlers.vim b/autoload/handlers.vim index 53f265ef21dca1b1107b4f18b71824f3aff88b09..74b004534025d3f210e5e4c08b57bec42da37c69 100644 --- a/autoload/handlers.vim +++ b/autoload/handlers.vim @@ -4,7 +4,11 @@ # Handlers for messages from the LSP server # Refer to https://microsoft.github.io/language-server-protocol/specification # for the Language Server Protocol (LSP) specificaiton. -import {WarnMsg, ErrMsg, TraceLog, LspUriToFile} from './util.vim' +import {WarnMsg, + ErrMsg, + TraceLog, + LspUriToFile, + GetLineByteFromPos} from './util.vim' import {LspDiagsUpdated} from './buf.vim' # process the 'initialize' method reply from the LSP server @@ -57,7 +61,7 @@ endif var result: dict = reply.result[0] var file = LspUriToFile(result.uri) - var wid = bufwinid(file) + var wid = file->bufwinid() if wid != -1 win_gotoid(wid) else @@ -65,7 +69,8 @@ exe 'split ' .. file endif # Set the previous cursor location mark setpos("'`", getcurpos()) - cursor(result.range.start.line + 1, result.range.start.character + 1) + setcursorcharpos(result.range.start.line + 1, + result.range.start.character + 1) redraw! enddef @@ -254,7 +259,7 @@ var text: string = bnr->getbufline(loc.range.start.line + 1)[0] ->trim("\t ", 1) qflist->add({filename: fname, lnum: loc.range.start.line + 1, - col: loc.range.start.character + 1, + col: GetLineByteFromPos(bnr, loc.range.start) + 1, text: text}) endfor setqflist([], ' ', {title: 'Language Server', items: qflist}) @@ -286,9 +291,10 @@ else # textual reference propName = 'LspTextRef' endif - prop_add(docHL.range.start.line + 1, docHL.range.start.character + 1, + prop_add(docHL.range.start.line + 1, + GetLineByteFromPos(bnr, docHL.range.start) + 1, {end_lnum: docHL.range.end.line + 1, - end_col: docHL.range.end.character + 1, + end_col: GetLineByteFromPos(bnr, docHL.range.end) + 1, bufnr: bnr, type: propName}) endfor @@ -399,31 +405,6 @@ symbolLineTable->sort((a, b) => a.range.start.line - b.range.start.line) lsp#updateOutlineWindow(fname, symbolTypeTable, symbolLineTable) enddef -# Returns the byte number of the specified line/col position. Returns a -# zero-indexed column. 'pos' is LSP "interface position". -def s:get_line_byte_from_position(bnr: number, pos: dict): number - # LSP's line and characters are 0-indexed - # Vim's line and columns are 1-indexed - var col: number = pos.character - # When on the first character, we can ignore the difference between byte and - # character - if col > 0 - if !bnr->bufloaded() - bnr->bufload() - endif - - var ltext: list = bnr->getbufline(pos.line + 1) - if !ltext->empty() - var bidx = ltext[0]->byteidx(col) - if bidx != -1 - return bidx - endif - endif - endif - - return col -enddef - # sort the list of edit operations in the descending order of line and column # numbers. # 'a': {'A': [lnum, col], 'B': [lnum, col]} @@ -538,9 +519,9 @@ # create a list of buffer positions where the edits have to be applied. for e in text_edits # Adjust the start and end columns for multibyte characters start_row = e.range.start.line - start_col = s:get_line_byte_from_position(bnr, e.range.start) + start_col = GetLineByteFromPos(bnr, e.range.start) end_row = e.range.end.line - end_col = s:get_line_byte_from_position(bnr, e.range.end) + end_col = GetLineByteFromPos(bnr, e.range.end) start_line = [e.range.start.line, start_line]->min() finish_line = [e.range.end.line, finish_line]->max() @@ -638,6 +619,7 @@ # interface TextEdit s:applyTextEdits(bnr, changes) endfor + # Restore the cursor to the location before the edit save_cursor->setpos('.') enddef @@ -737,9 +719,12 @@ return endif var r: dict> = reply.result[0].range + var bnr: number = bufnr() + var start_col: number = GetLineByteFromPos(bnr, r.start) + 1 + var end_col: number = GetLineByteFromPos(bnr, r.end) - setpos("'<", [0, r.start.line + 1, r.start.character + 1, 0]) - setpos("'>", [0, r.end.line + 1, r.end.character, 0]) + setcharpos("'<", [0, r.start.line + 1, start_col, 0]) + setcharpos("'>", [0, r.end.line + 1, end_col, 0]) :normal gv enddef @@ -834,8 +819,7 @@ fileName) symbols->add({name: symName, file: fileName, - lnum: r.start.line + 1, - col: r.start.character + 1}) + pos: r.start}) endfor symbols->setwinvar(lspserver.workspaceSymbolPopup, 'LspSymbolTable') lspserver.workspaceSymbolPopup->popup_settext( diff --git a/autoload/lsp.vim b/autoload/lsp.vim index 8408ed506cc6c382e5d2115af3a1b07cd837fb9c..f936a6ffb5ddf45508f911b3e324317898674cf7 100644 --- a/autoload/lsp.vim +++ b/autoload/lsp.vim @@ -3,10 +3,13 @@ # Vim9 LSP client import NewLspServer from './lspserver.vim' -import {WarnMsg, ErrMsg, lsp_server_trace} from './util.vim' +import {WarnMsg, + ErrMsg, + lsp_server_trace, + GetLineByteFromPos} from './util.vim' -# Needs Vim 8.2.2082 and higher -if v:version < 802 || !has('patch-8.2.2082') +# Needs Vim 8.2.2342 and higher +if v:version < 802 || !has('patch-8.2.2342') finish endif @@ -412,7 +415,7 @@ for [lnum, diag] in items(lspserver.diagsMap[bnr]) text = diag.message->substitute("\n\\+", "\n", 'g') qflist->add({'filename': fname, 'lnum': diag.range.start.line + 1, - 'col': diag.range.start.character + 1, + 'col': GetLineByteFromPos(bnr, diag.range.start) + 1, 'text': text, 'type': s:lspDiagSevToQfType(diag.severity)}) endfor @@ -494,7 +497,7 @@ var curlnum: number = line('.') for lnum in (which == 'next') ? sortedDiags : reverse(sortedDiags) if (which == 'next' && lnum > curlnum) || (which == 'prev' && lnum < curlnum) - call cursor(lnum, 1) + cursor(lnum, 1) return endif endfor @@ -527,7 +530,7 @@ lspserver.getCompletion() # locate the start of the word var line = getline('.') - var start = col('.') - 1 + var start = charcol('.') - 1 while start > 0 && line[start - 1] =~ '\k' start -= 1 endwhile @@ -678,11 +681,12 @@ enddef var skipOutlineRefresh: bool = false -def s:addSymbolText(symbolTypeTable: dict>>, - pfx: string, - text: list, - lnumMap: list>, - children: bool) +def s:addSymbolText(bnr: number, + symbolTypeTable: dict>>, + pfx: string, + text: list, + lnumMap: list>, + children: bool) var prefix: string = pfx .. ' ' for [symType, symbols] in items(symbolTypeTable) if !children @@ -701,11 +705,12 @@ lnumMap->extend([{}]) for s in symbols text->add(prefix .. s.name) # remember the line number for the symbol + var start_col: number = GetLineByteFromPos(bnr, s.range.start) + 1 lnumMap->add({name: s.name, lnum: s.range.start.line + 1, - col: s.range.start.character + 1}) + col: start_col}) s.outlineLine = lnumMap->len() if s->has_key('children') && !s.children->empty() - s:addSymbolText(s.children, prefix, text, lnumMap, true) + s:addSymbolText(bnr, s.children, prefix, text, lnumMap, true) endif endfor endfor @@ -743,7 +748,7 @@ # First two lines in the buffer display comment information var lnumMap: list> = [{}, {}] var text: list = [] - s:addSymbolText(symbolTypeTable, '', text, lnumMap, false) + s:addSymbolText(fname->bufnr(), symbolTypeTable, '', text, lnumMap, false) append('$', text) w:lspSymbols = {filename: fname, lnumTable: lnumMap, symbolsByLine: symbolLineTable} @@ -1112,7 +1117,8 @@ endif else winList[0]->win_gotoid() endif - cursor(symTbl[result - 1].lnum, symTbl[result - 1].col) + setcursorcharpos(symTbl[result - 1].pos.line + 1, + symTbl[result - 1].pos.character + 1) catch # ignore exceptions endtry diff --git a/autoload/lspserver.vim b/autoload/lspserver.vim index 90a0009f820825e2745611d602c079b376b198fd..fc344c7676aeabfd84b3020b710bc995aa92f658 100644 --- a/autoload/lspserver.vim +++ b/autoload/lspserver.vim @@ -4,8 +4,16 @@ # LSP server functions # Refer to https://microsoft.github.io/language-server-protocol/specification # for the Language Server Protocol (LSP) specificaiton. -import {ProcessReply, ProcessNotif, ProcessRequest, ProcessMessages} from './handlers.vim' -import {WarnMsg, ErrMsg, ClearTraceLogs, TraceLog, LspUriToFile, LspFileToUri} from './util.vim' +import {ProcessReply, + ProcessNotif, + ProcessRequest, + ProcessMessages} from './handlers.vim' +import {WarnMsg, + ErrMsg, + ClearTraceLogs, + TraceLog, + LspUriToFile, + LspFileToUri} from './util.vim' # LSP server standard output handler def s:output_cb(lspserver: dict, chan: channel, msg: string): void @@ -84,9 +92,9 @@ }, textDocument: { foldingRange: {lineFoldingOnly: v:true}, completion: { - snippetSupport: v:true, completionItem: { documentationFormat: ['plaintext', 'markdown'], + snippetSupport: v:false }, completionItemKind: {valueSet: range(1, 25)} }, @@ -326,8 +334,7 @@ # numbers start from one. The LSP column number is the character index in the # line and not the byte index in the line. def s:getLspPosition(): dict var lnum: number = line('.') - 1 - #var col: number = strchars(getline('.')[: col('.') - 1]) - 1 - var col: number = col('.') - 1 + var col: number = charcol('.') - 1 return {line: lnum, character: col} enddef @@ -652,8 +659,8 @@ var fname: string = fnamemodify(fname_arg, ':p') var bnr: number = bufnr(fname_arg) req.params->extend({textDocument: {uri: LspFileToUri(fname)}}) var r: dict> = { - start: {line: line('.') - 1, character: col('.') - 1}, - end: {line: line('.') - 1, character: col('.') - 1}} + start: {line: line('.') - 1, character: charcol('.') - 1}, + end: {line: line('.') - 1, character: charcol('.') - 1}} req.params->extend({range: r}) var diag: list> = [] var lnum = line('.') diff --git a/autoload/util.vim b/autoload/util.vim index 27c54eac6d389e848c711b793ac8b9dc1fa0f127..0b2bef2b44ce0b5353d8bfede2939884d997a729 100644 --- a/autoload/util.vim +++ b/autoload/util.vim @@ -89,4 +89,30 @@ return uri enddef +# Returns the byte number of the specified LSP position in buffer 'bnr'. +# LSP's line and characters are 0-indexed. +# Vim's line and columns are 1-indexed. +# Returns a zero-indexed column. +export def GetLineByteFromPos(bnr: number, pos: dict): number + var col: number = pos.character + # When on the first character, we can ignore the difference between byte and + # character + if col > 0 + # Need a loaded buffer to read the line and compute the offset + if !bnr->bufloaded() + bnr->bufload() + endif + + var ltext: list = bnr->getbufline(pos.line + 1) + if !ltext->empty() + var bidx = ltext[0]->byteidx(col) + if bidx != -1 + return bidx + endif + endif + endif + + return col +enddef + # vim: shiftwidth=2 softtabstop=2 diff --git a/doc/lsp.txt b/doc/lsp.txt index 5658f3b60eeb056675fbff981639dfcb82d6c736..d280ba1873007960fa4fff43999a73b542dc6cb4 100644 --- a/doc/lsp.txt +++ b/doc/lsp.txt @@ -1,8 +1,8 @@ *lsp.txt* Language Server Protocol (LSP) Plugin for Vim9 Author: Yegappan Lakshmanan (yegappan AT yahoo DOT com) -For Vim version 8.2.2082 and above -Last change: Jan 10, 2021 +For Vim version 8.2.2342 and above +Last change: Jan 17, 2021 ============================================================================== *lsp-license* @@ -35,7 +35,7 @@ https://microsoft.github.io/language-server-protocol/ https://langserver.org/ -This plugin needs Vim version 8.2.2082 and after. You will need a language +This plugin needs Vim version 8.2.2342 and after. You will need a language specific server in your system to use this plugin. Refer to the above pages for a list of available language servers for the various programming languages. diff --git a/plugin/lsp.vim b/plugin/lsp.vim index 07978130054b0578d3e6283ebf854616aa2f9579..d1af460bb740de2922630f70a8f83f65fd3e407b 100644 --- a/plugin/lsp.vim +++ b/plugin/lsp.vim @@ -1,7 +1,7 @@ " LSP plugin for vim9 -" Needs Vim 8.2.2082 and higher -if v:version < 802 || !has('patch-8.2.2082') +" Needs Vim 8.2.2342 and higher +if v:version < 802 || !has('patch-8.2.2342') finish endif