matchup.vim

⚠️ warning ⚠️ this plugin is unfinished and under heavy active development. It is not ready for use yet!

match-up is a replacement for the venerable vim plugin matchit.vim match-up aims to replicate all of matchit's features, fix a number of its deficiencies and bugs, and add a few totally new features. It also replaces the standard plugin matchparen, allowing all of matchit's words to be highlighted along with the matchpairs ((){}[]).

and in this corner...

A major goal of this project is to keep a modern and modular code base. Contributions are welcome!

Table of contents

Overview

This plugin

  • Extends vim's % motion to language-words like if, else, endif.
  • Adds motions g%, [%, ]%, and z%.
  • Combines these motions into convenient text objects i% and a%.
  • Highlights symbols and words under the cursor which % can work on, and highlights matching symbols and words. Now you can easily tell where % will jump to.

Planned features:

  • Add auto-completion for words and symbols- for example you could automatically insert corresponding a ) or endif.

Installation

If you use vim-plug, then add the following line to your vimrc file:

Plug 'andymass/matchup.vim'

Or use some other plugin manager:

  • vundle
  • neobundle
  • pathogen

Features

feature match-up matchit matchparen
(a.1) jump between matching words 👍 👍
(a.2) jump to open & close words 👍
(a.3) jump inside 👍
(b.1) full set of text objects 👍
(c.1) highlight (), [], & {} 👍 👍
(c.2) highlight all matches 👍
(c.3) display matches off-screen 👍
(d.1) parallel transmutation 🚧
(e.1) modern, modular coding style 👍
(e.2) actively developed 👍

Legend: 👍 supported. 🚧 TODO, planned, or in progress. poorly implemented, broken, or uncertain. not possible.

Detailed feature documentation

What do we mean by open, close, mid? Here is a vim-script example:

if l:x == 1
  call one()
else
  call two()
elseif
  call three()
endif

match-up understands the words if, else, elseif, endif and that they form a sequential construct in the vim-script language. The "open" word is if, the "close" word is endif, and the "mid" words are else and elseif. The if/endif pair is called an "open-to-close" block and the if/else, else/elsif, and elseif/endif are called "any" blocks.

(a.1) jump between matching words

  • % go forwards to next matching word. If at a close word, cycle back to the corresponding open word.
  • {count}% forwards {count} times. Requires let g:matchup_override_Npercent = 1. By default, {count}% goes to the {count} percentage in the file.
  • g% go backwards to [count]th previous matching word. If at an open word, cycle around to the corresponding close word.

(a.2) jump to open and close words

  • [% go to [count]th previous unmatched open word. Allows navigation to the start of surrounding blocks. This is similar to vim's built-in [( and [{ and is an exclusive motion.
  • ]% go to [count]th next unmatched close word. This is an exclusive motion.

(a.3) jump inside

  • z% go to inside [count]th nearest inner contained block. This is an inclusive motion.

(b.1) full set of text objects

  • i% the inside of an any block

  • 1i% the inside of an open-to-close block

  • {count}i% If count is greater than 1, the inside of the {count}th surrounding open-to-close block

  • a% an any block.

  • 1a% an open-to-close block. Includes mids but does not include open and close words.

  • {count}a% if {count} is greater than 1, the {count}th surrounding open-to-close block.

Note: by default objects involving matchpairs such as (){}[] are performed character-wise, while matchwords such as if/endif are performed line-wise. The -wise can be forced using "v", "V", or ^V Let g:matchup_all_charwise. XXX inclusive, exclusive XXX need () characterwise, others linewise except QUIRKS.

(c.1) highlight (), [], and {}

match-up emulates vim's matchparen to highlight the symbols contained in the matchpairs setting.

(c.2) highlight all matches

To disable match highlighting let g:matchup_matchparen_enabled = 0. If this option is set before the plugin is loaded, it will not disable the matchparen plugin (Planned). To disable highlighting entirely do not load matchparen.

(c.3) display matches off screen

If a open or close which would have been highlighted is on a line positioned outside the current window, the match is shown in the status line. If both the open and close match are off-screen, the close match is preferred.

(d.1) parallel transmutation

In insert mode, after changing text inside a word, matching words will be changed in parallel. As an example,

<pre>
  text
</pre>

Changing pre to div and leaving insert mode will produce:

<div>
  text
</div>

Note: this currently only works for match words which define a backref relation like \1. A wider set of transmutations are planned.

Planned: g:matchup_auto_transmute, CTRL-G % mapping. A corresponding normal mode command is also planned.

Inclusive and exclusive motions

In vim, character motions following operators (such as d for delete and c for change) are either inclusive or exclusive. This means they either include the ending position or not. match-up is designed so that d]% inside a set of parenthesis behaves exactly like d]). For other words, exclusive motions will not include the close word. In this example, where is the cursor position,

if| continue | endif

pressing d]% will produce

if endif

To include the close word, use either dv]% or vd]%. This is vim compatible with d]) and d]}.

Unlike ]%, % is an inclusive motion. As a special case for the d (delete) operator, if d% leaves behind lines white-space, they will be deleted also. In effect, it will be operating line-wise. As an example, pressing d% will leave behind nothing.

   █(

   )

To operate character-wise in this situation, use dv% or vd%. This is vim compatible with the built-in d% on matchpairs.

Line-wise operator/text-object combinations

Normally, the text objects i% and a% work character-wise. However, there are some special cases. For certain operators combined with i% (by default di% and yi%), under certain conditions, match-up will operate line-wise instead. For example, in

if conditioncall one()
  call two()
endif

pressing di% will produce

if condition
endif

even though deleting condition would be suggested by the object i%. The intention is to make operators more useful in some cases. The following rules apply:

  • The operator must be listed in g:matchup_text_obj_linewise_operators
  • The outer block must span multiple lines.
  • The open and close delimiters must be more than one character long. In particular, di% involving a (...) block will not be subject to these special rules.

To prevent this behavior for a particular sequence dvi% or vdi%.

To disable this entirely, remove the operator from the following variable,

let g:matchup_text_obj_linewise_operators = [ 'y' ]

Note: unlike vim's built-in i), ab, etc., i% does not make an existing visual mode character-wise.

A second special case involves da%. In this example,

    if conditioncall one()
      call two()
    endif

pressing da% will delete all four lines and leave no white-space. This is vim compatible with da(, dab, etc.

Options

To disable the plugin entirely,

let g:matchup_enabled = 0

default: 1

To disable a particular module,

let g:matchup_matchparen_enabled = 0
let g:matchup_motion_enabled = 0
let g:matchup_text_obj_enabled = 0

defaults: 1

To enable the experimental transmute module,

let g:matchup_transmute_enabled = 1

default: 0

Variables

matchup understands the following variables from matchit.

  • b:match_words
  • b:match_skip
  • b:match_ignorecase

These are set in the respective ftplugin files. They may not exist for every file type. To support a new file type, create a file after/ftplugin/{filetype}.vim which sets them appropriately.

Module matchparen

The matchparen module can be disabled on a per-buffer basis

let b:matchup_matchparen_enabled = 0

default: 1

If this module is disabled on a particular buffer, match-up will still fall-back to the vim standard plugin matchit, which will highlight matchpairs such as (), [], & {}. To disable this,

let b:matchup_matchparen_fallback = 0

default: 1

A common usage is to automatically disable matchparen for particular file types;

augroup matchup_matchparen_disable_ft
  autocmd!
  autocmd FileType tex let [b:matchup_matchparen_fallback,
      \ b:matchup_matchparen_enabled] = [0, 0]
augroup END

Whether to highlight known words even if there is no match:

let g:matchup_matchparen_singleton = 1

default: 0

Whether to replace the statusline for off-screen matches:

let g:matchup_matchparen_status_offscreen = 0

default: 1

Adjust timeouts for matchparen highlighting

let g:matchup_matchparen_timeout = 300
let g:matchparen_insert_timeout = 60

default: 300, 60

Module motion

To allow {count}%,

g:matchup_motion_override_Npercent = 1

default: 0

If enabled, cursor will land on the end of mid and close words while moving downwards (%/]%). While moving upwards (g%, [%) the cursor will land on the beginning. To disable,

let g:matchup_motion_cursor_end = 0

default: 1

Module text_obj

Modify the set of operators which may operate line-wise

let g:matchup_text_obj_linewise_operators' = ['d', 'y']

default: ['d', 'y']

Module transmute

Options planned.

FAQ

  • match-up doesn't work

    The plugin requires a fairly recent version of vim. Please tell me your vim version and error messages. Try updating vim and see if the problem persists.

  • Why does jumping not work for construct X in language Y?

    Please open a new issue

  • Highlighting is not correct for construct X

    match-up uses matchit's filetype-specific data, which may not give enough information to create proper highlights. To fix this, you may need to add a highlight quirk.

    For help, please open a new issue and be a specific as possible.

  • I'm having performance problems

    match-up aims to be as fast as possible. If you see any performance issues, please open a new issue and report g:matchup#perf#times.

  • How can I contribute?

    Read the contribution guidelines and issue template. Be as precise and detailed as possible when submitting issues and pull requests.

Interoperability

  • match-up's match highlighting is not compatible with vimtex's implementation. match-up highlighting and will be disabled automatically when vimtex is detected.
  • matchit.vim should not be loaded. If it is loaded, it must be loaded before match-up (in this case, matchit will be disabled when match-up loads).
  • match-up loads matchparen if it is not already loaded.

Acknowledgments

Origins

match-up was originally based on @lervag's vimtex. The concept and style of this plugin and its development are heavily influenced by vimtex. 🍻

Other inspirations

license

Totally new features

  • parallel transformations (transmutation) (need to cache matches and see if they change)
    • polymorphic / smart -> if:end,while:end
  • native split/join
  • quirks
  • auto insert

Definitions

Matchword A matchword is an regular expression which defines interesting items to matchup matchup treats specially. For instance, by default ( and ) are paired matchwords. is on the matched to buffer text, becomes a matched word,

Matched word A matched word is an instance of buffer text which matches

Variables

loaded_matchit

Existing matchit features, made better:

% v_% between matches g% v_g% backwards between matches [% ]% to nearest unmatched o_a% o_i% delimited text object

Features in matchparen:

matchup emulates matchparen's highlighting for matchpairs Echo invisible pairs

Features not in matchit:

Auto-completion ctrl-x shift completes the nearest unmatched matchword. When n-tuple matchwords are used the last one is inserted.

Highlighting general matchup highlights matches for b:match_words

Jump into z[]% go to the center of the next group of matchwords

Development

Reporting problems

This is a brand new plugin and there are likely to be many bugs. Thorough issue reports are encouraged. Please read the issue template first. Be as precise and detailed as possible when submitting issues.

Feature requests are also welcome.

Contributing

Please read the contribution guidelines before contributing.

Planned feature wish-list

This is a set of features planned for "version 1" but require a bit more research before they can be properly tackled.

feature match-up matchit matchparen
(d.2) auto-insert open, close, & mid 🚧
(d.3) completion 🚧
(d.4) split & join 🚧

(d.2) auto-insert open, close, and mid

Planned.

  • end-wise style completion: typing CTRL-X <cr> will insert the corresponding end word (mapping subject to change).

  • automatic block insertion: typing CTRL-X CTRL-B to produce block skeletons (mapping subject to change).

(d.3) completion

Planned.

Typing CTRL-X O/CTRL-X W will give a menu of possible words (mapping subject to change).

(d.4) split and join

Planned.

Convert between single-line and multi-line blocks. Mappings undecided.

Todo list

  • write proper vim doc
  • thoroughly test with unicode, tabs
  • complete parallel transmutation in an efficient way.
  • add screenshots and animations
  • support for fenced code possible?
  • add file type quirks module
  • investigate whether &selection/&virtualedit options are important
  • can match-up be integrated with vim-surround?
Description
vim match-up: even better % 👊 navigate and highlight matching words 👊 modern matchit and matchparen. Supports both vim and neovim + tree-sitter.
Readme 1.2 MiB
Languages
Vim Script 69.1%
Scheme 10%
Lua 8.2%
C 7.6%
Makefile 1.1%
Other 3.9%