From 8851e054b5c26b2003af608af381e50fb8b50adf Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Wed, 23 Jun 2021 18:20:09 -0700 Subject: [PATCH 1/2] Fix cursor position following multibyte characters Closes #427 Use `charcol` over `col` where it exists. Gate the function definition behind the feature check to avoid repeating it for every call. --- CHANGELOG.md | 3 +++ autoload/lsc/params.vim | 41 ++++++++++++++++++++++++++++------------- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e9dc9fd..e877ed5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,9 @@ - Avoid a state where no document highlights reference calls are made following a server restart. - Avoid creating public functions for callbacks which should be temporary. +- Fix character positions when the cursor is on a line following multibyte + characters for recent versions of vim. Older versions and neovim continue to + have incorrect positions. **Minor breaking changes** - Remove `noselect` from the default `completeopts` used during autocompletion. diff --git a/autoload/lsc/params.vim b/autoload/lsc/params.vim index 782527f3..56b9d8b2 100644 --- a/autoload/lsc/params.vim +++ b/autoload/lsc/params.vim @@ -2,16 +2,31 @@ function! lsc#params#textDocument() abort return {'textDocument': {'uri': lsc#uri#documentUri()}} endfunction -function! lsc#params#documentPosition() abort - return { 'textDocument': {'uri': lsc#uri#documentUri()}, - \ 'position': {'line': line('.') - 1, 'character': col('.') - 1} - \ } -endfunction - -function! lsc#params#documentRange() abort - return { 'textDocument': {'uri': lsc#uri#documentUri()}, - \ 'range': { - \ 'start': {'line': line('.') - 1, 'character': col('.') - 1}, - \ 'end': {'line': line('.') - 1, 'character': col('.')}}, - \ } -endfunction +if exists('*charcol') + function! lsc#params#documentPosition() abort + return { 'textDocument': {'uri': lsc#uri#documentUri()}, + \ 'position': {'line': line('.') - 1, 'character': charcol('.') - 1} + \ } + endfunction + function! lsc#params#documentRange() abort + return { 'textDocument': {'uri': lsc#uri#documentUri()}, + \ 'range': { + \ 'start': {'line': line('.') - 1, 'character': charcol('.') - 1}, + \ 'end': {'line': line('.') - 1, 'character': charcol('.')}}, + \ } + endfunction +else + " TODO - this is broken following multibyte characters. + function! lsc#params#documentPosition() abort + return { 'textDocument': {'uri': lsc#uri#documentUri()}, + \ 'position': {'line': line('.') - 1, 'character': col('.') - 1} + \ } + endfunction + function! lsc#params#documentRange() abort + return { 'textDocument': {'uri': lsc#uri#documentUri()}, + \ 'range': { + \ 'start': {'line': line('.') - 1, 'character': col('.') - 1}, + \ 'end': {'line': line('.') - 1, 'character': col('.')}}, + \ } + endfunction +endif From f1b7beb5333e2f5fc760b9b4cf1f334aa877748b Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Sat, 25 Mar 2023 16:51:39 -0700 Subject: [PATCH 2/2] Replace more uses of col Use a single utility that wraps charcol when possible, and falls back on col otherwise. Older vim without charcol will not handle multibyte characters for now. Continue to use `col` for the tag stack, and `virtcol` for setting character position. In places where the column is used to index into a string value, prefer `charcol` when available. --- autoload/lsc/complete.vim | 8 +++---- autoload/lsc/cursor.vim | 2 +- autoload/lsc/diagnostics.vim | 2 +- autoload/lsc/params.vim | 46 ++++++++++++++---------------------- autoload/lsc/util.vim | 12 ++++++++++ 5 files changed, 36 insertions(+), 34 deletions(-) diff --git a/autoload/lsc/complete.vim b/autoload/lsc/complete.vim index fa08feb9..518beb82 100644 --- a/autoload/lsc/complete.vim +++ b/autoload/lsc/complete.vim @@ -63,7 +63,7 @@ function! s:isCompletable() abort return v:false endif if s:next_char !~# '\w' | return v:false | endif - let l:cur_col = col('.') + let l:cur_col = lsc#util#currentChar() let l:min_length = exists('g:lsc_autocomplete_length') ? \ g:lsc_autocomplete_length : 3 if l:min_length == v:false | return v:false | endif @@ -108,8 +108,8 @@ function! s:SuggestCompletions(items) abort return endif let l:start = s:FindStart(a:items) - let l:base = l:start != col('.') - \ ? getline('.')[l:start - 1:col('.') - 2] + let l:base = l:start != lsc#util#currentChar() + \ ? getline('.')[l:start - 1:lsc#util#currentChar() - 2] \ : '' let l:completion_items = s:CompletionItems(l:base, a:items) call s:SetCompleteOpt() @@ -169,7 +169,7 @@ endfunction " Finds the 1-based index of the character after the last non word character " behind the cursor. function! s:GuessCompletionStart() abort - let l:search = col('.') - 2 + let l:search = lsc#util#currentChar() - 2 let l:line = getline('.') while l:search > 0 let l:char = l:line[l:search] diff --git a/autoload/lsc/cursor.vim b/autoload/lsc/cursor.vim index 2cd30029..5f77bc30 100644 --- a/autoload/lsc/cursor.vim +++ b/autoload/lsc/cursor.vim @@ -120,7 +120,7 @@ endfunction " not in any reference. function! lsc#cursor#isInReference(references) abort let l:line = line('.') - let l:col = col('.') + let l:col = lsc#util#currentChar() let l:idx = 0 for l:reference in a:references for l:range in l:reference.ranges diff --git a/autoload/lsc/diagnostics.vim b/autoload/lsc/diagnostics.vim index c7465e97..000532be 100644 --- a/autoload/lsc/diagnostics.vim +++ b/autoload/lsc/diagnostics.vim @@ -346,7 +346,7 @@ function! lsc#diagnostics#underCursor() abort return {} endif let l:diagnostics = l:file_diagnostics[l:line] - let l:col = col('.') + let l:col = lsc#util#currentChar() let l:closest_diagnostic = {} let l:closest_distance = -1 let l:closest_is_within = v:false diff --git a/autoload/lsc/params.vim b/autoload/lsc/params.vim index 56b9d8b2..84088a0d 100644 --- a/autoload/lsc/params.vim +++ b/autoload/lsc/params.vim @@ -2,31 +2,21 @@ function! lsc#params#textDocument() abort return {'textDocument': {'uri': lsc#uri#documentUri()}} endfunction -if exists('*charcol') - function! lsc#params#documentPosition() abort - return { 'textDocument': {'uri': lsc#uri#documentUri()}, - \ 'position': {'line': line('.') - 1, 'character': charcol('.') - 1} - \ } - endfunction - function! lsc#params#documentRange() abort - return { 'textDocument': {'uri': lsc#uri#documentUri()}, - \ 'range': { - \ 'start': {'line': line('.') - 1, 'character': charcol('.') - 1}, - \ 'end': {'line': line('.') - 1, 'character': charcol('.')}}, - \ } - endfunction -else - " TODO - this is broken following multibyte characters. - function! lsc#params#documentPosition() abort - return { 'textDocument': {'uri': lsc#uri#documentUri()}, - \ 'position': {'line': line('.') - 1, 'character': col('.') - 1} - \ } - endfunction - function! lsc#params#documentRange() abort - return { 'textDocument': {'uri': lsc#uri#documentUri()}, - \ 'range': { - \ 'start': {'line': line('.') - 1, 'character': col('.') - 1}, - \ 'end': {'line': line('.') - 1, 'character': col('.')}}, - \ } - endfunction -endif +function! lsc#params#documentPosition() abort + return { 'textDocument': {'uri': lsc#uri#documentUri()}, + \ 'position': { + \ 'line': line('.') - 1, + \ 'character': lsc#util#currentChar() - 1 + \ } + \} +endfunction +function! lsc#params#documentRange() abort + return { 'textDocument': {'uri': lsc#uri#documentUri()}, + \ 'range': { + \ 'start': { + \ 'line': line('.') - 1, + \ 'character': lsc#util#currentChar() - 1, + \ }, + \ 'end': {'line': line('.') - 1, 'character': lsc#util#currentChar()}}, + \} +endfunction diff --git a/autoload/lsc/util.vim b/autoload/lsc/util.vim index f15f2514..7e3f287d 100644 --- a/autoload/lsc/util.vim +++ b/autoload/lsc/util.vim @@ -170,3 +170,15 @@ function! s:IgnoreArgs(name, Callback, args, ...) abort let g:lsc_last_error_callback = [a:Callback, a:args] endtry endfunction + +if exists('*charcol') + function! lsc#util#currentChar() + return charcol('.') + endfunction +else + function! lsc#util#currentChar() + " TODO - Can charcol be implemented manually? + " For now, vim without charcol does not handle multibyte characters + return col('.') + endfunction +endif