From 7afc8ac1cdbd7828cbdc9a3b901d5f30b9a1e874 Mon Sep 17 00:00:00 2001 From: Camille Dejoye Date: Tue, 9 Jun 2020 20:12:04 +0200 Subject: [PATCH] improvement: preserve the cursor position The idea was to be able to keep the cursor on the same position than before an operation. To solve this issue I first extract the position of the cursor, this position is given relative to the argument list. It contains the number of the argument and a column number relative to the start of the argument. This way it is possible to position the cursor later on and I hope it should be pretty easy for any extension to adapt it if needed since it's provided in the container dictionary. --- autoload/argwrap.vim | 10 -- autoload/argwrap/hooks/000_curpos.vim | 148 ++++++++++++++++++++++++++ 2 files changed, 148 insertions(+), 10 deletions(-) create mode 100644 autoload/argwrap/hooks/000_curpos.vim diff --git a/autoload/argwrap.vim b/autoload/argwrap.vim index 35f704c..1b1e4c1 100644 --- a/autoload/argwrap.vim +++ b/autoload/argwrap.vim @@ -235,8 +235,6 @@ function! argwrap#initSetting(name, value) abort endfunction function! argwrap#toggle() - let l:cursor = getpos('.') - let l:linePrefix = argwrap#getSetting('line_prefix') let l:padded = argwrap#getSetting('padded_braces') let l:tailComma = argwrap#getSetting('tail_comma') @@ -260,19 +258,11 @@ function! argwrap#toggle() let l:container = argwrap#extractContainer(l:range) if l:range.lineStart == l:range.lineEnd call argwrap#hooks#execute('pre_wrap', l:range, l:container, l:arguments) - call argwrap#wrapContainer(l:range, l:container, l:arguments, l:wrapBrace, l:tailComma, l:tailCommaBraces, l:tailIndentBraces, l:linePrefix, l:commaFirst, l:commaFirstIndent) - let l:cursor[1] = l:range.lineStart + 1 - call argwrap#hooks#execute('post_wrap', l:range, l:container, l:arguments) else call argwrap#hooks#execute('pre_unwrap', l:range, l:container, l:arguments) - call argwrap#unwrapContainer(l:range, l:container, l:arguments, l:padded) - let l:cursor[1] = l:range.lineStart - call argwrap#hooks#execute('post_unwrap', l:range, l:container, l:arguments) endif - - call setpos('.', l:cursor) endfunction diff --git a/autoload/argwrap/hooks/000_curpos.vim b/autoload/argwrap/hooks/000_curpos.vim new file mode 100644 index 0000000..75f81ca --- /dev/null +++ b/autoload/argwrap/hooks/000_curpos.vim @@ -0,0 +1,148 @@ +function! s:extractCursorPositionForUnwrappedArguments(range, arguments) abort " {{{ + let l:cursorColumn = col('.') + let l:lineText = getline(a:range.lineStart) + let l:position = {} + + let l:argumentNumber = 0 + for argument in a:arguments + let l:argumentNumber += 1 + let l:argumentStart = stridx(l:lineText, argument) + let l:argumentEnd = l:argumentStart + len(argument) + + if l:cursorColumn <= l:argumentStart + let l:cursorColumn = l:argumentStart + 1 + endif + + if l:argumentEnd < l:cursorColumn + if l:lineText[l:cursorColumn - 1:] =~ '\v^,' " Cursor on the separator + if !argwrap#getSetting('comma_first') + let l:cursorColumn = l:argumentEnd + 1 + else + let l:position.argumentNumber = l:argumentNumber + 1 + let l:position.column = -1 + + break + endif + elseif l:lineText[l:cursorColumn - 1:] =~ '\v^\s+,' " Cursor before the separator + let l:cursorColumn = l:argumentEnd + endif + endif + + if l:cursorColumn <= l:argumentEnd + 1 + let l:position.argumentNumber = l:argumentNumber + let l:position.column = l:cursorColumn - l:argumentStart + + break + end + endfor + + " If the position was not found it's because the cursor is after the last + " argument + if empty(l:position) + let l:position.argumentNumber = l:argumentNumber + let l:position.column = l:argumentEnd - l:argumentStart + endif + + return l:position +endfunction " }}} + +function! s:extractCursorPositionForWrappedArguments(range, arguments) abort " {{{ + let l:position = {} + let l:isCommaFirst = argwrap#getSetting('comma_first') + let l:cursorColumn = col('.') + let l:cursorArgumentNumber = line('.') - a:range.lineStart + " In case the cursor is on the start line + let l:cursorArgumentNumber = min([len(a:arguments), l:cursorArgumentNumber]) + " In case the cursor is on the end line + let l:cursorArgumentNumber = max([1, l:cursorArgumentNumber]) + let l:argumentLine = getline('.') + let l:argumentText = a:arguments[l:cursorArgumentNumber - 1] + let l:argumentStart = stridx(l:argumentLine, l:argumentText) + let l:argumentEnd = l:argumentStart + len(l:argumentText) + let l:position.argumentNumber = l:cursorArgumentNumber + let l:position.column = l:cursorColumn - l:argumentStart + + if l:cursorColumn <= l:argumentStart + let l:position.column = 1 + + if l:isCommaFirst + if l:argumentLine[l:cursorColumn - 1:] =~ '\v^,' " Cursor on the separator + " The cursor should be placed on the separtor + let l:position.argumentNumber -= 1 + let l:position.column = len(a:arguments[l:position.argumentNumber - 1]) + 1 + elseif l:argumentLine[l:cursorColumn - 1:] =~ '\v^\s+,' " Cursor before the separator + " The cursor should be placed on the end of the previous argument + let l:position.argumentNumber -= 1 + let l:position.column = len(a:arguments[l:position.argumentNumber - 1]) + endif + endif + endif + + if l:argumentEnd < l:cursorColumn + let l:position.column = len(l:argumentText) + + if !l:isCommaFirst + if l:argumentLine[l:cursorColumn - 1:] =~ '\v^\s+,' " Cursor before the separator + " The cursor should be placed on the end of the current argument + elseif l:argumentLine[l:cursorColumn - 1:] =~ '\v^,' " Cursor on the separator + " The cursor should be placed on the separator + let l:position.column += 1 + endif + endif + endif + + return l:position +endfunction " }}} + +function! s:getCursorPositionForWrappedArguments(range, container, arguments) abort " {{{ + let l:line = a:range.lineStart + a:container.cursor.argumentNumber + let l:argumentStart = stridx(getline(l:line), a:arguments[a:container.cursor.argumentNumber - 1]) + let l:column = l:argumentStart + a:container.cursor.column + + return {'line': l:line, 'column': l:column} +endfunction " }}} + +function! s:getCursorPositionForUnwrappedArguments(range, container, arguments) abort " {{{ + let l:line = a:range.lineStart + let l:column = a:range.colStart + + " For each arguments before the one where the cursor must be positioned + for index in range(a:container.cursor.argumentNumber - 1) + " Add the length of the argument + 2 for the separator ', ' + let l:column += len(a:arguments[index]) + 2 + endfor + + let l:column += a:container.cursor.column + + return {'line': l:line, 'column': l:column} +endfunction " }}} + +function! s:setCursorPosition(position) abort " {{{ + let l:curpos = getcurpos() + let l:curpos[1] = a:position.line + let l:curpos[2] = a:position.column + + call setpos('.', l:curpos) +endfunction " }}} + +function! argwrap#hooks#000_curpos#pre_wrap(range, container, arguments) abort " {{{ + let a:container.cursor = s:extractCursorPositionForUnwrappedArguments(a:range, a:arguments) +endfunction " }}} + +function! argwrap#hooks#000_curpos#pre_unwrap(range, container, arguments) abort " {{{ + let a:container.cursor = s:extractCursorPositionForWrappedArguments(a:range, a:arguments) +endfunction " }}} + +function! argwrap#hooks#000_curpos#post_wrap(range, container, arguments) abort " {{{ + let l:position = s:getCursorPositionForWrappedArguments(a:range, a:container, a:arguments) + + call s:setCursorPosition(l:position) +endfunction " }}} + +function! argwrap#hooks#000_curpos#post_unwrap(range, container, arguments) abort " {{{ + let l:position = s:getCursorPositionForUnwrappedArguments(a:range, a:container, a:arguments) + + call s:setCursorPosition(l:position) +endfunction " }}} + +" vim: ts=2 sw=2 et fdm=marker