vim: need to workaround a bug in undofile() - vim

The persistent undo feature in vim uses an internal function undofile() to determine the filename of the undofile for the current buffer. There is a bug in vim 8.0 on one of my machines where undofile() does not correctly escape the path separator.
:echo undofile(#%)
/home/bhawkins/.vim/undo/%home%bhawkins%some%path%to%code.c
Those bare % characters don't work on this machine (not sure why not). The correct result of this command should be:
:echo undofile(#%)
/home/bhawkins/.vim/undo/\%home\%bhawkins\%some\%path\%to\%code.c
So it fails to load the undo history, even though it saves the history correctly. For example, when I manually load the undo file, persistent undo magically starts working (just for that one buffer):
:rundo /home/bhawkins/.vim/undo/\%home\%bhawkins\%some\%path\%to\%code.c
I tried setting this up as an autocmd FileReadPost in my .vimrc by putting the result of undofile() into a let variable--but I can't figure out how to escape the % characters.

You can try this:
1 redir => oldundo
2 echo undofile(#%)
3 redir end
4 let oldundo=oldundo[1:]
5 let newundo=substitute(oldundo,"%","\\\\%","g")
The first three lines capture the undo path in oldundo variable.
Line 4 removes the first character (^#) from oldundo. The last line replaces % with \% and stores the result in newundo. Finally to use the new path you can type:
:rundo <c-r>=newundo<cr>
Here <c-r> means Ctrl + r. And <cr> represents the Enter key.

It turns out to be very tricky to reference a variable in an autocmd:
autocmd BufReadPost * :let undofilename=substitute(undofile(#%), "%", "\\\\%", "g")
autocmd BufReadPost * :silent execute "rundo ".g:undofilename
In my opinion this is yet another reason that vim should use a well-organized language like python for its command line and scripts, instead of torturing people with this campy homegrown mess known as "vimscript".

Related

Is it possible to have Ctrl-K cut/uncut functionality in Vim like there is in Nano and Pico?

In case you're not familiar with how it works, in Pico and Nano you can hit ctrl-k multiple times and it will add each line to the clipboard. Then you can ctrl-u to "uncut" this. It's a very useful command. Vim does something similar with the dd command, but it only works one line at a time. Thus, you have to use visual mode to properly accomplish the above.
I couldn't find a good answer online so I rolled my own solution. You can add this to your vimrc file:
imap <C-k> <Esc>:execute #a ? 'normal! "Bdd' : 'normal! "bdd'<cr>:let #a=1<cr>:echo ''<cr>i
imap <C-u> <Esc>"bPi
autocmd CursorMovedI * execute(':let #a=0')
The register #a is used to track whether or not the cut line should be appended. The register #b is used as the clipboard register. Whenever the cursor position changes, you stop being in "append" mode. Thus, you can hit ctrl-k over and over to keep appending lines, but as soon as you move the cursor you go back to normal. I'm pretty sure this is how Nano and Pico implement it under the hood.
Is anyone aware of a cleaner solution?
Intro to Registers
The Vim commands you are looking for are delete/cut, dd and put/paste, p. Each of these commands by default use the unnamed register, "". So dd will delete a line and put the freshly deleted line into the unnamed register. p will put the contents from the unnamed register into your current buffer.
Vim has more than just the unnamed register, it also has named registers. These registers are a-z.
To see what your registers are set to you can execute :registers.
To use a named register prefix your command with quote and a lowercase letter, e.g. "add
Upper case letters will append instead of replace the contents of a register, e.g "Add"
The Vim Way
"add the first line, then append the next line via "Add. For repeated deletions use .
Use a text object. e.g. dap for delete a paragraph. See :h text-objects
Delete text via some motion. e.g. d} is delete till end of paragraph
Delete to a regex patter. e.g. d/foo<cr>
Lastly would be using visual mode, V6jd. Nothing wrong with using visual mode
Additionally you want to stay out of insert mode unless you are inserting text. You only want to be in insert move for short burst at a time. Most of the time you should be in Normal mode, hence the name.
For a nice post on the Vi/Vim way see this StackOverflow post: Your problem with Vim is that you don't grok vi.
Alternatives
If none of the standard Vim tricks satisfy your needs you may want to look into plugins that support line exchanging like Unimpaired or LineJuggler.
However if you really do want something similar this nano/pico features you can use the following by putting it in your ~/.vimrc file:
nnoremap Q :<c-u>call <SID>DeleteAppend()<cr>
function! s:DeleteAppend()
let save = #a
let #a = ##
let reg = get(g:, 'append_tick', -1) == b:changedtick ? 'A' : 'a'
execute 'normal! "' . reg . 'dd'
let g:append_tick = b:changedtick
let ## = #a
let #a = save
endfunction
The Q normal command will now delete a line and append additional deleted lines until a another command is executed. Example usage: QQjjQQQp
For more help please see
:h d
:h p
:h "
:h registers
:h :reg
:h motion
:h text-objects
:h .
:h visual-mode
Yes, there are many cleaner solutions:
12dd{motion}p " cut 12 lines, move elsewhere, paste
d5j{motion}p " cut from here to 5 lines down, move elsewhere, paste
d/foo<CR>{motion}p " cut from here to next 'foo', move elsewhere, paste
:.,23d<CR>:12put<CR> " cut from here to line 23, paste after line 12
:.,+5d<CR>:0put<CR> " cut from here to fifth line below, paste at top of buffer
or the truly amazing:
:.,11m35<CR> " move the lines between this one and 11 to after line 35
You can even do:
Vjjjjjjjjjjd{motion}p
One of the big advantages of Vim over other editors is its expressive language: don't think in "nano", think in "Vim".

VIM 7: How to navigate the code source tree from the root of the code base, in an efficient manner?

I have a system of navigating the code across multiple files in a huge code base, and I want to improve/fix a drawback that it currently has :
My shell is pre-configured to open at the root of my code base - lets call it Dev/.
During syncing/code building, I have a script which automatically stores the relative path of all .h and .c files in a single file to be used by cscope (lets call it cscope.files).
Once I sync, this file is updated - and then I can open any file I want in vim using the following command from Dev/ :
vif "part of file name",
where
vif: aliased to vi `grep !:1 cscope.files`
Provided I give a part of the filename long enough to uniquely identify it, I can immediately open it in vim.
Now, the drawback to this approach is, when I've already opened one file, and jump to another file without exiting vim, the only way I can do so is
:!vif *file2*
This spawns a new shell and then opens the file in a vim launched there. As a result, I can't switch between the two files (using Ctrl-^). I'm unable to come up with a solution that :
a) Lets me open any file from Dev/ instantly
b) Lets me open any other file inside vim (once I've opened an existing file) in the same shell, so that the 2 vim sessions are aware of each other (I can hop between the 2 using Ctrl-^)
I know this is a long question (how does one google this :) ), but I'm betting the solution is simple and obvious to someone more proficient in vim !!
Let me know if any part of the question is fuzzy, and I'll clarify it...
UPDATE:
I ultimately went the cscope way, after customizing using a shortcut (as using 'gf' on cscope.files still prevented me from toggling between 2 source files). See VIM 7 and cscope: Using "cscope find f" inside a keyboard mapping for switching between files for the shortcut.
Use vim's grep in something like this:
:map <F1> :vim <pattern> cscope.files<CR>gf
For example, with this:
vnoremap <F1> "ry:exe ':1vim /'.#r.'/ cscope.files'<CR>gf
you select (visual mode) the pattern you'd like to search for and then press F1. The first file that matches the pattern will open, replacing the current buffer*.
* If this is possible. i.e if current buffer is saved or if hidden is set etc.
If you prefer to get a prompt, use input():
nnoremap <F1> :exe ':1vim /'.input("Enter pattern: ").'/ cscope.files'<CR>
[ but then you have to manually gf because input() consumes the remaining characters of the map. To avoid this, you can use inputsave() and inputrestore() ]
update
... for example like this:
function! GetPat()
call inputsave()
let mypat = input("Enter pattern: ")
call inputrestore()
return mypat
endfunction
nnoremap <F1> :exe ':1vim /'.GetPat().'/ cscope.files'<CR>gf
I think that the Vim Fuzzy Finder plugin is well adapted to your use case.
As the name implies, using the plugin, you can find files using a fuzzy text search.
Additionnaly, it also works for other Vim ressources like buffers, tags, etc.
I don't use cscope myself, but it seems that you can use it to find files, see :help cscope-find.
Otherwise, something like (not tested) this could help:
"Custom function
function! MyFunc(pat)
" Get files list
let filelist = readfile('path/to/cscope.files')
" Filter non matching item out and see if only one item is left
if len(filter(filelist, 'v:var =~? '.a:pat)) == 1
" edit file
exec 'edit '.filelist[0]
else
" Report back
echom 'More than one match:'
for file in filelist
echom file
endfor
endif
endfunction
" Custom command
command! -bar -nargs=1 MyCom call MyFunc(<args>)
Also try using the built-in cscope integration:
:cs find f stdio.h
Cscope tag: stdio.h
# line filename / context / line
1 1 /usr/include/stdio.h <<<unknown>>>
2 1 /usr/include/bits/stdio.h <<<unknown>>>
Type number and <Enter> (empty cancels):
See :help cscope-suggestions for some mappings that may make it easier to use cscope from within vim.

Vim no end of line on last line or eof

I am trying to setup vim to skip adding eol on last line or eof, i have tried this
:set binary
:set noeol
:w
which is not perfect cause binary override filetype for later use.
Any other option to set this, i don't need newline on last line.
I wanted to add an answer that I think can be as useful. The selected answer always remove the EOL on files even if they had one to begin with. This may be the behavior that you want, it also may not be. In my opinion I want to preserve the EOL as I originally opened the file.
What I suggest is a slight modification. Put set binary at the top of your .vimrc file. This way any files will open in binary mode. If they have no EOL then vim will detect this and leave it NOEOL. If they have an EOL then it'll recognize it as having an EOL and leave it be.
If you would like new files to also not have EOL then you should set,
au BufNewFile * set noeol
The commands for the chap would be if you always want NOEOL ever. The one thing to be aware of is that if you have a file with spaces at the bottom, then you will lose spaces at the end. This is because the following happens,
VIM reads the file and see a newline at the end and thinks, OK, this is a file with a newline (regardless of whether in binary mode or not).
When you then write a file it executes the autocmd,
set binary mode
set noeol (this deletes the EOL that VIM thought was ending the file and deletes it.
the file gets saved
Now you have a file that has one less EOL in it.
This process will repeat until the EOL's are gone and the file ends in a single character. With my setup, what happens is the file is opened and if VIM sees at least one EOL then it keeps it internally. If you save that file, no matter what it is there (you can check by typing set eol?. In the case that you want to get rid of that internally stored EOL you just say set noeol and then save and BOOM, the last EOL is removed.
WHOOO, I am winded typing this.
This is even better i found somewhere:
au BufWritePre * :set binary | set noeol
au BufWritePost * :set nobinary | set eol
From vim documentation:
'binary' 'bin' boolean (default off)
local to buffer
{not in Vi}
This option should be set **before** editing a binary file. You can also
You should therefore use vim -b or :e ++bin file, or reload using :e! ++bin.
I have came up with this:
" php remove eol from end of file
autocmd FileType php setlocal noeol binary fileformats="mac,unix,dos"
Thank you all.
Cheers.

Reading the result of an exe file in :vs in vim

Many of my programs are console like applications, which take a data file, and print out the results, either on screen (more often) or in another file.
Since these days I'm doing some analysis which requires a lot of little tempting with one input data file, just to get a few numbers, I usually go: edit data file, start program, get result, edit data file, start program, get result ...
So I was wondering, is there a way in vim, while having open the input file, to define a function which would open a vertical split and load the result of program (pro12.exe) in it ?
How would one go about that ?
The easiest thing to do is probably write a Makefile to run the program (and redirect output to a file), and :set the autoread option on in Vim. Then, you can create a macro r by typing qr:make<Enter>q and run the macro with #r. The macro will cause Vim to invoke make, which will run the program to update the data file. The autoread option will make sure that vim refreshes the updated file without prompting you first.
Assuming pro12.exe is in your %PATH%, this will invoke your helper app and vert-split-open a static output file name. Map to whatever key you like. QND: won't work when relative paths (bufnames) change via cd. YMMV
fun! RunPro12()
bufdo if bufname(bufnr($)) == '/path/to/pro12.exe.output' | close | endif
silent exe '!pro12.exe'
vs /path/to/pro12.exe.output
endfun
map <f3> :call RunPro12()<cr>
I don't like :bufdo solutions as this command always messes up the current windows organisation.
In normal time, I'd use lh#buffer#jump() that jumps to the window where the named buffer is opened if it is already opened, or opens the window otherwise.
function! s:Run0()
call lh#buffer#jump('/path/to/pro12.exe.output', 'vsp')
silent exe '!ls'
endfunction
Given the requested "to define a function which would open a vertical split", we could simply use:
function! s:Run1()
let bn = bufnr('/path/to/pro12.exe.output')
if bn >= 0
exe 'bw '.bn
endif
silent exe '!pro12'
vsp /path/to/pro12.exe.output
endfunction
nnoremap ยต :call <sid>Run1()<cr>
Last thing. If as I suspect pro12 writes to stdout (the standard output, i.e. ~ the console), the last two lines of the function shall become:
vsp /path/to/pro12.exe.output
r!pro12
The "/path/to/pro12.exe.output" may be replaced with anything unique. Typically, I'd use a scratch buffer with a unique name having "pro12 result" in it.
PS: if in the function (this is important) you add a
nnoremap <buffer> q :bw<cr>
you'll be able to simply quit the output window simply by hitting q, from within the output window.

tail like functionality for gvim

I want to use gvim to view a log file which is being updated continuously, such that I always see the last updated line, much like tail command in unix. Is it possible?
Open logfile and
:setlocal autoread
There is a plugin (Tail Bundle) on the vim site.
I like it short and without a lot of hacking or external scripts.
You can run this oneliner from ex (whithin vim) when needed (or put each command in vimrc, for when log-files are opened.)
:set autoread | au CursorHold * checktime | call feedkeys("lh")
and additionally you can :set syntax=logtalk to color the log
(if you would want to jump (nearly) to the end of the file, just use "G" instead of "lh" with feedkeys)
Explanation:
autoread: reads the file when changed from the outside (but it doesnt work on its own, there is no internal timer or something like that. It will only read the file when vim does an action, like a command in ex :!
CursorHold * checktime: when the cursor isn't moved by the user for the time specified in updatetime (which is 4000 miliseconds by default) checktime is executed, which checks for changes from outside the file
call feedkeys("lh"): the cursor is moved once, right and back left. and then nothing happens (... which means, that CursorHold is triggered, which means we have a loop)
To stop the scrolling when using call feedkeys("G"), execute :set noautoread - now vim will tell, that the file was change ans ask if one wants to read the changes or not)
*from this answer (refering to an answer by PhanHaiQuang and a comment by flukus)
Or a less elegant solution on a vim 7.x would be just do :e! whenever you need the update .

Resources