padding = 3
symbol = DiagSevToSymbolText(diag.severity)
else
- padding = diag.range.start.character
+ var charIdx = util.GetCharIdxWithoutCompChar(bnr, diag.range.start)
+ padding = charIdx
if padding > 0
- padding = strdisplaywidth(getline(diag.range.start.line + 1)[ : diag.range.start.character - 1])
+ padding = strdisplaywidth(getline(diag.range.start.line + 1)[ : charIdx - 1])
endif
endif
def ShowDiagInPopup(diag: dict<any>)
var dlnum = diag.range.start.line + 1
var ltext = dlnum->getline()
- var dlcol = ltext->byteidx(diag.range.start.character + 1)
+ var dlcol = ltext->byteidxcomp(diag.range.start.character) + 1
var lastline = line('$')
if dlnum > lastline
var diags_in_line = GetDiagsByLine(bnr, lnum)
for diag in diags_in_line
+ var startCharIdx = util.GetCharIdxWithoutCompChar(bnr, diag.range.start)
+ var endCharIdx = util.GetCharIdxWithoutCompChar(bnr, diag.range.end)
if atPos
- if col >= diag.range.start.character + 1 && col < diag.range.end.character + 1
+ if col >= startCharIdx + 1 && col < endCharIdx + 1
return diag
endif
- elseif col <= diag.range.start.character + 1
+ elseif col <= startCharIdx + 1
return diag
endif
endfor
# Utility function to do the actual jump
def JumpDiag(diag: dict<any>)
- setcursorcharpos(diag.range.start.line + 1, diag.range.start.character + 1)
- if !opt.lspOptions.showDiagWithVirtualText
- :redraw
- DisplayDiag(diag)
- endif
+ var startPos: dict<number> = diag.range.start
+ setcursorcharpos(startPos.line + 1,
+ util.GetCharIdxWithoutCompChar(bufnr(), startPos) + 1)
+ if !opt.lspOptions.showDiagWithVirtualText
+ :redraw
+ DisplayDiag(diag)
+ endif
enddef
# jump to the next/previous/first diagnostic message in the current buffer
for diag in (which == 'next' || which == 'here') ?
diags : diags->copy()->reverse()
var lnum = diag.range.start.line + 1
- var col = diag.range.start.character + 1
+ var col = util.GetCharIdxWithoutCompChar(bnr, diag.range.start) + 1
if (which == 'next' && (lnum > curlnum || lnum == curlnum && col > curcol))
|| (which == 'prev' && (lnum < curlnum || lnum == curlnum
&& col < curcol))
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
+ if col <= 0
+ return col
+ endif
- var ltext: list<string> = bnr->getbufline(pos.line + 1)
- if !ltext->empty()
- var bidx = ltext[0]->byteidx(col)
- if bidx != -1
- return bidx
- endif
+ # Need a loaded buffer to read the line and compute the offset
+ bnr->bufload()
+
+ var ltext: string = GetBufOneLine(bnr, pos.line + 1)
+ if ltext->empty()
+ return col
+ endif
+
+ var byteIdx = ltext->byteidxcomp(col)
+ if byteIdx != -1
+ return byteIdx
+ endif
+
+ return col
+enddef
+
+# Get the index of the character at [pos.line, pos.character] in buffer "bnr"
+# without counting the composing characters. The LSP server counts composing
+# characters as separate characters whereas Vim string indexing ignores the
+# composing characters.
+export def GetCharIdxWithoutCompChar(bnr: number, pos: dict<number>): number
+ var col: number = pos.character
+ # When on the first character, nothing to do.
+ if col <= 0
+ return col
+ endif
+
+ # Need a loaded buffer to read the line and compute the offset
+ bnr->bufload()
+
+ var ltext: string = GetBufOneLine(bnr, pos.line + 1)
+ if ltext->empty()
+ return col
+ endif
+
+ # Convert the character index that includes composing characters as separate
+ # characters to a byte index and then back to a character index ignoring the
+ # composing characters.
+ var byteIdx = ltext->byteidxcomp(col)
+ if byteIdx != -1
+ if byteIdx == ltext->strlen()
+ # Byte index points to the byte after the last byte.
+ return ltext->strcharlen()
+ else
+ return ltext->charidx(byteIdx, v:false)
endif
endif
return col
enddef
+# Get the index of the character at [pos.line, pos.character] in buffer "bnr"
+# counting the composing characters as separate characters. The LSP server
+# counts composing characters as separate characters whereas Vim string
+# indexing ignores the composing characters.
+export def GetCharIdxWithCompChar(ltext: string, charIdx: number): number
+ # When on the first character, nothing to do.
+ if charIdx <= 0 || ltext->empty()
+ return charIdx
+ endif
+
+ # Convert the character index that doesn't include composing characters as
+ # separate characters to a byte index and then back to a character index
+ # that includes the composing characters as separate characters
+ var byteIdx = ltext->byteidx(charIdx)
+ if byteIdx != -1
+ if byteIdx == ltext->strlen()
+ return ltext->strchars()
+ else
+ return ltext->charidx(byteIdx, v:true)
+ endif
+ endif
+
+ return charIdx
+enddef
+
# push the current location on to the tag stack
export def PushCursorToTagStack()
settagstack(winnr(), {items: [
else
exe $'{cmdmods} split {fname}'
endif
- setcursorcharpos(range.start.line + 1, range.start.character + 1)
+ setcursorcharpos(range.start.line + 1,
+ GetCharIdxWithoutCompChar(bufnr(), range.start) + 1)
enddef
# 'indexof' is to new to use it, use this instead.
:%bw!
enddef
+# Test for :LspFormat when using composing characters
+def g:Test_LspFormat_ComposingChars()
+ :silent! edit Xtest.c
+ sleep 200m
+ var lines =<< trim END
+ void fn(int aVar)
+ {
+ int 馃槉馃槉馃槉馃槉 = aVar + 1;
+ int a虂b虂a虂b虂 = aVar + 1;
+ int a台虂a台虂a台虂a台虂 = aVar + 1;
+ }
+ END
+ setline(1, lines)
+ g:WaitForServerFileLoad(0)
+ :redraw!
+ :LspFormat
+ var expected =<< trim END
+ void fn(int aVar) {
+ int 馃槉馃槉馃槉馃槉 = aVar + 1;
+ int a虂b虂a虂b虂 = aVar + 1;
+ int a台虂a台虂a台虂a台虂 = aVar + 1;
+ }
+ END
+ assert_equal(expected, getline(1, '$'))
+ :%bw!
+enddef
+
# Test for formatting a file using 'formatexpr'
def g:Test_LspFormatExpr()
:silent! edit Xtest.c
:%bw!
enddef
+# Test for :LspShowReferences when using composing characters
+def g:Test_LspShowReferences_ComposingChars()
+ :silent! edit Xtest.c
+ sleep 200m
+ var lines: list<string> =<< trim END
+ #include <stdio.h>
+ void fn(int aVar)
+ {
+ printf("aVar = %d\n", aVar);
+ printf("馃槉馃槉馃槉馃槉 = %d\n", aVar);
+ printf("a虂b虂a虂b虂 = %d\n", aVar);
+ printf("a台虂a台虂a台虂a台虂 = %d\n", aVar);
+ }
+ END
+ setline(1, lines)
+ g:WaitForServerFileLoad(0)
+ redraw!
+ cursor(4, 27)
+ :LspShowReferences
+ var qfl: list<dict<any>> = getloclist(0)
+ assert_equal([2, 13], [qfl[0].lnum, qfl[0].col])
+ assert_equal([4, 27], [qfl[1].lnum, qfl[1].col])
+ assert_equal([5, 39], [qfl[2].lnum, qfl[2].col])
+ assert_equal([6, 35], [qfl[3].lnum, qfl[3].col])
+ assert_equal([7, 43], [qfl[4].lnum, qfl[4].col])
+ :lclose
+
+ :%bw!
+enddef
+
# Test for LSP diagnostics
def g:Test_LspDiag()
:silent! edit Xtest.c
:%bw!
enddef
+# Test for :LspDiagShow when using composing characters
+def g:Test_LspDiagShow_ComposingChars()
+ :silent! edit Xtest.c
+ sleep 200m
+ var lines =<< trim END
+ #include <stdio.h>
+ void fn(int aVar)
+ {
+ printf("aVar = %d\n", aVar);
+ printf("馃槉馃槉馃槉馃槉 = %d\n". aVar);
+ printf("a虂b虂a虂b虂 = %d\n". aVar);
+ printf("a台虂a台虂a台虂a台虂 = %d\n". aVar);
+ }
+ END
+ setline(1, lines)
+ g:WaitForServerFileLoad(3)
+ :redraw!
+ :LspDiagShow
+ var qfl: list<dict<any>> = getloclist(0)
+ assert_equal([5, 37], [qfl[0].lnum, qfl[0].col])
+ assert_equal([6, 33], [qfl[1].lnum, qfl[1].col])
+ assert_equal([7, 41], [qfl[2].lnum, qfl[2].col])
+ :lclose
+ :%bw!
+enddef
+
# Test for LSP diagnostics handler
def g:Test_LspProcessDiagHandler()
g:LSPTest_modifyDiags = true
:%bw!
enddef
+# Test for :LspCodeAction with symbols containing composing characters
+def g:Test_LspCodeAction_ComposingChars()
+ silent! edit Xtest.c
+ sleep 200m
+ var lines =<< trim END
+ #include <stdio.h>
+ void fn(int aVar)
+ {
+ printf("aVar = %d\n", aVar);
+ printf("馃槉馃槉馃槉馃槉 = %d\n", aVar):
+ printf("a虂b虂a虂b虂 = %d\n", aVar):
+ printf("a台虂a台虂a台虂a台虂 = %d\n", aVar):
+ }
+ END
+ setline(1, lines)
+ g:WaitForServerFileLoad(3)
+ :redraw!
+ cursor(5, 5)
+ redraw!
+ :LspCodeAction 1
+ assert_equal(' printf("馃槉馃槉馃槉馃槉 = %d\n", aVar);', getline(5))
+ cursor(6, 5)
+ redraw!
+ :LspCodeAction 1
+ assert_equal(' printf("a虂b虂a虂b虂 = %d\n", aVar);', getline(6))
+ cursor(7, 5)
+ redraw!
+ :LspCodeAction 1
+ assert_equal(' printf("a台虂a台虂a台虂a台虂 = %d\n", aVar);', getline(7))
+
+ :bw!
+enddef
+
# Test for :LspRename
def g:Test_LspRename()
silent! edit Xtest.c
:%bw!
enddef
+# Test for :LspRename with composing characters
+def g:Test_LspRename_ComposingChars()
+ silent! edit Xtest.c
+ sleep 200m
+ var lines: list<string> =<< trim END
+ #include <stdio.h>
+ void fn(int aVar)
+ {
+ printf("aVar = %d\n", aVar);
+ printf("馃槉馃槉馃槉馃槉 = %d\n", aVar);
+ printf("a虂b虂a虂b虂 = %d\n", aVar);
+ printf("a台虂a台虂a台虂a台虂 = %d\n", aVar);
+ }
+ END
+ setline(1, lines)
+ g:WaitForServerFileLoad(0)
+ redraw!
+ cursor(2, 12)
+ :LspRename bVar
+ redraw!
+ var expected: list<string> =<< trim END
+ #include <stdio.h>
+ void fn(int bVar)
+ {
+ printf("aVar = %d\n", bVar);
+ printf("馃槉馃槉馃槉馃槉 = %d\n", bVar);
+ printf("a虂b虂a虂b虂 = %d\n", bVar);
+ printf("a台虂a台虂a台虂a台虂 = %d\n", bVar);
+ }
+ END
+ assert_equal(expected, getline(1, '$'))
+ :%bw!
+enddef
+
# Test for :LspSelectionExpand and :LspSelectionShrink
def g:Test_LspSelection()
silent! edit Xtest.c
:%bw!
enddef
+# Test for :LspGotoDefinition when using composing characters
+def g:Test_LspGotoDefinition_With_ComposingCharacters()
+ :silent! edit Xtest.c
+ sleep 200m
+ var lines: list<string> =<< trim END
+ #include <stdio.h>
+ void fn(int aVar)
+ {
+ printf("aVar = %d\n", aVar);
+ printf("馃槉馃槉馃槉馃槉 = %d\n", aVar);
+ printf("a虂b虂a虂b虂 = %d\n", aVar);
+ printf("a台虂a台虂a台虂a台虂 = %d\n", aVar);
+ }
+ END
+ setline(1, lines)
+ g:WaitForServerFileLoad(0)
+ redraw!
+
+ for [lnum, colnr] in [[4, 27], [5, 39], [6, 35], [7, 43]]
+ cursor(lnum, colnr)
+ :LspGotoDefinition
+ assert_equal([2, 13], [line('.'), col('.')])
+ endfor
+
+ :%bw!
+enddef
+
+# Test for :LspGotoDefinition when using composing characters
+def g:Test_LspGotoDefinition_After_ComposingCharacters()
+ :silent! edit Xtest.c
+ sleep 200m
+ var lines =<< trim END
+ void fn(int aVar)
+ {
+ int 馃槉馃槉馃槉馃槉, bVar;
+ int a虂b虂a虂b虂, cVar;
+ int a台虂a台虂a台虂a台虂, dVar;
+ bVar = 10;
+ cVar = 10;
+ dVar = 10;
+ }
+ END
+ setline(1, lines)
+ g:WaitForServerFileLoad(0)
+ :redraw!
+ cursor(6, 5)
+ :LspGotoDefinition
+ assert_equal([3, 27], [line('.'), col('.')])
+ cursor(7, 5)
+ :LspGotoDefinition
+ assert_equal([4, 23], [line('.'), col('.')])
+ cursor(8, 5)
+ :LspGotoDefinition
+ assert_equal([5, 31], [line('.'), col('.')])
+
+ :%bw!
+enddef
+
# Test for :LspHighlight
def g:Test_LspHighlight()
silent! edit Xtest.c
:%bw!
enddef
+# Test for :LspSymbolSearch when using composing characters
+def g:Test_LspSymbolSearch_ComposingChars()
+ silent! edit Xtest.c
+ sleep 200m
+ var lines: list<string> =<< trim END
+ typedef void 馃槉馃槉馃槉馃槉;
+ typedef void a虂b虂a虂b虂;
+ typedef void a台虂a台虂a台虂a台虂;
+
+ 馃槉馃槉馃槉馃槉 Func1()
+ {
+ }
+
+ a虂b虂a虂b虂 Func2()
+ {
+ }
+
+ a台虂a台虂a台虂a台虂 Func3()
+ {
+ }
+ END
+ setline(1, lines)
+ g:WaitForServerFileLoad(0)
+
+ cursor(1, 1)
+ feedkeys(":LspSymbolSearch Func1\<CR>\<CR>", "xt")
+ assert_equal([5, 18], [line('.'), col('.')])
+ cursor(1, 1)
+ feedkeys(":LspSymbolSearch Func2\<CR>\<CR>", "xt")
+ assert_equal([9, 14], [line('.'), col('.')])
+ cursor(1, 1)
+ feedkeys(":LspSymbolSearch Func3\<CR>\<CR>", "xt")
+ assert_equal([13, 22], [line('.'), col('.')])
+
+ :%bw!
+enddef
+
# Test for :LspIncomingCalls
def g:Test_LspIncomingCalls()
silent! edit Xtest.c
delete('Xtest.c')
enddef
+# Test for setting the 'tagfunc' with composing characters in symbols
+def g:Test_LspTagFunc_ComposingChars()
+ var lines =<< trim END
+ void fn(int aVar)
+ {
+ int 馃槉馃槉馃槉馃槉, bVar;
+ int a虂b虂a虂b虂, cVar;
+ int a台虂a台虂a台虂a台虂, dVar;
+ bVar = 10;
+ cVar = 10;
+ dVar = 10;
+ }
+ END
+ writefile(lines, 'Xtest.c', 'D')
+ :silent! edit! Xtest.c
+ g:WaitForServerFileLoad(0)
+ :setlocal tagfunc=lsp#lsp#TagFunc
+ cursor(6, 5)
+ :exe "normal \<C-]>"
+ assert_equal([3, 27], [line('.'), col('.')])
+ cursor(7, 5)
+ :exe "normal \<C-]>"
+ assert_equal([4, 23], [line('.'), col('.')])
+ cursor(8, 5)
+ :exe "normal \<C-]>"
+ assert_equal([5, 31], [line('.'), col('.')])
+ :set tagfunc&
+
+ :%bw!
+enddef
+
# Test for the LspDiagsUpdated autocmd
def g:Test_LspDiagsUpdated_Autocmd()
g:LspAutoCmd = 0
:bw!
enddef
+# Test for doing omni completion for symbols with composing characters
+def g:Test_OmniComplete_ComposingChars()
+ :silent! edit Xtest.c
+ sleep 200m
+ var lines: list<string> =<< trim END
+ void Func1(void)
+ {
+ int 馃槉馃槉馃槉馃槉, aVar;
+ int a虂b虂a虂b虂, bVar;
+ int a台虂a台虂a台虂a台虂, cVar;
+
+
+
+ }
+ END
+ setline(1, lines)
+ g:WaitForServerFileLoad(0)
+ redraw!
+
+ cursor(6, 4)
+ feedkeys("aaV\<C-X>\<C-O> = 馃槉馃槉\<C-X>\<C-O>;", 'xt')
+ assert_equal(' aVar = 馃槉馃槉馃槉馃槉;', getline('.'))
+ cursor(7, 4)
+ feedkeys("abV\<C-X>\<C-O> = a虂b虂\<C-X>\<C-O>;", 'xt')
+ assert_equal(' bVar = a虂b虂a虂b虂;', getline('.'))
+ cursor(8, 4)
+ feedkeys("acV\<C-X>\<C-O> = a台虂a台虂\<C-X>\<C-O>;", 'xt')
+ assert_equal(' cVar = a台虂a台虂a台虂a台虂;', getline('.'))
+ feedkeys("oa虂b虂\<C-X>\<C-O> = a台虂a台虂\<C-X>\<C-O>;", 'xt')
+ assert_equal(' a虂b虂a虂b虂 = a台虂a台虂a台虂a台虂;', getline('.'))
+ feedkeys("oa台虂a台虂\<C-X>\<C-O> = a虂b虂\<C-X>\<C-O>;", 'xt')
+ assert_equal(' a台虂a台虂a台虂a台虂 = a虂b虂a虂b虂;', getline('.'))
+ :%bw!
+enddef
+
# Test for the :LspServer command.
def g:Test_LspServer()
new a.raku