Vim logo vim online Vim Book Ad

basic Tip #332: make footnotes in vim

 tip karma   Rating 14/5, Viewed by 1215 

created:   September 23, 2002 3:43      complexity:   basic
author:   [email protected]      as of Vim:   6.0

I found at http://groups.google.com/groups?q=vim+changing+shell+to+zsh&hl;=en&lr;=&ie;=UTF-8&selm;=S_Rh9.716%24a5.124150%40news.uchicago.edu&rnum;=4
a macro to insert footnotes in vim, but it doesn't work as of vim6.0.
so i wrote my own; this macro requires two differents shortcuts, one for entering the first footnote, the other one for all subsequent footnotes.

when you hit "K0" (first footnote) or "KK" (all other footnotes) in normal mode, your cursor is positionned at the end of the document, in the footnote & in insert mode. The "a" bookmark is set to the place where you entered the footnote in the text. so a "`a" will bring you back to the location of the footnote in the text.

" for now requires entering K0 for the first footnote and then KK                                  nmap K0 i[0]<esc>maG$i<end><enter>[0]
nmap KK maG$?\[[0-9]*\]<enter>yt]G$i<end><enter><esc>p<C-a>i<end>]<esc>`aP<C-a><right>i]<esc>maG$i<end><end>

 rate this tip  Life Changing Helpful Unfulfilling 

<<modline magic... | Syntax-based folding for c/c++/java >>

Additional Notes

[email protected], September 23, 2002 5:21
for information i just added a worthwhile special case:
we do not add the footnotes at the end of the document, but on the line before a "--".

this is useful if you have a signature at the end of your mails, eg, do:
hello[0]!

[0] footnote
--
sig

and not:
hello[0]!
--
sig

[0] footnote

here is the updated code:
nmap K0 i[0]<esc>maG?--<enter><up>$i<end><enter>[0]
nmap KK maG$?\[[0-9]*\]<enter>yt]G?--<enter><up>$i<end><enter><esc>p<C-a>i<end>]<esc>`aP<C-a><right>i]<esc>maG?--<enter><up>$i<end><end>

(i'll take any suggestion to remove code duplication, but i don't know the language well enough for now)
[email protected], September 24, 2002 6:36
ok, learned some more vim. now it's not anymore a macro, but a function.
you don't have to enter the text of the footnote separately, you are prompted for it, so it's faster to use. also, there is no more separation for the first footnote case: you all the time enter KK.
it also supports the case when there is a sig or not.

here is the whole code:

nmap KK :call InsertFootNote()<LF>

function! InsertFootNote()
.       " mark the position of the cursor
.       execute "normal ma"
.       " ask for footnote text
.       let footNoteText = input("enter text for footnote: ")
.       " was there already a footnote?
.       if search("\[[0-9]*]", "w")
.       .       " yes => get the number, copy it, increase
.       .       " it, put it at the footnote position, put
.       .       " the footnote text and position the cursor back.
.       .       execute "normal G$?\[[0-9]*\]\<enter>yt]:call GotoFootNoteLocation()\<LF>$i\<end>\<enter>\<esc>p\<C-a>i\<end>] " . footNoteText .  "\<esc>`aP\<C-a>\<right>i]\<esc>"
.       else
.       .       " no => put [0], add at the end [0] + footnote text
.       .       " and position cursor back
.       .       execute "normal i[0]\<esc>:call GotoFootNoteLocation()\<LF>$i\<end>
\<enter>\<enter>[0] " . footNoteText . "\<esc>`a"
.       endif
endfunction

" if there is a signature, the footnote
" should be positionned ontop of it, eg
" mail text
" [0] footnote 0
" --
" sig
" and not:
" mail text
" --
" sig
" [0] footnote 0
" otherwise it's at the end of the text.
function! GotoFootNoteLocation()
.       " the signature is found by the "--"
.       " pattern.
.       " i don't search from the end because
.       " a fwd will also match this and i don't want
.       " that footnotes are too far off, say after 5-6
.       " old forwarded emails.
.       if search("^--", "w")
.       .       " ok, there's a sig.
.       .       " just go on top of it.
.       .       execute "normal \<up>"
.       else
.       .       " no sig: we go at the end of the
.       .       " document.
.       .       execute "normal G$"
.       endif
endfunction
[email protected], September 24, 2002 9:12
This is easier:
inoremap ,f <esc>:call VimFootnotes()<cr>
inoremap ,r <esc>:exe b:pos<cr>

function! VimFootnotes()
    if exists("b:vimfootnotenumber")
        let b:vimfootnotenumber = b:vimfootnotenumber + 1
        let cr = ""
    else    
        let b:vimfootnotenumber = 0
        let cr = "\<cr>"
    endif  
    let b:pos = line('.').' | normal! '.virtcol('.').'|'.'4l'
    exe "normal a[".b:vimfootnotenumber."]\<esc>G"
    if search("-- $", "b")
        exe "normal O".cr."[".b:vimfootnotenumber."] "
    else    
        exe "normal o".cr."[".b:vimfootnotenumber."] "
    endif  
    startinsert!
endfunction
[email protected], September 24, 2002 13:12
This works just fine -- and is a pretty cool idea -- except that it would be nice if ,r would restart insert-mode when done.  Replacing the beginning of the rhs with <c-o> instead of <esc> works in all cases except when the footnote was added to the end of the line (more likely than not, actually, since footnotes might be added during the initial text entry).
[email protected], September 25, 2002 1:04
[email protected], i really like your 100% function implementation (as opposed to my half-macro), but i have some comments:
* detail: you don't use an input text to ask the text of the footnote, which i found nicer than providing a goto footnote (see my latest version). it's trivial to change though:

function! VimFootnotes()
    execute "normal ma"
    let footNoteText = input("enter text for footnote: ")
    if exists("b:vimfootnotenumber")
        let b:vimfootnotenumber = b:vimfootnotenumber + 1
        let cr = ""
    else
        let b:vimfootnotenumber = 0
        let cr = "\<cr>"
    endif
    let b:pos = line('.').' | normal! '.virtcol('.').'|'.'4l'
    exe "normal a[".b:vimfootnotenumber."]\<esc>G"
    if search("-- $",  "b")
        exe "normal O".cr."[".b:vimfootnotenumber."] " . footNoteText
    else
        exe "normal o".cr."[".b:vimfootnotenumber."] " . footNoteText
    endif
    execute "normal `a"
endfunction

* bigger problem: if you insert a footnote, and undo it, and insert a footnote again, the number is incremented once too much. i guess it's impossible to catch undo events for vim functions? :O(

btw, can you give me a pointer to a good vim programming website?

mikmach, September 25, 2002 2:07
1. Input field doesn't give possibility to format text
2. The best programming vim page is user manual and vim-mail list
[email protected], September 25, 2002 2:28
thanks, point one is a killer :O)
i didn't think about it. i guess at the end i'll take a mix of our scripts (for the undo thing).

thanks! your code is a good example.
mikmach, September 25, 2002 10:33
And here is another example how nice (although sophisticated) map can become terrible beast :)
Split of the window is a good compromise between input field and going down
Play with b:vimfootnotetype :) Alpha, alpha, arabic - roman for footnotes is rare

inoremap ,f <C-O>:call VimFootnotes()<CR>
inoremap ,r <C-O>:q<CR><Right>

let b:vimfootnotetype = "alpha"
function! VimFootnoteType(footnumber)
if !exists("b:vimfootnotetype")
let b:vimfootnotetype = "arabic"
endif
if (b:vimfootnotetype =~ "^alpha\\|^Alpha")
if (b:vimfootnotetype =~ "^alpha")
let upper = "0"
else
let upper = "-32"
endif
if (a:footnumber <= 26)
let ftnumber = nr2char(a:footnumber+96+upper)
elseif (a:footnumber <= 52)
   let ftnumber = nr2char(a:footnumber+70+upper).nr2char(a:footnumber+70+upper)
else
let b:vimfootnotenumber = 1
let ftnumber = nr2char(97+upper)
endif
else
let ftnumber = a:footnumber
endif
return ftnumber
endfunction

function! VimFootnotes()
if exists("b:vimfootnotenumber")
let b:vimfootnotenumber = b:vimfootnotenumber + 1
let b:vimfootnotemark = VimFootnoteType(b:vimfootnotenumber)
let cr = ""
else
let b:vimfootnotenumber = 1
let b:vimfootnotemark = VimFootnoteType(b:vimfootnotenumber)
let cr = "\<cr>"
endif
"let b:pos = line('.').' | normal! '.virtcol('.').'|'.'4l'
exe "normal a[".b:vimfootnotemark."]\<esc>"
let splitposition = &splitbelow;
set splitbelow
:5 split
let &splitbelow; = splitposition
normal G
if search("^-- $", "bW")
exe "normal O".cr."[".b:vimfootnotemark."] "
else
exe "normal o".cr."[".b:vimfootnotemark."] "
endif
startinsert!
endfunction
Anonymous, September 25, 2002 19:28
Instead of hard-coding numbers like 97, suggest doing something like char2nr( 'a' ) -- hard-coded numbers frighten me.
hermitte at free.fr, September 26, 2002 0:36
I do also vote for the window-splitting approach. I think it is one of the best and cleanest choice we can have.

Otherwise, some minor other improvments can be done like "pluginizing" the script :

It would start with something like :
" ----
if exists("g:loaded_footnote_vim") | finish | endif
let g:loaded_footnote_vim = 1

let s:first_footnote = exists('g:first_footnote') : g:first_footnote ? 1
" Because I don't like to start the footnotes with [0]

if !hasmapto('<Plug>AddVimFootnote', 'i')
  imap <C-X>f <Plug>AddVimFootnote
endif
if !hasmapto('<Plug>AddVimFootnote', 'n')
  nmap <leader>af <Plug>AddVimFootnote
endif
nnoremap <Plug>AddVimFootnote :call <sid>VimFootnotes('a')<cr>
inoremap <Plug>AddVimFootnote <c-o>:call <sid>VimFootnotes('i')<cr>
"Note: be sure there is *NO* space after the '<cr>' when you copy-paste.

" The previous paragraph enables anyone to remap the functions calls
" to anything else that the developper's default bindings. To do so, add into
" your .vimrc something like :
"  nmap ,f <Plug>AddVimFootNote


function! s:VimFootnotes(appendcmd)
  ....
:below 3sp
  " note that you don't need change the value of 'splitbelow'
  exe "normal ".a:appendcmd."[".b:vimfootnotenumber."]\<esc>G"
...
  
Anon, September 26, 2002 6:01
You could technically parse the line just above the place where the next footnote is going to be placed:  something like substitute( getline( '.' - 1 ), '^\[\(\w\+\)\]', '\1', '' ) should give you the footnote number/letter.  Then, either increment it as a number or do a char2nr on it (depending on the footnote style) for the processing.  That way, you don't have to worry about not being able to handle decrementing the footnote value upon an undo operation.
[email protected], October 30, 2002 2:23
there is now a script improving on this tip.
you probably want it instead: https://www.vim8.org/script.php?script_id=431

emmanuel
If you have questions or remarks about this site, visit the vimonline development pages. Please use this site responsibly.
Questions about Vim should go to [email protected] after searching the archive. Help Bram help Uganda.
SourceForge Logo