Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion autoload/matchit.vim
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
" matchit.vim: (global plugin) Extended "%" matching
" autload script of matchit plugin, see ../plugin/matchit.vim
" Last Change: Jan 06, 2025
" Last Change: Dec 23, 2025

" Neovim does not support scriptversion
if has("vimscript-4")
Expand Down Expand Up @@ -69,6 +69,26 @@ function matchit#Match_wrapper(word, forward, mode) range
let startpos = [line("."), col(".")]
endif

" Check for custom match function hook
if exists("b:match_function")
let MatchFunc = b:match_function
try
let result = call(MatchFunc, [a:forward])
if !empty(result)
call cursor(result)
return s:CleanUp(restore_options, a:mode, startpos)
endif
catch /.*/
if exists("b:match_debug")
echohl WarningMsg
echom 'matchit: b:match_function error: ' .. v:exception
echohl NONE
endif
return s:CleanUp(restore_options, a:mode, startpos)
endtry
" Empty result: fall through to regular matching
endif

" First step: if not already done, set the script variables
" s:do_BR flag for whether there are backrefs
" s:pat parsed version of b:match_words
Expand Down
41 changes: 40 additions & 1 deletion doc/matchit.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
*matchit.txt* Extended "%" matching Last change: 2025 Aug 07
*matchit.txt* Extended "%" matching Last change: 2025 Dec 23

VIM REFERENCE MANUAL by Benji Fisher et al

Expand Down Expand Up @@ -253,6 +253,45 @@ Examples:
See the $VIMRUNTIME/ftplugin/vim.vim for an example that uses both
syntax and a regular expression.

*b:match_function*
If b:match_function is defined, matchit.vim will first call this function to
perform matching. This is useful for languages with an indentation-based block
structure (such as Python) or other complex matching requirements that cannot
be expressed with regular expression patterns.

The function should accept one argument:
forward - 1 for forward search (% command)
0 for backward search (g% command)

The function should return a list with one of these values:
[line, col] - Match found at the specified position
[] - No match found; fall through to regular matching
(|b:match_words|, matchpairs, etc.)

The cursor position is not changed by the function; matchit handles cursor
movement based on the returned position.

If the function throws an error, matchit gives up and doesn't continue.
Enable |b:match_debug| to see error messages from custom match functions.

Python example (simplified): >
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry if this is somehow duplicated but I don't think it was sent with my previous comment as I expected.

This example doesn't work for g%. It needs separate patterns when working in that direction.

Perhaps it could be simplified to just work i the forward direction with a note, "backwards jumping is left as an exercise for the reader". Otherwise it should probably be improved so it can at least be dropped in as a working example.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, that's a good idea. I've done that in #55.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need #55 in Vim?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a good documentation improvement, but it doesn't change any functionality. Do you agree @dkearns?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think it would be a good idea to include it.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alright, will merge it then in both projects

let s:keywords = {'if': 'elif\|else', 'elif': 'elif\|else'}

function! s:PythonMatch(forward) abort
let keyword = matchstr(getline('.'), '^\s*\zs\w\+')
let pattern = get(s:keywords, keyword, '')
if empty(pattern) | return [] | endif

let flags = a:forward ? 'nW' : 'nbW'
let [lnum, col] = searchpos('^\s*\%(' . pattern . '\)\>', flags, 0, 0,
\ 'indent(".") != ' . indent('.'))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This skip pattern needs to test the indent at the jump point with that at the destination.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scrap that, sorry, I misread it .

return lnum > 0 ? [lnum, col] : []
endfunction

let b:match_function = function('s:PythonMatch')
<
See |matchit-newlang| below for more details on supporting new languages.

==============================================================================
4. Supporting a New Language *matchit-newlang*
*b:match_words*
Expand Down