Sunday, October 12, 2014

Save state of the folds: mkview

I have tried in the past to use fold, but found them to be cumbersome and unwieldy. Most problematic of all, was that the VIM doesn't save state of the fold: reopening the file with folds presents you with an useless screen showing you how many top level fold there are in the file. Real useless.

But as it turned out, VIM has a feature to preserve state of folds between sessions: views.

After some time googling, I have stumbled on this tip. The .vimrc's code is fairly trivial (when you know what to look for):


autocmd BufWinLeave *.* mkview
autocmd BufWinEnter *.* silent loadview 

Every time one closes a file,VIM would save under ~/.vim/view/ (default, see 'viewdir') the view information for the file. Every time one opens a file, VIM would try to restore the view information. And the state of the fold with it.

Friday, February 28, 2014

VIM got fork: Neovim

Heise.de reports (German) that VIM has got a fork. The name of the new fork is Neovim. The site at the moment has only two things: link to donation site and link to Github repo.

I do not approve of redundant forks. But IMHO VIM source code is in dire need of clean up.

Bram doesn't approve. Discussion on the vim_dev group.

In the end, we'll see in couple of years whether the people behind Neovim have what it takes.

Saturday, February 15, 2014

A trick for keywordprg

Scouting web for what's possible with git aliases, I have found a cool trick on how to combine/chain calls to several tools when the program allows to configure only one. The trick turned out to be not new, but more of the old, forgotten ones: define one-shot shell function.

Example. How to make perl's keyword program to look into both functions and module helps.

Old one, looking only for function's help:

au BufReadPost *.pl set keywordprg=perldoc\ -f

The trick in action:

au BufReadPost *.pl set keywordprg=f(){\ perldoc\ -f\ $*\|\|perldoc\ $*;};f

Deescaped command looks like that: f(){ perldoc -f $* || perldoc $*; }; f

VIM would append the word to search for at the end and run it. Shell would see pretty normal function definition and immediately after a call to it.

The trick obviously works only on the platforms which have Bourne shell.

Friday, November 15, 2013

Highlight the locking primitives in the C/C++ code

A quick hack to highlight the locking calls in a C/C++ program:

:syn match cTodo '\<\i*[lL]ock\i*\>'

Reuses colors of the "TODO" and "XXX" items.

Edit1 I like this one better:


function! LockSyntaxFix()
        :syn match WarningMsg '\c\i*\(un\)\@<!\(lock\)\i*'
        :syn match MoreMsg '\c\i*unlock\i*'
endfunction

Red (WarningMsg) for lock, green (MoreMsg) for unlock. Trick is in the regexp which allows to match "lock" in all cases except "unlock". Packed into a function to be called when debugging locking.

Thursday, January 24, 2013

[off-topic] split/filter C++ class declarations from source into header file

Occasionally, I end up writing a simple one-file prototype for the whatever future C++ thingy I'm doing at the moment. Occasionally, the prototype ends up being pretty big and un-prototyle-like a really working program.

For many years now, the most painful part, if prototype was successful, was to make out of it something looking more like a real source code, suitable for integration with the rest of the project.

Namely: move class declarations to a header file. This often ends up being very very mundane task, especially if the prototype was to test some elaborate data model.

Literally ages into the C++ and ages into the *NIX, it downed on me that sed has a trick for it. If source is properly formatted and the main problem is the move of class declarations, then sed's /begin_re/,/end_re/ should work fine. And it does indeed.

$ sed '/^\(struct\|class\)/,/^}/p; d' < prototype.cc > realthing.h
$ sed '/^\(struct\|class\)/,/^}/d;' < prototype.cc > realthing.cc


Basically, sed is told to take from the input the blocks surrounded by struct or class and closing curly bracket (both in first column). For the header we 'p'rint the blocks and 'd'elete the rest, while for the source file we do the opposite (print is the default action of sed, thus no 'p' in the second command). The test with diff should show no differences (I don't like losing lines in the un-checked-in source so I always double check):

$ diff -wu <(sort < prototype.cc) \
     <(cat realthing.h realthing.cc | sort) | less


P.S. That of course does nothing to typedefs and forward declarations, but those are peanuts compared to the declaration of classes. And anyway, I try to put both typedefs and forward declarations into a struct or class just to give them common namespace prefix.

Thursday, January 17, 2013

Few tricks for the GVIM diff mode

Couple of useful tricks for the GVIM diff mode (~/.gvimrc):

- Equalize the diff file window sizes when resizing the GVIM window. That is probably applicable to console vim too, but I generally diff with the GUI vim. (The "Trailing characters" error from :exec occasionally drives me up the wall. But I have finally found out how to properly trigger keyboard shortcut!)

- Toggle font size very very small/very small and small on F5 (font names below are for *nix).

The Doom1-styled toggles I wanted to try already for a long time. In VIM the need for toggles isn't that high since the boolean parameters can be already toggled (:set hls! or :set wrap! for example). I found no other (readable) way to implement them but with functions - one function per state of toggle. A function which is part part of the toggle group does two things: first it sets parameter(s) it needs to set, then it map the toggle shortcut to the next function in the toggle group. To "initialize" the toggle group, simply call one of the functions.


if &diff
        " equalize size of diffed file windows
        au VimResized * :execute "normal! \<C-W>="

        " toggle diff font
        function! DiffSmallFont1()
                set guifont=-misc-fixed-medium-r-normal--9-90-75-75-c-60-iso10646-1
                map :call DiffSmallFont2()<CR>
        endfunction
        function! DiffSmallFont2()
                set guifont=-misc-fixed-medium-r-normal--10-100-75-75-c-60-iso10646-1
                map <F5> :call DiffSmallFont3()<CR>
        endfunction
        function! DiffSmallFont3()
                set guifont=-misc-fixed-medium-r-normal--13-120-75-75-c-70-iso10646-1
                map <F5> :call DiffSmallFont1()<CR>
        endfunction
        call DiffSmallFont2()
endif




Monday, December 03, 2012

[off-topic] Perl or PCRE: sort strings with numbers

A little trick with regular expressions (if backtracking is supported) on how to compare two strings which might include number.

The trick is to join the strings with NUL character (never occurring in human readable strings anyway) and use it as an anchor to find the longest common sub-string, in both strings followed by a number. And then compare the numbers.

#!/usr/bin/env perl
use strictuse warnings;

sub cmp_str_with_numbers
{
        #my ($a, $b) = @_;
        warn $a."<=>".$b;
        my $s = $a."\x00".$b;
        if ($s =~ m/^(.*)(\d+).*?\x00\1(\d+)/) {
                if ($2 != $3) {
                        return $2 <=> $3;
                }
        }
        return $a cmp $b;
}

my @test1 = (
        'Test 2 ccc',
        'Test 1 aaa 1',
        'Test 1 aaa 10',
        'Test 1 aaa 2',
        'Test 10 bbb',
);

my @out0 = sort @test1;
my @out1 = sort cmp_str_with_numbers @test1;
print "original:\n";
print "\t$_\n" for @test1;
print "normal sort:\n";
print "\t$_\n" for @out0;
print "number-aware sort:\n";
print "\t$_\n" for @out1;

Output:

original:
        Test 2 ccc
        Test 1 aaa 1
        Test 1 aaa 10
        Test 1 aaa 2
        Test 10 bbb
normal sort:
        Test 1 aaa 1
        Test 1 aaa 10
        Test 1 aaa 2
        Test 10 bbb
        Test 2 ccc
number-aware sort:
        Test 1 aaa 1
        Test 1 aaa 2
        Test 1 aaa 10
        Test 2 ccc
        Test 10 bbb

Saturday, December 01, 2012

Regex to match the word under cursor

The VIM-specific regex below matches the word under cursor. (Pasting unmodified as it is in my vimrc to also match German letters.)

/[a-zA-Z0-9ßÄÜÖäüö]*\%#[a-zA-Z0-9ßÄÜÖäüö]*

Documentation is under ':h /\%#'

Example usage: enclose the word under cursor in 'em' tag. Best experience if that is triggered on a keyboard shortcut.

:s![a-zA-Z0-9ßÄÜÖäüö]*\%#[a-zA-Z0-9ßÄÜÖäüö]*!<em>\0</em>!

Negative side-effect: causes fancy behavior of a seemingly random word to be highlighted when 'set hls' is in effect.

Search for a misspelled word

Alternative 1:

/The\S\+les\(Themistokles\)\@<!

Alternative 2:

/\(Themistokles\)\@!\(\<The\S\+les\>\)

Both search for any word which starts with 'The' and ends with 'les', but is not 'Themistokles'.

[link] Wrap a visual selection in an HTML tag

Wrap a visual selection in an HTML tag.

Pretty useful function. I have only slightly modified it to take the tag as parameter and insert the tag on the line before/after selection. And hooked it on a keyboard shortcut.

[ The '^M' below should be converted there into real ^M (typed as ^V^M). ]

" Wrap visual selection in an HTML tag.
vmap <C-q> <Esc>:call VisualHTMLTagWrap('cite')<CR>
vmap <C-T> <Esc>:call VisualHTMLTagWrap('title')<CR>
function! VisualHTMLTagWrap(tag)
 normal `>
 if &selection == 'exclusive'
  exe "normal i^M</".a:tag.">"
 else
  exe "normal a^M</".a:tag.">"
 endif
 normal `<
 exe "normal i<".a:tag.">^M"
 normal `>
 normal j
endfunction

Folding something semi-automatically, on demand

Simple functions to fold in a file blocks which have beginning and ending markers.

Since custom folding functions can cause VIM's performance to degrade, the trick is: after applying the folding, disable it immediately back. For that work I found that I have to call 'redraw' before disabling the 'foldmethod=expr'.

The snippet below folds all lines enclosed between '<binary' and '</binary>'.

function! FoldWhateverFunc(mstart,mend,ln)
 let t = getline(a:ln)
 if t =~ a:mstart
  return '>1'
 elseif t =~ a:mend
  return '<1'
 endif
 return '='
endfunction

function! FoldWhatever()
 set foldexpr=FoldWhateverFunc('<binary','</binary>',v:lnum)
 set foldmethod=expr
 redraw
 set foldmethod=manual
endfunction

Hint: one can replace the hardcoded 'binary' tag with call to the 'input()' function. Though I prefer non-interactive approach, something I can plug into the ':au'.

Edit1 BTW ':h fold-expr' contains several useful one-line examples of folding expressions.

Thursday, November 01, 2012

Search case in/sensitve, 'ignorecase' regardless

I found it always bit clumsy that when I want to search case sensitive/case insensitive, I had to flip the 'ignorecase' option.

As it turned out, there is much much simple way: an 'ignorecase' override, right in the search pattern itself. Here it is.

Case insensitive search (as if 'noignorecase'):

/\CPORT

Case sensitive search (as if 'ignorecase'):
/\cPORT
First would find only "PORT" while the second would find also "port" and "Port". Easy peasy.

See more at :h /\c and :h /character-classes.


P.S. Blogspot seems to be too devastated by the storm Sandy bunch of monkeys Google's Web design team. (Now YouTube too.) A major redesign, but again without single improvement to the substance of the blogging platform.

Monday, March 26, 2012

Mapping: disable the search history modification

A nice trick from :h histdel() to remove the last entry from search history:

:call histdel("search", -1)
:let @/ = histget("search", -1)


That can be used the following way (a mapping to insert HTML's paragraph break (</p>\n<p>) before the word under cursor):

map <silent> <F3> :s!^\(\s*\)\(.\{}\)\s\+\(\S*\%#\S*\)!\1\2</p>\r\1<p>\3!<CR>:call histdel("search", -1)<CR>:let @/ = histget("search", -1)<CR>


With the histdel() andreset of @/, the search pattern replaced by :s is reset back to what it was before and thus the n/N keys work as expected. (You will not believe it: I have spent many years in VIM without knowing about the N shortcut. Discovering it by an accident was like enlightenment to me.)

OK, the mapping gets obsessively long, but works as expected. One can't have it all.

Sunday, January 29, 2012

[off-topic] Perl on Windows: handling files with Unicode characters

Using Strawberry Perl.

The rename with the proper Win32 module initialization:


# init
use strict;
use warnings;
use utf8;
use Win32::OLE qw(in);
Win32::OLE->Option( CP => Win32::OLE::CP_UTF8 );

# the code
my $fso = Win32::OLE->new("Scripting.FileSystemObject");
$fso->MoveFile( ".\\".$old_name , ".\\".$new_name );


Recursively scan directory tree:


# the code
sub scan1
{
my ($f, $l) = @_;
my $obj = Win32::OLE->new('Scripting.FileSystemObject');

my $folder = $obj->GetFolder( $f );
die "ERROR: $f" unless $folder;
foreach my $file (in $folder->Files) {
print $file->{Name}, ", size ", $file->{Size}, "bytes\n";
}

my $collection = $folder->{SubFolders};
foreach my $value (in $collection) {
my $foldername = $value->{Name};
scan1( "$f\\$foldername", $l );
}
}
my @l;
scan1( 'c:\\Movies', \@l );


Scripting.FileSystemObject documentation.

Tuesday, January 17, 2012

A use case for \%#

Some time ago, per chance, quite obscure feature of VIM's regular expressions: \%# - :h /\%# says "Matches with the cursor position."

At first I couldn't see any use for it.

But then, while editing per hand some XMLs, I stumbled upon a problem: how to insert a tag break (close and open), while adding the indentation? Normal VIM macrii(*) can insert - but one looses the cursor position as soon as movement commands are used. But to make a copy of the indentation, one needs to move the cursor to the beginning of the line.

Edit0. OK. It just dawned on me. One could first insert the XML tag break + new line. Then copy indentation. Uh. Need to sleep more and more often. But I already wrote the post so what the heck I'll just post it.

Then I recalled that the regular expressions could match current cursor position. And I had a hunch that the \%# could be used for the purpose. The Enlightenment come only few days later and took shape of that for the <p> tag:

:s!^\(\s*\)\(.\{}\)\s\+\(\k*\%#\k*\)!\1\2</p>\r\1<p>\3!

First () submatch are the spaces (= indentation) of the current line.

Second () submatch is everything up to the word under cursor (matched non-greedily as I had some fancy problems with greedy match here).

Third () submatch is the actual word under cursor, anchored by the \%# to the current cursor position. (The redundant spaces before the word are trimmed between the second and third submatches.)

That all is replaced by. Original line: \1\2 is the line up to the word under cursor, closing </p> tag and \r for new line. New line: \1 which is the wanted indentation, opening <p> tag and finally the word (obviously followed by the rest of the original line).

P.S. \S (anything but space) in place of \k works pretty well too.

P.P.S. VIM should support some sort of nesting of regular expressions. During editing lots of pieces could be reused - but only reuse regular expressions do support is the copy-paste. The Clue.


(*) Because for the particular task at hand, macros have infested irreversibly my vimrc. Just like virii. Any press of a wrong button, and only the undo can sort out what the hell has just happened.

Sunday, January 08, 2012

[link] LOCALE settings and regexp classes

Spent some time editing a German text (actually an FB2 book) in VIM. Hell of a job, because as it turned out, I wasn't blind: VIM really doesn't support locale in the regular expressions.

Workaround is to use \k (also suggested \i doesn't match ß). But that's only half workaround, since \k is case insensitive and case is used in German (e.g. nouns are capitalized).

Another workaround is to use Perl or Python integration for regular expressions, since both provide locale support in the regular expressions. But I haven't gone that far yet.

Edit1. Do NOT use Perl for the purpose. Locale/utf8 support is totally and utterly messed up.

P.S. Here are some of my FB2 editing helper functions.



" some Fiction Book functions

"
" merge adjacent paragraphs
"
function! ParaMany_ToSingle() range
        let lines = getline(a:firstlinea:lastline)
        call filter( lines, 'v:val !~ "^$"' )
        let xind = substitute( lines[0], '^\(\s*\).*''\1''' )
        for i in range( 0len(lines)-1 )
                if i != 0
                        let lines[i] = substitute( lines[i], '^\s*<p>[ \t]*''''' )
"                       echo i." < ".lines[i] | sleep 2
                endif
                if i != len(lines)-1
                        let lines[i] = substitute( lines[i], '[ \t]*</p>\s*$''''' )
"                       echo i." > ".lines[i] | sleep 2
                endif
        endfor
"       echo "xxx:".join( lines, " " ) | sleep 2
        if a:lastline > a:firstline
                exec ':'.(a:firstline+1).",".a:lastline."d"
        endif
        let text = join( lines, " " )
        let text = substitute( text, '[ \t]\{2,}'' ''g' )
        let text = substitute( text, '^\s\+''''' )
        call setline( a:firstline, xind.text )
endfunction

" gvim helper keyboard shortcuts (c-up/-down do not work in terminal)
function! Keyboard_ParaCUpDown()
        map <C-Up> :-1,.call ParaMany_ToSingle()<CR>
        map <C-Down> :.,+1call ParaMany_ToSingle()<CR><Up>
endfunction

"
"  insert section break, using the line's text as the section title
"
function! Para_ToSectionBreak()
        let t = getline(".")

        " capture the line indentation
        let xind = substitute( t, '^\(\s*\).*''\1''' )
        " etch a bit from section tag indentation
        let xindS = substitute( xind, '^\(\s*\)\s$''\1''' )

        " clean-up tags
        let t = substitute( t, '\v\</{0,1}[a-z]+[^>]*\>''''g' )
        let t = substitute( t, '[ \t]\+$''''' )
        let t = substitute( t, '^[ \t]\+''''' )

        " generate id, ensure starts with letter or _
        let id = substitute( t, '[^a-zA-Z0-9_-]''_''g' )
        let id = substitute( id, '^\([^a-zA-Z_]\)''_\1''g' )

        let l = [xindS.'</section>',
\               '',
\               xindS.'<section>',
\               xind.'<title>',
\               xind.'<p id="'.id.'">'.t.'</p>',
\               xind.'</title>' ]
        call setline( ".", l[0)
        call  append( ".", l[1:] )

        call cursor( line(".")+len(l)0 )
endfunction

"
"  convert line's text to subtitle
"
function! Para_ToSubtitle()
        let t = getline(".")
        let xi = substitute( t, '^\(\s*\).*''\1''' )
        let t = substitute( t, '\v\</{0,1}[a-z]+[^>]*\>''''g' )
        let t = substitute( t, '[ \t]\+$''''' )
        let t = substitute( t, '^[ \t]\+''''' )
        call setline( ".", xi.'<subtitle>'.t.'</subtitle>' )
endfunction

Friday, November 25, 2011

[link] More Fun with Vimscript

More Fun with Vimscript by Juliet Kemp.

Mentions something totally new to me: ZOMG! VIM has a function debugger!!

:h debug-scripts.

Example :debug call CustomeFunc().

P.S. Wow, and profiler too! :h profiling

Sunday, February 20, 2011

[link] Getting more out of Vim - some tips

Some VIM tips from FSM (Free Software Magazine).

The neat trick to count words - :s/pattern//gn - somehow I have missed it before.

^O/^I (in normal mode, :h jump-motions) are cool too. Work like Back/Forward buttons on the web browsers.

Thursday, January 20, 2011

modeline on steroids

Modeline is a great tool. Yet, sometimes it is also quite limiting: it can change only limited number of options. My most often gripe - one can't change the 'makeprg' to add the target (for what in past I attempted to use the workaround).

Reading through the modeline documentation, I have found an idea how to improve the modeline to allow to execute random VIM commands, not just setting few options.

Here it goes.


function! CustomModeLine(cid)
        let i = &modelines
        let lln = line("$")
        if i > lln | let i = lln | endif
        while i>0
                let l = getline(lln-i+1)
                if l =~ a:cid
                        exec strpart(l, stridx(l, a:cid)+strlen(a:cid))
                endif
                let i = i-1
        endwhile
endfunction

au BufReadPost * :call CustomModeLine("VIM666:")


Now, after adding that to the .vimrc, one can put something like this in the last lines (among &modelines last of them) of the source file:

// VIM666:set makeprg=make\ aaaa
// VIM666:sy keyword cType block_id_t

When opening the file in VIM, the scriptlet would find the lines and :execute them. First would add to the makeprg a default target "aaaa", second would make symbol "block_id_t" to be highlighted as a C/C++ type (typedef from my pet not-really-a-project).

Why the VIM666:?

Well. VIM creator has allowed the modelines to change only limited set of options for the reason:

No other commands than "set" are supported, for security reasons (somebody
might create a Trojan horse text file with modelines). And not all options
can be set. For some options a flag is set, so that when it's used the
|sandbox| is effective.


Thus, randomizing the "VIM666" into something more random and unpredictable is strongly advised. Sadly, :sandbox isn't configurable and thus incapable of disallowing only certain actions - at the moment it is all-or-nothing type of command in VIM.

Tuesday, January 11, 2011

[DUMP] VIM custom folding for Rational Purify's plog files

Dump.


"
" Fold (collapse) the Purify problem descriptions into single line
" As the comment to the fold, Purify message would appear with
" list of files/libraries in the call stack.
"-CHAIN_LENGTH=32" parameter in PURIFYOPTIONS is highly recommended
"
" custom fold function for Purify's .plog files
function! PuriFold(lnum)
        let l = getline(a:lnum)
        " fold starts at 'XXX:' marker (though exclude File In Use)
        if l =~ '^[A-Z][A-Z][A-Z]:' && l !~ '^FIU:'
                return 1
        endif

        " store fold level of previous line
        let pfl = foldlevel(a:lnum-1) > 0

        " check whether it is a continuation of a fold
        if pfl && (=~ '^\t' || l =~ '^  \*' || l =~ '^    ')
                return 1
        endif

        if pfl
                return '<1'     " close fold if open
        else
                return 0        " not a fold otherwise
        endif
endfunction

" custom fold text function to quickly see the approx location of the problem
function! Puri_GatherNames(fl, ll)
        let l:i = a:fl
        let l:x = ''
        let l:count = 0
        while l:i <= a:ll && l:count < 10
                let l:l = getline( i )
                if l:l =~ '\*unknown func\*'
                        let l:l = ''
                endif
                let l:tmp = matchstr( l:l, '\[[^\]]\+\]$' )
                if strlen(l:tmp) == 0
                        let l:i = l:i+1
                        continue
                endif
                if l:tmp =~ '^\[libclntsh\.so'
                        let l:tmp = '[*ORA*]'
                endif
                if l:tmp =~ '^\[libstlport\.so' ||
\                  l:tmp =~ '^\[_\(pair\|tree\|alloc\|map\|construct\)\.h:' ||
\                  l:tmp =~ '^\[_\(tree\)\.c:'
                        let l:tmp = '[*STL*]'
                endif
                if strlen(l:tmp)>0 && l:tmp!=strpart(l:x,strlen(l:x)-strlen(l:tmp))
                        if l:tmp == '[crt1.o]' || l:tmp == '[rtlib.o]' ||
\                          l:tmp == '[crti.o]' || l:tmp == '[libCrun.a]' ||
\                          l:tmp == '[libc.so.1]' || l:tmp == '[libnsl.so.1]' ||
\                          l:tmp == '[libsocket.so.1]'
                                " nothing, ignore system libraries
                        else
                                let l:x = l:x.' '.l:tmp
                                let l:count = l:count+1
                        endif
                endif
                "echo x
                let l:i = l:i+1
        endwhile
        return l:x
endfunction
function! Puri_FoldText()
        return foldtext().Puri_GatherNames(v:foldstart, v:foldend)
endfunction

" activate the folds when .plog file is opened
au BufReadPost *.plog set foldexpr=PuriFold(v:lnum)
au BufReadPost *.plog set foldmethod=expr
" use custom fold text function
au BufReadPost *.plog set foldtext=Puri_FoldText()



P.S. WT Rational Purify?