let g:loaded_matchparen=1
is a workaround, but no solution.
I already compiled Vim with '-O3' to no avail.
Any hints? Alternatives to Vim (except *Emacs)? It should have
syntax highlighting (at least for Scheme) and paren matching.
Thank you!
>Any hints? Alternatives to Vim (except *Emacs)? It should have
>syntax highlighting (at least for Scheme) and paren matching.
Hehe, I use nvi(1), if you like that. :-) No syntax highlighting that
I know of, but it is plenty fast when I edit my Scheme code. ;-)
NEdit is also one that I use on occassion, and I have Scheme modes set
up for it as well.
--
Aaron W. Hsu <arc...@sacrideo.us> | <http://www.sacrideo.us>
"Government is the great fiction, through which everybody endeavors to
live at the expense of everybody else." -- Frederic Bastiat
+++++++++++++++ ((lambda (x) (x x)) (lambda (x) (x x))) ++++++++++++++
Is this simple Highlighting Script faster?
fu! MatchPairs()
if mode() !~# 'i\|n'
return
endif
if exists("w:matchpairs")
3match none
let w:matchpairs=0
endif
let l:oldpos = getpos('.')
let l:c_line = line('.')
let l:c_col = col('.')
let l:char = getline('.')[l:c_col-1]
if match(split(&matchpairs, '[:,]'),'\V'.l:char) != -1 && len(l:char) > 0
exe 'keepjumps normal %'
let l:n_line = line('.')
let l:n_col = col('.')
exe '3match MatchParen /\(\%' . l:c_line . 'l\%' . l:c_col . 'c\)\|\(\%' . l:n_line . 'l\%' . l:n_col . 'c\)/'
let w:matchpairs=1
exe 'keepjumps normal %'
call setpos('.',l:oldpos)
endif
endfu
fu! EnableAutoCommand(on)
if a:on
augroup MatchPair
au!
autocmd CursorMoved * call MatchPairs()
augroup END
else
au! MatchPair
endif
endfu
command! NoMatchPair 3match none | call EnableAutoCommand(0)
command! DoMatchPair call EnableAutoCommand(1)
regards,
Christian
--
:wq!
I haven't seen this. And note that I've done paren matching on some huge files
containing Lisp data. For instance, some years ago I was debugging a balanced
binary tree module (written in C, irrelevant). I had to understand something
about the structure, so I had the program dump it as a Lisp s-expression. The
test case in which the problem reproduced had tens of thousands of nodes, so I
worked with that. I used Vim paren matching (%) to navigate around the
subtrees. It wasn't instantaneous, but usable.
Do you have a sample file that you can put somewhere which reproduces the
problem?
We're thinking of different functions, I guess. Finding a matching paren on
demand via '%' works fine for me. The problem is highlighting the matching
paren whenever the cursor is over the other one. This is a very useful
function, since one always sees at first glance which expression is closed
by the paren currently under the cursor.
> Do you have a sample file that you can put somewhere which reproduces the
> problem?
Try this:
(((((((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))))
If you have 'syntax=scheme' (or 'syntax=lisp'), then movement will be rather
jerky, especially in the rightmost region.
Interestingly, this seems to be a problem specific to Scheme mode. The
default mode does an (apparently) similar paren matching, and it is much,
much faster.
It should be possible to exchange the respective part of the Scheme mode
implementation, shouldn't it? But I'm not sure whether I can do it.
[...]
It is much, much faster on simple examples, like:
(((((((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))))
However, it appears to be only slightly faster on real-world code. I wasn't
able yet to construct a minimal example that shows it. Kind of mystifying.
>> Is this simple Highlighting Script faster?
>>
>> fu! MatchPairs()
[...]
> However, it appears to be only slightly faster on real-world code. I wasn't
> able yet to construct a minimal example that shows it. Kind of mystifying.
This seems to be a problematic pattern:
(
(
(
(
))))
You have to use about, say, 80 parens in total. The amount of whitespace is
essential. On the following, it's faster, sometimes at least:
(
(
(
(
))))
Oh, and it's only so slow when I use your highlighting script on top of
Scheme mode (with 'let g:loaded_matchparen=1'). When I use your script alone,
it's no problem.
Sorry, I am unable to reproduce your example. Both highlighting
scripts work for me fast enough, if I copy your examples into a file
and :set ft=scheme. Please give us a step by step example.
You might want to consider to update your runtime files (using
something like rsync -avzcP --delete --exclude="/dos/"
--exclude=".svn" ftp.nluug.nl::Vim/runtime/) as well as update your
Vim installation to the most recent 7.2 version.
If the problem still persists, I would take this to the vim_dev
mailinglist or send a bug report (:h bug-report) to Bram.
regards,
Christian
--
:wq!
Maybe I had not made that clear enough: the examples just show the pattern,
but are far too small to show the problem. I attach a complete example below.
How I can reproduce it:
1. Start Vim on a file containing the large example at the end of this
article.
2. :set ft=scheme
3. :sy on (that's important, I just figured out)
4. Move the cursor left and right on the 'tail' of closing parens at the end
of the example. Movement should be rather jerky.
> You might want to consider to update your runtime files (using
> something like rsync -avzcP --delete --exclude="/dos/"
> --exclude=".svn" ftp.nluug.nl::Vim/runtime/) as well as update your
> Vim installation to the most recent 7.2 version.
I did so (a few patches could not be applied, though) and the problem
persits.
Example file:
(
(
(
(
(
(
(
(
(
(
(
(
(
(
(
(
(
(
(
(
(
(
(
(
(
(
(
(
(
(
(
(
(
(
(
(
(
(
(
(
))))))))))))))))))))))))))))))))))))))))
> 1. Start Vim on a file containing the large example at the end of this
> article.
>
> 2. :set ft=scheme
>
> 3. :sy on (that's important, I just figured out)
>
> 4. Move the cursor left and right on the 'tail' of closing parens at the end
> of the example. Movement should be rather jerky.
Hm... I'm not seeing the problem. I'm using Vim installed by MacPorts.
At any rate, it sounds (just a guess) like the problem is with
scheme-mode. To figure out exactly where the time is going, :profile
might be useful.
I never did profiling with Vim, so I'm not sure whether I did it right. I
follow the instructions above, place the cursor on the closing parens, then I
do ':profile start p1', and then ':profile func *'. I move the cursor left
and right several times over the closing parens, then exit.
File 'p1' then reads:
FUNCTION <SNR>4_Highlight_Matching_Pair()
Called 298 times
Total time: 34.030438
Self time: 34.030438
count total (s) self (s)
" Remove any previous match.
298 0.010095 if exists('w:paren_hl_on') && w:paren_hl_on
229 0.002631 3match none
229 0.017111 let w:paren_hl_on = 0
229 0.001419 endif
" Avoid that we remove the popup menu.
" Return when there are no colors (looks like the cursor jumps).
298 0.017200 if pumvisible() || (&t_Co < 8 && !has("gui_running"))
return
endif
" Get the character under the cursor and check if it's in 'matchpairs'.
298 0.004015 let c_lnum = line('.')
298 0.003358 let c_col = col('.')
298 0.002343 let before = 0
298 0.006504 let c = getline(c_lnum)[c_col - 1]
298 0.023950 let plist = split(&matchpairs, '.\zs[:,]')
298 0.005520 let i = index(plist, c)
298 0.001817 if i < 0
" not found, in Insert mode try character before the cursor
63 0.000903 if c_col > 1 && (mode() == 'i' || mode() == 'R')
let before = 1
let c = getline(c_lnum)[c_col - 2]
let i = index(plist, c)
endif
63 0.000351 if i < 0
" not found, nothing to do
63 0.000339 return
endif
endif
" Figure out the arguments for searchpairpos().
235 0.001496 if i % 2 == 0
let s_flags = 'nW'
let c2 = plist[i + 1]
else
235 0.014996 let s_flags = 'nbW'
235 0.001948 let c2 = c
235 0.003299 let c = plist[i - 1]
235 0.001028 endif
235 0.001597 if c == '['
let c = '\['
let c2 = '\]'
endif
" Find the match. When it was just before the cursor move it there for a
" moment.
235 0.001451 if before > 0
let save_cursor = winsaveview()
call cursor(c_lnum, c_col - before)
endif
" When not in a string or comment ignore matches inside them.
235 0.006068 let s_skip ='synIDattr(synID(line("."), col("."), 0), "name") ' . '=~? "string\\|character\\|singlequote\\|comment"'
235 0.788161 execute 'if' s_skip '| let s_skip = 0 | endif'
" Limit the search to lines visible in the window.
235 0.023624 let stoplinebottom = line('w$')
235 0.003236 let stoplinetop = line('w0')
235 0.001650 if i % 2 == 0
let stopline = stoplinebottom
else
235 0.036905 let stopline = stoplinetop
235 0.000983 endif
235 0.001476 try
" Limit the search time to 300 msec to avoid a hang on very long lines.
" This fails when a timeout is not supported.
235 32.771449 let [m_lnum, m_col] = searchpairpos(c, '', c2, s_flags, s_skip, stopline, 300)
235 0.018232 catch /E118/
" Can't use the timeout, restrict the stopline a bit more to avoid taking
" a long time on closed folds and long lines.
" The "viewable" variables give a range in which we can scroll while
" keeping the cursor at the same position.
" adjustedScrolloff accounts for very large numbers of scrolloff.
let adjustedScrolloff = min([&scrolloff, (line('w$') - line('w0')) / 2])
let bottom_viewable = min([line('$'), c_lnum + &lines - adjustedScrolloff - 2])
let top_viewable = max([1, c_lnum-&lines+adjustedScrolloff + 2])
" one of these stoplines will be adjusted below, but the current values are
" minimal boundaries within the current window
if i % 2 == 0
if has("byte_offset") && has("syntax_items") && &smc > 0
let stopbyte = min([line2byte("$"), line2byte(".") + col(".") + &smc * 2])
let stopline = min([bottom_viewable, byte2line(stopbyte)])
else
let stopline = min([bottom_viewable, c_lnum + 100])
endif
let stoplinebottom = stopline
else
if has("byte_offset") && has("syntax_items") && &smc > 0
let stopbyte = max([1, line2byte(".") + col(".") - &smc * 2])
let stopline = max([top_viewable, byte2line(stopbyte)])
else
let stopline = max([top_viewable, c_lnum - 100])
endif
let stoplinetop = stopline
endif
let [m_lnum, m_col] = searchpairpos(c, '', c2, s_flags, s_skip, stopline)
endtry
235 0.001727 if before > 0
call winrestview(save_cursor)
endif
" If a match is found setup match highlighting.
235 0.003489 if m_lnum > 0 && m_lnum >= stoplinetop && m_lnum <= stoplinebottom
228 0.034055 exe '3match MatchParen /\(\%' . c_lnum . 'l\%' . (c_col - before) . 'c\)\|\(\%' . m_lnum . 'l\%' . m_col . 'c\)/'
228 0.002615 let w:paren_hl_on = 1
228 0.000938 endif
FUNCTIONS SORTED ON TOTAL TIME
count total (s) self (s) function
298 34.030438 <SNR>4_Highlight_Matching_Pair()
FUNCTIONS SORTED ON SELF TIME
count total (s) self (s) function
298 34.030438 <SNR>4_Highlight_Matching_Pair()
> I follow the instructions above, place the cursor on the closing parens, then
> I do ':profile start p1', and then ':profile func *'. I move the cursor left
> and right several times over the closing parens, then exit.
>
> File 'p1' then reads:
>
> FUNCTION <SNR>4_Highlight_Matching_Pair()
> Called 298 times
> Total time: 34.030438
> Self time: 34.030438
298 times? Just how deeply nested are your parentheses? Anyway, 298 /
34 ~= 0.11s per match. I was able to get a similar number by nesting
100 parentheses 100 levels deep, then trying to match the outermost parens.
> 235 32.771449 let [m_lnum, m_col] = searchpairpos(c, '', c2, s_flags, s_skip, stopline, 300)
There's the bottleneck. That function is built into vim, so I'm not
sure there's much configuration-level optimization you can do. To make
that faster might require a patch to Vim's C source code.
The reason you say you want this is to highlight matching parentheses,
which is fine. However, if there are 100s of levels of parentheses
between the one you're on and its match, can you see the matching paren,
anyway? If not, then some maximum-depth option may be in order.
> 235 32.771449 let [m_lnum, m_col] = searchpairpos(c, '', c2, s_flags, s_skip, stopline, 300)
Wow, that looks bad. I would create a bug report or take this to the
vim-dev mailing list.
regards,
Christian
--
:wq!
One obvious lack of optimization in this function (do_searchpair in the eval.c
source file) is the fact that it relies on repeated regex searches. It
basically searches forward (or backwards, depending on the flags) for the
patterns, and uses a simple counter to determine nesting. The individual
searches are done using a function called searchit, over three different
patterns.
The searchit function calls regcomp to compile the regular expression. This is
wasteful, since compiled regular expressions could be re-used for repeated
searches.
Secondly, you don't need a full-blown regex for just finding parentheses. A
simplified non-regex version of searchit could be used for this case. No regex
engine can beat a simple search for a one-character match (unless perhaps it
compiles to native machine code, in which an instruction that compares the
character as an immediate operand stored in the opcode is able to shave off a
cycle).
I recommend that a second implementation of searchit be written which does
trivial one-character searching. Early in the searchpair_cmn function, some
cheap tests done on the patterns can determine that use of the simplified
version of searchit is appropriate. A function pointer can be set up to point
to one function or the other, so that the core loop remains essentially the
same.
Parens matching via the % command is fast, because this is implemented by a
special case function which doesn't use regex, named findmatchlimit. This is
used by a few places inside Vim, but doesn't appear to be exposed through
any intrinsic function to the scripting language (I'm looking at the 7.0
source). It doesn't use regex, just simple searching. It also does the
special-case behavior for matching #if.../#el.../#end... C preprocessing
directives, but this behavior is suppressed if the parenthesis type to look for
is passed as an argument. The function is smart enough to know about which
direction to search for when it's obvious from the parenthesis curvature. :)
Maybe exposing this function to scripts would be a better idea than trying to
fix searchpairpos.