mirror of
https://github.com/inkarkat/vim-ingo-library.git
synced 2026-05-29 11:18:51 +02:00
153 lines
5.4 KiB
VimL
153 lines
5.4 KiB
VimL
" ingo/change.vim: Functions around the last changed text.
|
|
"
|
|
" DEPENDENCIES:
|
|
" - ingo/cursor/move.vim autoload script
|
|
" - ingo/pos.vim autoload script
|
|
" - ingo/str/split.vim autoload script
|
|
" - ingo/text.vim autoload script
|
|
" - ingo/undo.vim autoload script
|
|
"
|
|
" Copyright: (C) 2018-2019 Ingo Karkat
|
|
" The VIM LICENSE applies to this script; see ':help copyright'.
|
|
"
|
|
" Maintainer: Ingo Karkat <ingo@karkat.de>
|
|
|
|
function! ingo#change#IsLastAnInsertion( ... )
|
|
let l:lastChangedText = (a:0 ? a:1 : ingo#text#Get(getpos("'[")[1:2], getpos("']")[1:2], 1))
|
|
return (l:lastChangedText ==# @.)
|
|
endfunction
|
|
|
|
function! ingo#change#Get()
|
|
"******************************************************************************
|
|
"* PURPOSE:
|
|
" Get the last inserted / changed text (between marks '[,']).
|
|
"* ASSUMPTIONS / PRECONDITIONS:
|
|
" None.
|
|
"* EFFECTS / POSTCONDITIONS:
|
|
" None.
|
|
"* INPUTS:
|
|
" None.
|
|
"* RETURN VALUES:
|
|
" Last changed text, or empty string if there was no change yet or the last
|
|
" change was a deletion.
|
|
"******************************************************************************
|
|
let [l:startPos, l:endPos] = [getpos("'[")[1:2], getpos("']")[1:2]]
|
|
|
|
" If the change was an insertion, the end of change mark is set _after_ the
|
|
" last inserted character. For other changes (e.g. gU), the end of change
|
|
" mark is _on_ the last changed character. We need to compare with register
|
|
" . contents.
|
|
let l:lastInsertedText = ingo#text#Get(l:startPos, l:endPos, 1)
|
|
if ingo#change#IsLastAnInsertion(l:lastInsertedText)
|
|
return l:lastInsertedText
|
|
endif
|
|
|
|
let l:lastChangedText = ingo#text#Get(l:startPos, l:endPos, 0)
|
|
return l:lastChangedText
|
|
endfunction
|
|
|
|
function! ingo#change#IsCursorOnPreviousChange()
|
|
"******************************************************************************
|
|
"* PURPOSE:
|
|
" Test whether the cursor is inside the area marked by the '[,'] marks.
|
|
" (Depending on the type of change, it can be at the beginning, end, or
|
|
" shortly before the end.)
|
|
"* ASSUMPTIONS / PRECONDITIONS:
|
|
" None.
|
|
"* EFFECTS / POSTCONDITIONS:
|
|
" None.
|
|
"* INPUTS:
|
|
" None.
|
|
"* RETURN VALUES:
|
|
" 1 if cursor is on previous change, 0 if not.
|
|
"******************************************************************************
|
|
let [l:currentPos, l:startPos, l:endPos] = [getpos('.')[1:2], getpos("'[")[1:2], getpos("']")[1:2]]
|
|
if ! ingo#pos#IsInside(l:currentPos, l:startPos, l:endPos)
|
|
return 0
|
|
endif
|
|
|
|
if l:currentPos == l:endPos && ingo#change#IsLastAnInsertion()
|
|
return 0 " Special case: After an insertion, the change mark is positioned one after the last inserted character.
|
|
endif
|
|
|
|
return 1
|
|
endfunction
|
|
|
|
function! ingo#change#JumpAfterEndOfChange()
|
|
normal! g`]
|
|
if ! ingo#change#IsLastAnInsertion()
|
|
call ingo#cursor#move#Right()
|
|
endif
|
|
endfunction
|
|
|
|
function! ingo#change#GetOverwrittenText()
|
|
"******************************************************************************
|
|
"* PURPOSE:
|
|
" Get the text that was overwritten by the last change.
|
|
"* ASSUMPTIONS / PRECONDITIONS:
|
|
" None.
|
|
"* EFFECTS / POSTCONDITIONS:
|
|
" None.
|
|
"* INPUTS:
|
|
" None.
|
|
"* RETURN VALUES:
|
|
" Overwritten text, or empty string.
|
|
"******************************************************************************
|
|
let l:save_view = winsaveview()
|
|
let [l:startPos, l:endPos] = [getpos("'["), getpos("']")]
|
|
let [l:startLnum, l:endLnum] = [l:startPos[1], l:endPos[1]]
|
|
let l:lastLnum = line('$')
|
|
|
|
let l:textBeforeChange = ingo#text#Get([l:startLnum, 1], l:startPos[1:2], 1)
|
|
let l:textAfterChange = ingo#text#Get(l:endPos[1:2], [l:endLnum, len(getline(l:endLnum))], 0)
|
|
|
|
if ! ingo#change#IsLastAnInsertion() | let l:textAfterChange = matchstr(l:textAfterChange, '^.\zs.*') | endif
|
|
"****D echomsg string(l:textBeforeChange) string(l:textAfterChange)
|
|
|
|
let l:undoChangeNumber = ingo#undo#GetChangeNumber()
|
|
if l:undoChangeNumber < 0 | return '' | endif " Without undo, the overwritten text cannot be determined.
|
|
try
|
|
silent undo
|
|
|
|
let l:changeOffset = l:lastLnum - line('$')
|
|
let l:changedArea = join(getline(l:startLnum, l:endLnum - l:changeOffset), "\n")
|
|
|
|
let l:startOfOverwritten = ingo#str#split#AtPrefix(l:changedArea, l:textBeforeChange)
|
|
let l:overwritten = ingo#str#split#AtSuffix(l:startOfOverwritten, l:textAfterChange)
|
|
|
|
return l:overwritten
|
|
finally
|
|
silent execute 'undo' l:undoChangeNumber
|
|
|
|
" The :undo clobbered the change marks; restore them.
|
|
call ingo#change#Set(l:startPos, l:endPos)
|
|
|
|
" The :undo also affected the cursor position.
|
|
call winrestview(l:save_view)
|
|
endtry
|
|
endfunction
|
|
|
|
function! ingo#change#Set( startPos, endPos ) abort
|
|
"******************************************************************************
|
|
"* PURPOSE:
|
|
" Sets the change marks to the passed area.
|
|
"* ASSUMPTIONS / PRECONDITIONS:
|
|
" None.
|
|
"* EFFECTS / POSTCONDITIONS:
|
|
" Sets the change marks.
|
|
"* INPUTS:
|
|
" a:startPos [lnum, col] or [0, lnum, col, 0] of the start ('[) of the last
|
|
" change.
|
|
" a:endPos [lnum, col] or [0, lnum, col, 0] of the end (']) of the last
|
|
" change.
|
|
"* RETURN VALUES:
|
|
" 1 if successful, 0 if one position could not be set.
|
|
"******************************************************************************
|
|
let l:result = 0
|
|
let l:result += setpos("'[", ingo#pos#Make4(a:startPos))
|
|
let l:result += setpos("']", ingo#pos#Make4(a:endPos))
|
|
return (l:result == 0)
|
|
endfunction
|
|
|
|
" vim: set ts=8 sts=4 sw=4 noexpandtab ff=unix fdm=syntax :
|