I'd like to create a command and bindings for appending a selected piece of text into another file located next to it with a timestamp. I've put some scraps together from other posts, yet I barely know what I am doing and I am not getting what I expect.
fun! MoveSelectedLinesToFile(filename)
exec "'<,'>w! >>" . a:filename
norm gvd
endfunc
fun! ArchiveSelectedLinesToFile(filename)
call writefile( ["[" . strftime("%Y-%m-%d %H:%M:%S") . "]"], a:filename, "a" )
call MoveSelectedLinesToFile(a:filename)
call writefile( ["",""], a:filename, "a" )
endfunc
vnoremap a :call ArchiveSelectedLinesToFile(expand('%:p') . '.arc.md')<CR>
using this on a sequence of lines 4-6 of this content:
1
2
3
4
5
6
7
8
9
The archive file shows:
[2022-09-19 14:52:10]
[2022-09-19 14:52:10]
while it should show
[2022-09-19 14:52:10]
4
5
6
and the source file was altered to
1
2
3
8
9
which is one line to much as 7 was wrongfully taken. I am on Windows, if that means anything.
Q:
ArchiveSelectedLinesToFile
. Where from there exactly? And why?I am aware that none of this is probably as clean as it could be, yet I do not know better as of now. Improvement suggestions of form are appreciated.
Background: I recently started using VimWiki for taking notes, including my TODOs, my call-stack if you will. I have one main TODO file, however large tasks warrant their own file. Now whenever I am done with a TODO I might have put more notes under the heading of that TODO since its inception, holding valuable information for future me. Without going to the length of creating an extra wiki entry for the topic, traditionally I just deleted the lines, I've come to think it would be neat to archive most of them away instead. I think using Version Control for this is overkill. By putting those contents into another file with a timestamp this acts as a sort of history. Further I could still choose between archiving and deleting to decide what might be relevant further and what's not.
:help writefile()
takes a list as first argument so a better strategy would be to build the list first, with timestamp and all, and then use a single writefile()
.
For that, you need to start by handling ranges properly. When you call a function defined like this over a range, the function is called for each line:
function! Foo()
echo line('.')
endfunction
:4,6call Foo()
4
5
6
which is totally fine if that's your goal, but this is not the case here. You want your function to be called once, so you need to define it with the range
argument:
function! Foo() range
echo line('.')
endfunction
:4,6call Foo()
4
which allows you to handle the range yourself, within the function:
function! Foo() range
echo a:firstline
echo a:lastline
endfunction
:4,6call Foo()
4
6
See :help :func-range
.
First, generate a timestamp:
function! ArchiveSelectedLinesToFile() range
let timestamp = '[' . strftime('%Y-%m-%d %H:%M:%S') . ']'
endfunction
Second, store the selected lines in a variable:
function! ArchiveSelectedLinesToFile() range
let timestamp = '[' . strftime('%Y-%m-%d %H:%M:%S') . ']'
let lines = getline(a:firstline, a:lastline)
endfunction
See :help getline()
.
Third, put them together in a list:
function! ArchiveSelectedLinesToFile() range
let timestamp = '[' . strftime('%Y-%m-%d %H:%M:%S') . ']'
let lines = getline(a:firstline, a:lastline)
let archival_data = [timestamp] + lines
endfunction
See :help list-concatenation
.
Fourth, write the list to the given file:
function! ArchiveSelectedLinesToFile(filename) range
let timestamp = '[' . strftime('%Y-%m-%d %H:%M:%S') . ']'
let lines = getline(a:firstline, a:lastline)
let archival_data = [timestamp] + lines
call writefile(archival_data, a:filename, 'a')
endfunction
Fifth, delete the selected lines:
function! ArchiveSelectedLinesToFile(filename) range
let timestamp = '[' . strftime('%Y-%m-%d %H:%M:%S') . ']'
let lines = getline(a:firstline, a:lastline)
let archival_data = [timestamp] + lines
call writefile(archival_data, a:filename, 'a')
execute a:firstline . ',' . a:lastline . 'd _'
endfunction
See :help :range
, :help :d
and :help "_
.
You are not done yet, though, because of all the text objects that start with a
. This will cause timing issues so you will have to map your function call to a different key that is not the start of some command, motion, or mapping. Additionally, you might want to restrict your mapping to visual mode. Here is an example with <F5>
, YMMV:
xnoremap <F5> :call ArchiveSelectedLinesToFile(expand('%:p') . '.arc.md')<CR>