├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── README.md ├── autoload ├── mark.vim └── mark │ ├── cascade.vim │ ├── early.vim │ └── palettes.vim ├── doc └── mark.txt ├── mark.manifest └── plugin └── mark.vim /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: The plugin isn't working at all or shows wrong or unexpected behavior 4 | title: '' 5 | labels: '' 6 | 7 | --- 8 | **Frequent Issues** 9 | 10 | * **E117: Unknown function: ingo#...**: Have you installed the [ingo-library plugin](http://www.vim.org/scripts/script.php?script_id=4433) (or via [GitHub](https://github.com/inkarkat/vim-ingo-library)) as well, as documented in the _dependencies_ section of the readme and plugin help? 11 | * **Sudden problems after updating**: Did you check out the default _master_ branch? Unless you want to participate in feature development and alpha testing, I would recommend that you switch from _master_ to the _stable_ branch; this way, you'll only update to released versions that are (hopefully) better tested and documented. 12 | * **Neovim**: I don't explicitly consider nor test Neovim compatibility; my plugins are written for Vim. If a plugin can be made to work on Neovim with trivial changes, I wouldn't mind including them, but anything more involved should in my opinion be filed as a compatibility bug against Neovim (as its homepage proclaims: _Fully compatible with Vim's editing model and the Vimscript language._) 13 | 14 | **Describe the bug** 15 | 16 | _A clear and concise description of what the bug is._ 17 | 18 | **How to Reproduce** 19 | 20 | _Detailed steps to reproduce the behavior._ 21 | 22 | **Expected Behavior** 23 | 24 | _A clear and concise description of what you expected to happen._ 25 | 26 | **Environment** 27 | - Plugin version (e.g. stable version _1.10_) / revision _a1b2c3d4_ from the master branch 28 | - Dependency versions (e.g. [ingo-library plugin](https://github.com/inkarkat/vim-ingo-library), or external tool versions) 29 | - Vim version (e.g. _8.1.1234_) Or paste the result of `vim --version`. 30 | - OS: (e.g. _Ubuntu 18.04_, _Windows 10 1809_, _macOS 10.14_) 31 | - Install method: (e.g. manually via Vimball or ZIP file, GitHub clone as pack plugin, Plugin manager _NAME_) 32 | - Other plugins / additional context if you think this could be important 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an enhancement for the plugin 4 | title: '' 5 | labels: enhancement 6 | 7 | --- 8 | 9 | **Motivation** 10 | 11 | _A clear and concise description of what currently is hard to do._ 12 | 13 | **Request and Purpose** 14 | 15 | _A clear and concise description of what you want to happen. Possible fit criteria._ 16 | 17 | **Alternatives** 18 | 19 | _A clear and concise description of any alternative solutions or features you've considered._ 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.msgout 2 | *.msgresult 3 | *.out 4 | *.tap 5 | doc/*.description 6 | doc/*.install 7 | tags 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MARK 2 | =============================================================================== 3 | _by Ingo Karkat_ 4 | _(original version by Yuheng Xie)_ 5 | 6 | DESCRIPTION 7 | ------------------------------------------------------------------------------ 8 | 9 | This plugin adds mappings and a :Mark command to highlight several words in 10 | different colors simultaneously, similar to the built-in 'hlsearch' 11 | highlighting of search results and the \* star command. For example, when you 12 | are browsing a big program file, you could highlight multiple identifiers in 13 | parallel. This will make it easier to trace the source code. 14 | 15 | This is a continuation of [vimscript #1238](http://www.vim.org/scripts/script.php?script_id=1238) by Yuheng Xie, who doesn't maintain 16 | his original version anymore and recommends switching to this fork. This 17 | plugin offers the following advantages over the original: 18 | - Much faster, all colored words can now be highlighted, no more clashes with 19 | syntax highlighting (due to use of matchadd()). 20 | - Many bug fixes. 21 | - Jumps behave like the built-in search, including wrap and error messages. 22 | - Like the built-in commands, jumps take an optional [count] to quickly skip 23 | over some marks. 24 | - Marks can be persisted, and patterns can be added / subtracted from 25 | mark highlight groups. 26 | 27 | ### SEE ALSO 28 | 29 | - SearchAlternatives.vim ([vimscript #4146](http://www.vim.org/scripts/script.php?script_id=4146)) provides mappings and commands to 30 | add and subtract alternative branches to the current search pattern. 31 | - SearchHighlighting.vim ([vimscript #4320](http://www.vim.org/scripts/script.php?script_id=4320)) can change the semantics of the 32 | start command \*, extends it to visual mode (like Mark) and has auto-search 33 | functionality which instantly highlights the word under the cursor when 34 | typing or moving around, like in many IDEs. 35 | - MarkMarkup.vim ([vimscript #5777](http://www.vim.org/scripts/script.php?script_id=5777)) extends mark.vim with rendering the 36 | highlightings as markup directly inside the text: as HTML <span> tags that 37 | reproduce the mark's colors, or as appended numbers or symbols and a legend 38 | to look up the mark names. Any markup-based export format can be defined. 39 | 40 | ### RELATED WORKS 41 | 42 | - MultipleSearch ([vimscript #479](http://www.vim.org/scripts/script.php?script_id=479)) can highlight in a single window and in all 43 | buffers, but still relies on the :syntax highlighting method, which is 44 | slower and less reliable. 45 | - http://vim.wikia.com/wiki/Highlight_multiple_words offers control over the 46 | color used by mapping the 1-9 keys on the numeric keypad, persistence, and 47 | highlights only a single window. 48 | - highlight.vim ([vimscript #1599](http://www.vim.org/scripts/script.php?script_id=1599)) highlights lines or patterns of interest in 49 | different colors, using mappings that start with CTRL-H and work on cword. 50 | - quickhl.vim ([vimscript #3692](http://www.vim.org/scripts/script.php?script_id=3692)) can also list the matches with colors and in 51 | addition offers on-the-fly highlighting of the current word (like many IDEs 52 | do). 53 | - Highlight (http://www.drchip.org/astronaut/vim/index.html#HIGHLIGHT) has 54 | commands and mappings for highlighting and searching, uses matchadd(), but 55 | limits the scope of highlightings to the current window. 56 | - TempKeyword ([vimscript #4636](http://www.vim.org/scripts/script.php?script_id=4636)) is a simple plugin that can matchadd() the 57 | word under the cursor with \\0 - \\9 mappings. (And clear with \\c0 etc.) 58 | - simple\_highlighting ([vimscript #4688](http://www.vim.org/scripts/script.php?script_id=4688)) has commands and mappings to highlight 59 | 8 different slots in all buffers. 60 | - searchmatch ([vimscript #4869](http://www.vim.org/scripts/script.php?script_id=4869)) has commands and mappings for :[1,2,3]match, 61 | in the current window only. 62 | - highlight-groups.vim ([vimscript #5612](http://www.vim.org/scripts/script.php?script_id=5612)) can do buffer-local as well as 63 | tab-scoped highlighting via :syntax, and has multiple groups whose 64 | highlighting is defined in an external CSV file. 65 | - Syntax match ([vimscript #5376](http://www.vim.org/scripts/script.php?script_id=5376)) provides various (color-based) shortcut 66 | commands for :syntax match, and saves and restores those definitions, for 67 | text and log files. 68 | - SelX ([vimscript #5875](http://www.vim.org/scripts/script.php?script_id=5875)) provides multiple multi-colored highlights per-tab 69 | (that can be stored in a session), mappings that mirror the built-in search 70 | commands, as a special feature automatically displays a Highlight Usage Map. 71 | - hi ([vimscript #5887](http://www.vim.org/scripts/script.php?script_id=5887)) highlights words, sentences or regular expressions 72 | using many configured colors, and can search; also offers separate windows 73 | for filtering and configuration editing catered towards log analysis. 74 | - vim-highlight-hero ([vimscript #5922](http://www.vim.org/scripts/script.php?script_id=5922)) can also highlight the current word or 75 | selection, has some flexibility with regard to whitespace matching, is 76 | limited to the current window. 77 | - high-str (https://github.com/Pocco81/high-str.nvim) is a Neovim-only plugin 78 | that can highlight the visual selection in multiple, configurable colors. 79 | 80 | USAGE 81 | ------------------------------------------------------------------------------ 82 | 83 | ### HIGHLIGHTING 84 | 85 | m Mark the word under the cursor, similar to the star 86 | command. The next free highlight group is used. 87 | If already on a mark: Clear the mark, like 88 | n. 89 | gm Variant of m that marks the word under the 90 | cursor, but doesn't put "\<" and "\>" around the word, 91 | similar to the gstar command. 92 | {Visual}m Mark or unmark the visual selection. 93 | {N}m With {N}, mark the word under the cursor with the 94 | {N}gm named highlight group {N}. When that group is not 95 | empty, the word is added as an alternative match, so 96 | you can highlight multiple words with the same color. 97 | When the word is already contained in the list of 98 | alternatives, it is removed. 99 | 100 | When {N} is greater than the number of defined mark 101 | groups, a summary of marks is printed. Active mark 102 | groups are prefixed with "*" (or "M*" when there are 103 | M pattern alternatives), the default next group with 104 | ">", the last used search with "/" (like :Marks 105 | does). Input the mark group, accept the default with 106 | , or abort with or any other key. 107 | This way, when unsure about which number represents 108 | which color, just use 99n and pick the color 109 | interactively! 110 | 111 | {Visual}[N]m Ditto, based on the visual selection. 112 | 113 | [N]r Manually input a regular expression to mark. 114 | {Visual}[N]r Ditto, based on the visual selection. 115 | 116 | In accordance with the built-in star command, 117 | all these mappings use 'ignorecase', but not 118 | 'smartcase'. 119 | 120 | n Clear the mark under the cursor. 121 | If not on a mark: Disable all marks, similar to 122 | :nohlsearch. 123 | Note: Marks that span multiple lines are not detected, 124 | so the use of n on such a mark will 125 | unintentionally disable all marks! Use 126 | {Visual}r or :Mark {pattern} to clear 127 | multi-line marks (or pass [N] if you happen to know 128 | the group number). 129 | {N}n Clear the marks represented by highlight group {N}. 130 | 131 | :{N}Mark Clear the marks represented by highlight group {N}. 132 | :[N]Mark[!] [/]{pattern}[/] 133 | Mark or unmark {pattern}. Unless [N] is given, the 134 | next free highlight group is used for marking. 135 | With [N], mark {pattern} with the named highlight 136 | group [N]. When that group is not empty, the word is 137 | added as an alternative match, so you can highlight 138 | multiple words with the same color, unless [!] is 139 | given; then, {pattern} overrides the existing mark. 140 | When the word is already contained in the list of 141 | alternatives, it is removed. 142 | For implementation reasons, {pattern} cannot use the 143 | 'smartcase' setting, only 'ignorecase'. 144 | Without [/], only literal whole words are matched. 145 | :search-args 146 | :Mark Disable all marks, similar to :nohlsearch. Marks 147 | will automatically re-enable when a mark is added or 148 | removed, or a search for marks is performed. 149 | 150 | :MarkClear Clear all marks. In contrast to disabling marks, the 151 | actual mark information is cleared, the next mark will 152 | use the first highlight group. This cannot be undone. 153 | 154 | :[N]Mark[!] /{pattern}/ as [name] 155 | Mark or unmark {pattern}, and give it [name]. 156 | :{N}MarkName [name] 157 | Give [name] to mark group {N}. 158 | :MarkName! Clear names for all mark groups. 159 | 160 | ### SEARCHING 161 | 162 | [count]* [count]# 163 | [count]* [count]# 164 | [count]/ [count]? 165 | Use these six keys to jump to the [count]'th next / 166 | previous occurrence of a mark. 167 | You could also use Vim's / and ? to search, since the 168 | mark patterns are (optionally, see configuration) 169 | added to the search history, too. 170 | 171 | Cursor over mark Cursor not over mark 172 | --------------------------------------------------------------------------- 173 | * Jump to the next occurrence of Jump to the next occurrence of 174 | current mark, and remember it "last mark". 175 | as "last mark". 176 | 177 | / Jump to the next occurrence of Same as left. 178 | ANY mark. 179 | 180 | * If * is the most recently Do Vim's original * command. 181 | used, do a *; otherwise 182 | (/ is the most recently 183 | used), do a /. 184 | 185 | Note: When the cursor is on a mark, the backwards 186 | search does not jump to the beginning of the current 187 | mark (like the built-in search), but to the previous 188 | mark. The entire mark text is treated as one entity. 189 | 190 | You can use Vim's jumplist to go back to previous 191 | mark matches and the position before a mark search. 192 | 193 | If you work with multiple highlight groups and assign special meaning to them 194 | (e.g. group 1 for notable functions, 2 for variables, 3 for includes), you can 195 | use the 1-9 keys on the numerical keypad to jump to occurrences of a 196 | particular highlight group. With the general * and # commands above, you'd 197 | first need to locate a nearby occurrence of the desired highlight group if 198 | it's not the last mark used. 199 | 200 | .. Jump to the [count]'th next occurrence of the mark 201 | belonging to highlight group 1..9. 202 | .. Jump to the [count]'th previous occurrence of the mark 203 | belonging to highlight group 1..9. 204 | Note that these commands only work in GVIM or if your 205 | terminal sends different key codes; sadly, most still 206 | don't. 207 | https://unix.stackexchange.com/questions/552297/make-gnome-terminal-send-correct-numeric-keypad-keycodes-to-vim 208 | The "Num Lock" indicator of your keyboard has 209 | to be ON; otherwise, the keypad is used for cursor 210 | movement. If the keypad doesn't work for you, you can 211 | still remap these mappings to alternatives; see below. 212 | Alternatively, you can set up mappings to search in a next / previous used 213 | group, see mark-group-cycle. 214 | 215 | [...] 216 | After a stop, retriggering the cascaded search in the same buffer and window 217 | moves to the next used group (you can jump inside the current buffer to choose 218 | a different starting point first). If you instead switch to another window or 219 | buffer, the current mark group continues to be searched (to allow you to 220 | keep searching for the current group in other locations, until those are all 221 | exhausted too). 222 | 223 | ### MARK PERSISTENCE 224 | 225 | The marks can be kept and restored across Vim sessions, using the viminfo 226 | file. For this to work, the "!" flag must be part of the 'viminfo' setting: 227 | set viminfo^=! " Save and restore global variables. 228 | Marks can also be stored in |session-file|s with this setting: 229 | set sessionoptions+=globals " Save and restore global variables. 230 | If you've configured the session-files but not viminfo, :MarkLoad and 231 | :MarkSave will default to a slot name that works with session files, i.e. 232 | the variable g:MARK_marks, instead of g:MARK_MARKS. Alternatively, if you've 233 | configured both but want to explicitly persist the marks into the session file 234 | and have it restored when that session is loaded, use 235 | :MarkSave marks 236 | and either set g:mwAutoLoadMarks (automatic restore on session load) or use 237 | :MarkLoad marks 238 | 239 | :MarkLoad Restore the marks from the previous Vim session. All 240 | current marks are discarded. 241 | :MarkLoad {slot} Restore the marks stored in the named {slot}. All 242 | current marks are discarded. 243 | 244 | :MarkSave Save the currently defined marks (or clear the 245 | persisted marks if no marks are currently defined) for 246 | use in a future Vim session. 247 | :MarkSave {slot} Save the currently defined marks in the named {slot}. 248 | If {slot} is all UPPERCASE, the marks are persisted 249 | and can be |:MarkLoad|ed in a future Vim session (to 250 | persist without closing Vim, use :wviminfo; an 251 | already running Vim session can import marks via 252 | :rviminfo followed by :MarkLoad). 253 | If {slot} contains at least one lowercase letter, the 254 | marks are persisted to any user sessions created by 255 | :mksession. 256 | If variable persistence isn't configured or the above 257 | conditions for {slot} aren't met (e.g. when using 258 | numbered slots), you can just recall within the 259 | current session. 260 | When no marks are currently defined, the {slot} is 261 | cleared. 262 | 263 | By default, automatic persistence is enabled (so you don't need to explicitly 264 | :MarkSave), but you have to explicitly load the persisted marks in a new Vim 265 | session via :MarkLoad, to avoid that you accidentally drag along outdated 266 | highlightings from Vim session to session, and be surprised by the arbitrary 267 | highlight groups and occasional appearance of forgotten marks. If you want to 268 | automatically restore any marks, set g:mwAutoLoadMarks. 269 | 270 | You can also initialize some marks (even using particular highlight groups) to 271 | static values, e.g. by including this in vimrc: 272 | runtime plugin/mark.vim 273 | silent MarkClear 274 | silent 5Mark foo 275 | silent 6Mark /bar/ 276 | Or you can define custom commands that preset certain marks: 277 | command -bar MyMarks exe '5Mark! foo' | exe '6Mark! /bar/' 278 | Or a command that adds to the existing marks and then toggles them: 279 | command -bar ToggleFooBarMarks exe 'Mark foo' | exe 'Mark /bar/' 280 | The following commands help with setting these up: 281 | 282 | :MarkYankDefinitions [x] 283 | Place definitions for all current marks into the 284 | default register / [x], like this: 285 | 1Mark! /\/ 286 | 2Mark! /bar/ 287 | 9Mark! /quux/ 288 | :MarkYankDefinitionsOneLiner [x] 289 | Like :MarkYankDefinitions, but place all definitions 290 | into a single line, like this: 291 | exe '1Mark! /\/' | exe '2Mark! /bar/' | exe '9Mark! /quux/' 292 | Alternatively, the mark#GetDefinitionCommands(isOneLiner) function can be used 293 | to obtain a List of :Mark commands instead of using a register. With that, 294 | you could for example build a custom alternative to :MarkSave that stores 295 | Marks in separate files (using writefile(), read by :source or even 296 | automatically via a local vimrc plugin) instead of the viminfo file. 297 | 298 | ### MARK INFORMATION 299 | 300 | Both mark-highlighting and mark-searching commands print information about 301 | the mark and search pattern, e.g. 302 | mark-1/\ 303 | This is especially useful when you want to add or subtract patterns to a mark 304 | highlight group via [N]. 305 | 306 | :Marks List all mark highlight groups and the search patterns 307 | defined for them. 308 | The group that will be used for the next :Mark or 309 | m command (without {N}) is shown with a ">". 310 | The last mark used for a search (via *) is 311 | shown with a "/". 312 | 313 | ### MARK HIGHLIGHTING PALETTES 314 | 315 | The plugin comes with three predefined palettes: original, extended, and 316 | maximum. You can dynamically toggle between them, e.g. when you need more 317 | marks or a different set of colors. 318 | 319 | :MarkPalette {palette} Highlight existing and future marks with the colors 320 | defined in {palette}. If the new palette contains less 321 | mark groups than the current one, the additional marks 322 | are lost. 323 | You can use :command-completion for {palette}. 324 | 325 | See g:mwDefaultHighlightingPalette for how to change the default palette, 326 | and mark-palette-define for how to add your own custom palettes. 327 | 328 | INSTALLATION 329 | ------------------------------------------------------------------------------ 330 | 331 | The code is hosted in a Git repo at 332 | https://github.com/inkarkat/vim-mark 333 | You can use your favorite plugin manager, or "git clone" into a directory used 334 | for Vim packages. Releases are on the "stable" branch, the latest unstable 335 | development snapshot on "master". 336 | 337 | This script is also packaged as a vimball. If you have the "gunzip" 338 | decompressor in your PATH, simply edit the \*.vmb.gz package in Vim; otherwise, 339 | decompress the archive first, e.g. using WinZip. Inside Vim, install by 340 | sourcing the vimball or via the :UseVimball command. 341 | 342 | vim mark*.vmb.gz 343 | :so % 344 | 345 | To uninstall, use the :RmVimball command. 346 | 347 | ### DEPENDENCIES 348 | 349 | - Requires Vim 7.1 with matchadd(), or Vim 7.2 or higher. 350 | - Requires the ingo-library.vim plugin ([vimscript #4433](http://www.vim.org/scripts/script.php?script_id=4433)), version 1.046 or 351 | higher. 352 | 353 | CONFIGURATION 354 | ------------------------------------------------------------------------------ 355 | 356 | For a permanent configuration, put the following commands into your vimrc. 357 | 358 | This plugin defines 6 mark groups: 359 | ``` 360 | 1: Cyan 2:Green 3:Yellow 4:Red 5:Magenta 6:Blue 361 | ``` 362 | Higher numbers always take precedence and are displayed above lower ones. 363 | 364 | Especially if you use GVIM, you can switch to a richer palette of up to 18 365 | colors: 366 | 367 | let g:mwDefaultHighlightingPalette = 'extended' 368 | 369 | Or, if you have both good eyes and display, you can try a palette that defines 370 | 27, 58, or even 77 colors, depending on the number of available colors: 371 | 372 | let g:mwDefaultHighlightingPalette = 'maximum' 373 | 374 | Note: This only works for built-in palettes and those that you define prior to 375 | running the plugin. If you extend the built-ins after plugin initialization 376 | (mark-palette-define), use :MarkPalette instead. 377 | 378 | If you like the additional colors, but don't need that many of them, restrict 379 | their number via: 380 | 381 | let g:mwDefaultHighlightingNum = 9 382 | 383 | If none of the default highlightings suits you, define your own colors in your 384 | vimrc file (or anywhere before this plugin is sourced, but after any 385 | :colorscheme), in the following form (where N = 1..): 386 | 387 | highlight MarkWordN ctermbg=Cyan ctermfg=Black guibg=#8CCBEA guifg=Black 388 | 389 | You can also use this form to redefine only some of the default highlightings. 390 | If you want to avoid losing the highlightings on :colorscheme commands, you 391 | need to re-apply your highlights on the ColorScheme event, similar to how 392 | this plugin does. Or you define the palette not via :highlight commands, but 393 | use the plugin's infrastructure: 394 | 395 | let g:mwDefaultHighlightingPalette = [ 396 | \ { 'ctermbg':'Cyan', 'ctermfg':'Black', 'guibg':'#8CCBEA', 'guifg':'Black' }, 397 | \ ... 398 | \] 399 | 400 | If you want to switch multiple palettes during runtime, you need to define 401 | them as proper palettes. 402 | a) To add your palette to the existing ones, do this _after_ the default 403 | palette has been defined (e.g. in ~/.vim/after/plugin/mark.vim): 404 | 405 | if ! exists('g:mwPalettes') " (Optional) guard if the plugin isn't properly installed. 406 | finish 407 | endif 408 | 409 | let g:mwPalettes['mypalette'] = [ 410 | \ { 'ctermbg':'Cyan', 'ctermfg':'Black', 'guibg':'#8CCBEA', 'guifg':'Black' }, 411 | \ ... 412 | \] 413 | let g:mwPalettes['other'] = [ ... ] 414 | 415 | " Make it the default; you cannot use g:mwDefaultHighlightingPalette 416 | here, as the Mark plugin has already been initialized: 417 | MarkPalette mypalette 418 | 419 | b) Alternatively, you can completely override all built-in palettes in your 420 | vimrc: 421 | 422 | let g:mwPalettes = { 423 | \ 'mypalette': [ 424 | \ { 'ctermbg':'Cyan', 'ctermfg':'Black', 'guibg':'#8CCBEA', 'guifg':'Black' }, 425 | \ ... 426 | \ ] 427 | \} 428 | 429 | " Make it the default: 430 | let g:mwDefaultHighlightingPalette = 'mypalette' 431 | 432 | The search type highlighting (in the search message) can be changed via: 433 | 434 | highlight link SearchSpecialSearchType MoreMsg 435 | 436 | By default, any marked words are also added to the search (/) and input (@) 437 | history; if you don't want that, remove the corresponding symbols from: 438 | 439 | let g:mwHistAdd = '/@' 440 | 441 | To enable the automatic restore of marks from a previous Vim session: 442 | 443 | let g:mwAutoLoadMarks = 1 444 | 445 | To turn off the automatic persistence of marks across Vim sessions: 446 | 447 | let g:mwAutoSaveMarks = 0 448 | 449 | You can still explicitly save marks via :MarkSave. 450 | 451 | If you have set 'ignorecase', but want marks to be case-insensitive, you can 452 | override the default behavior of using 'ignorecase' by setting: 453 | 454 | let g:mwIgnoreCase = 0 455 | 456 | To exclude some tab pages, windows, or buffers / filetypes from showing mark 457 | highlightings (you can still "blindly" navigate to marks in there with the 458 | corresponding mappings), you can define a List of expressions or Funcrefs that 459 | are evaluated in every window; if one returns 1, the window will not show 460 | marks. 461 | 462 | " Don't mark temp files, Python filetype, and scratch files as defined by 463 | " a custom function. 464 | let g:mwExclusionPredicates = 465 | \ ['expand("%:p") =~# "/tmp"', '&filetype == "python", function('ExcludeScratchFiles')] 466 | 467 | By default, tab pages / windows / buffers that have t:nomarks / w:nomarks / 468 | b:nomarks with a true value are excluded. Therefore, to suppress mark 469 | highlighting in a buffer, you can simply 470 | 471 | :let b:nomarks = 1 472 | 473 | If the predicate changes after a window has already been visible, you can 474 | update the mark highlighting by either: 475 | - switching tab pages back and forth 476 | - toggling marks on / off (via <Plug>MarkToggle) 477 | - :call mark#UpdateMark() (for current buffer) 478 | - :call mark#UpdateScope() (for all windows in the current tab page) 479 | 480 | This plugin uses matchadd() for the highlightings. Each mark group has its 481 | own priority, with higher group values having higher priority; i.e. going "on 482 | top". The maximum priority (used for the last defined mark group) can be 483 | changed via: 484 | 485 | let g:mwMaxMatchPriority = -10 486 | 487 | For example when another plugin or customization also uses matches and you 488 | would like to change their relative priorities. The default is negative to 489 | step back behind the default search highlighting. 490 | 491 | If you want no or only a few of the available mappings, you can completely 492 | turn off the creation of the default mappings by defining (before the plugin 493 | is loaded): 494 | 495 | :let g:mw_no_mappings = 1 496 | 497 | This saves you from mapping dummy keys to all unwanted mapping targets. 498 | 499 | If you want to use different mappings, map your keys to the <Plug>Mark... 500 | mapping targets _before_ sourcing the script (e.g. in your vimrc): 501 | 502 | nmap m MarkSet 503 | nmap gm MarkPartialWord 504 | xmap m MarkSet 505 | nmap r MarkRegex 506 | xmap r MarkRegex 507 | nmap n MarkClear 508 | nmap * MarkSearchCurrentNext 509 | nmap # MarkSearchCurrentPrev 510 | nmap / MarkSearchAnyNext 511 | nmap ? MarkSearchAnyPrev 512 | nmap * MarkSearchNext 513 | nmap # MarkSearchPrev 514 | 515 | There are no default mappings for toggling all marks and for the :MarkClear 516 | command, but you can define some yourself: 517 | 518 | nmap M MarkToggle 519 | nmap N MarkAllClear 520 | 521 | As the latter is irreversible, there's also an alternative with an additional 522 | confirmation: 523 | 524 | nmap N MarkConfirmAllClear 525 | 526 | To remove the default overriding of \* and #, use: 527 | 528 | nmap IgnoreMarkSearchNext MarkSearchNext 529 | nmap IgnoreMarkSearchPrev MarkSearchPrev 530 | 531 | If you don't want the \* and # mappings remember the last search type and 532 | instead always search for the next occurrence of the current mark, with a 533 | fallback to Vim's original \* command, use: 534 | 535 | nmap * MarkSearchOrCurNext 536 | nmap # MarkSearchOrCurPrev 537 | 538 | Or for search for the next occurrence of any mark with fallback to \*: 539 | 540 | nmap * MarkSearchOrAnyNext 541 | nmap # MarkSearchOrAnyPrev 542 | 543 | Mark searches could also be combined with the built-in search. This mapping 544 | overloads the default n|/|N commands to search for any mark if there is any 545 | mark defined and marks are enabled, and fall back to the default search if 546 | not: 547 | 548 | nmap n MarkSearchAnyOrDefaultNext 549 | nmap N MarkSearchAnyOrDefaultPrev 550 | 551 | The search mappings (\*, #, etc.) interpret [count] as the number of 552 | occurrences to jump over. If you don't want to use the separate 553 | mark-keypad-searching mappings, and rather want [count] select the highlight 554 | group to target (and you can live with jumps restricted to the very next 555 | match), (re-)define to these mapping targets: 556 | 557 | nmap * MarkSearchGroupNext 558 | nmap # MarkSearchGroupPrev 559 | 560 | You can remap the direct group searches (by default via the keypad 1-9 keys): 561 | 562 | nmap 1 MarkSearchGroup1Next 563 | nmap ! MarkSearchGroup1Prev 564 | 565 | If you need more / less groups, this can be configured via: 566 | 567 | let g:mwDirectGroupJumpMappingNum = 20 568 | 569 | Set to 0 to completely turn off the keypad mappings. This is easier than 570 | remapping all <Plug>-mappings. 571 | 572 | As an alternative to the direct group searches, you can also define mappings 573 | that search a next / previous used group: 574 | 575 | nmap +* MarkSearchUsedGroupNext 576 | nmap -* MarkSearchUsedGroupPrev 577 | 578 | Some people like to create a mark based on the visual selection, like 579 | v\_<Leader>m, but have whitespace in the selection match any whitespace when 580 | searching (searching for "hello world" will also find "hello<Tab>world" as 581 | well as "hello" at the end of a line, with "world" at the start of the next 582 | line). The Vim Tips Wiki describes such a setup for the built-in search at 583 | http://vim.wikia.com/wiki/Search_for_visually_selected_text 584 | You can achieve the same with the Mark plugin through the <Plug>MarkIWhiteSet 585 | mapping target: Using this, you can assign a new visual mode mapping <Leader>\* 586 | 587 | xmap * MarkIWhiteSet 588 | 589 | or override the default v\_<Leader>m mapping, in case you always want this 590 | behavior: 591 | 592 | vmap IgnoreMarkSet MarkSet 593 | xmap m MarkIWhiteSet 594 | 595 | INTEGRATION 596 | ------------------------------------------------------------------------------ 597 | 598 | The following functions offer (read-only) access to the script's internals: 599 | - mark#GetGroupNum(): number of available groups 600 | - mark#GetCount(): number of defined marks 601 | - mark#GetPattern([{index}]): search regular expression for an individual mark 602 | - mark#GetMarkNumber({pattern}, {isLiteral}, {isConsiderAlternatives}): mark 603 | number of a pattern / literal text 604 | 605 | LIMITATIONS 606 | ------------------------------------------------------------------------------ 607 | 608 | - If the 'ignorecase' setting is changed, there will be discrepancies between 609 | the highlighted marks and subsequent jumps to marks. 610 | - If {pattern} in a :Mark command contains atoms that change the semantics of 611 | the entire (/\\c, /\\C) regular expression, there may be discrepancies 612 | between the highlighted marks and subsequent jumps to marks. 613 | 614 | ### CONTRIBUTING 615 | 616 | Report any bugs, send patches, or suggest features via the issue tracker at 617 | https://github.com/inkarkat/vim-mark/issues or email (address below). 618 | 619 | HISTORY 620 | ------------------------------------------------------------------------------ 621 | 622 | ##### 3.4.0 RELEASEME 623 | - ENH: Support mark persistence to sessions created via :mksession, too. 624 | 625 | __You need to update to ingo-library ([vimscript #4433](http://www.vim.org/scripts/script.php?script_id=4433)) version 1.047!__ 626 | 627 | ##### 3.3.0 17-Jan-2025 628 | - Expose mark#mark#AnyMarkPattern(). 629 | - Robustness: Place the ColorScheme initialization also in the 630 | MarkInitialization autocommand group. 631 | - Robustness: Add check for existence and compatible version of ingo-library. 632 | - ENH: Add <Leader>gm mapping for non-whole word matching like gstar. 633 | Contributed by Carl Smith. 634 | 635 | __You need to update to ingo-library ([vimscript #4433](http://www.vim.org/scripts/script.php?script_id=4433)) version 1.046!__ 636 | 637 | ##### 3.2.0 15-Feb-2022 638 | - Add mark#GetMarkNumber(), based on feedback by Snorch in #36. 639 | - Mark updates across windows now use win\_execute() (since Vim 8.1.1418) 640 | instead of :windo. This hopefully addresses the changes in window sizes that 641 | have been reported (e.g. in #34). 642 | - Add <Plug>MarkSearchAnyOrDefaultNext and <Plug>MarkSearchAnyOrDefaultPrev 643 | for an any-mark search with fallback to the built-in search pattern. 644 | Suggested by Denis Kasak. 645 | 646 | __You need to update to ingo-library ([vimscript #4433](http://www.vim.org/scripts/script.php?script_id=4433)) version 1.043!__ 647 | 648 | ##### 3.1.1 03-Aug-2020 649 | - Compatibility: After Vim 8.1.1241, a :range outside the number of buffers 650 | (e.g. :99Mark[Name]) causes an error. 651 | - ENH: Add (GUI-only) additional palettes "soft" and "softer" that are 652 | variants of "extended" with less saturation / higher brightness of 653 | background colors (for when the default colors are too distracting). 654 | - ENH: Marks that cover multiple lines (created through a visual selection or 655 | :Mark /{pattern}/) now also can be jumped to when the cursor is not on the 656 | mark's first line. 657 | 658 | __You need to update to ingo-library ([vimscript #4433](http://www.vim.org/scripts/script.php?script_id=4433)) version 1.042!__ 659 | 660 | ##### 3.1.0 23-Mar-2019 661 | - ENH: Handle magicness atoms (\\V, \\m) in regexps entered via <Leader>r or 662 | :Mark /{pattern}/. 663 | - ENH: Choose a more correct insertion point with multiple alternatives for a 664 | mark by projecting the length of the existing and alternatives and the added 665 | pattern. 666 | - BUG: Regression: <Leader>n without {N} and not on an existing mark prints 667 | error "Do not pass empty pattern to disable all marks". 668 | - ENH: Allow to exclude certain tab pages, windows, or buffers / filetypes 669 | from showing mark highlightings via g:mwExclusionPredicates or (with the 670 | default predicate) t:nomarks / w:nomarks / b:nomarks flags. 671 | - ENH: Allow to tweak the maximum match priority via g:mwMaxMatchPriority for 672 | better coexistence with other customizations that use :match / matchadd(). 673 | - ENH: Allow to disable all default mappings via a single g:mw\_no\_mappings 674 | configuration flag. 675 | - ENH: Appended (strong) green and yellow highlightings to the extended 676 | palette. 677 | - Refactoring: Move mark persistence implementation to ingo-library. No need 678 | to serialize into String type for viminfo beyond Vim 7.3.030. 679 | - BUG: Avoid creating jump when updating marks. Need to use :keepjumps windo. 680 | Reported by epheien. 681 | 682 | __You need to update to ingo-library ([vimscript #4433](http://www.vim.org/scripts/script.php?script_id=4433)) version 1.036!__ 683 | 684 | ##### 3.0.0 18-Sep-2017 685 | - CHG: Parse :Mark arguments as either /{pattern}/ or whole {word}. This 686 | better addresses the common use case of searching for whole words, and is 687 | consistent with built-in commands like :djump. 688 | - ENH: Keep previous (last accessed) window on :windo. 689 | - Consistently use :noautocmd during window iteration. 690 | - ENH: Add :MarkYankDefinitions and :MarkYankDefinitionsOneLiner commands. 691 | These make it easier to persist marks for specific files (e.g. by putting 692 | the :Mark commands into a local vimrc) or occasions (by defining a custom 693 | command or mapping with these commands), and are an alternative to 694 | :MarkSave/Load. 695 | - ENH: Add <Plug>MarkSearchUsedGroupNext and <Plug>MarkSearchUsedGroupPrev to 696 | search in a next / previous used group. Suggested by Louis Pan. 697 | - ENH: Add <Plug>MarkSearchCascadeStartWithStop, 698 | <Plug>MarkSearchCascadeNextWithStop, <Plug>MarkSearchCascadeStartNoStop, 699 | <Plug>MarkSearchCascadeNextNoStop to search in cascading mark groups, i.e. 700 | first all matches for group 1, then all for group 2, and so on. 701 | - CHG: Duplicate mark#GetNum() and mark#GetGroupNum(). Rename the former into 702 | mark#GetCount() and have it return the number of actually defined (i.e. 703 | non-empty) marks. 704 | - ENH: Allow to give names to mark groups via :MarkName and new :Mark 705 | /{pattern}/ as {name} command syntax. Names will be shown during searching, 706 | and persisted together with the marks. This makes it easier to handle 707 | several marks and enforce custom semantics for particular groups. 708 | - Properly abort on error by using :echoerr. 709 | - Add dependency to ingo-library ([vimscript #4433](http://www.vim.org/scripts/script.php?script_id=4433)). 710 | 711 | __You need to separately 712 | install ingo-library ([vimscript #4433](http://www.vim.org/scripts/script.php?script_id=4433)) version 1.020 (or higher)!__ 713 | 714 | ##### 2.8.5 29-Oct-2014 715 | - ENH: Add alternative <Plug>MarkConfirmAllClear optional command that works 716 | like <Plug>MarkAllClear, but with confirmation. Thanks to Marcelo Montu for 717 | suggesting this! 718 | 719 | ##### 2.8.4 19-Jun-2014 720 | - To avoid accepting an invalid regular expression (e.g. "\\(blah") and then 721 | causing ugly errors on every mark update, check the patterns passed by the 722 | user for validity. 723 | - CHG: The :Mark command doesn't query for a mark when the passed mark group 724 | doesn't exist (interactivity in Ex commands is unexpected). Instead, it 725 | returns an error. 726 | 727 | ##### 2.8.3 23-May-2014 728 | - The additional mapping described under :help mark-whitespace-indifferent got 729 | broken again by the refactoring of mark#DoMark() on 31-Jan-2013. Finally 730 | include this in the script as <Plug>MarkIWhiteSet and 731 | mark#GetVisualSelectionAsLiteralWhitespaceIndifferentPattern(). Thanks to 732 | Greg Klein for noticing and prodding me to include it. 733 | 734 | ##### 2.8.2 16-Dec-2013 735 | - BUG: :Mark cannot highlight patterns starting with a number. Use -range=0 736 | instead of -count. Thanks to Vladimir Marek for reporting this. 737 | 738 | ##### 2.8.1 22-Nov-2013 739 | - Allow to override the adding to existing marks via :[N]Mark! {pattern}. 740 | - ENH: Implement command completion for :[N]Mark that offers existing mark 741 | patterns (from group [N] / all groups), both as one regular expression and 742 | individual alternatives. The leading \\< can be omitted. 743 | 744 | ##### 2.8.0 01-Jun-2013 745 | - Also allow a [count] for <Leader>r to select (or query for) a mark group, as 746 | with <Leader>m. 747 | - CHG: Also set the current mark to the used mark group when a mark was set 748 | via <Leader>r and :Mark so that it is easier to determine whether the 749 | entered pattern actually matches anywhere. Thanks to Xiaopan Zhang for 750 | notifying me about this problem. 751 | - Add <Plug>MarkSearchGroupNext / <Plug>MarkSearchGroupPrev to enable 752 | searching for particular mark groups. Thanks to Xiaopan Zhang for the 753 | suggestion. 754 | - Define default mappings for keys 1-9 on the numerical keypad to jump to a 755 | particular group (backwards with <C-kN>). Their definition is controlled by 756 | the new g:mwDirectGroupJumpMappingNum variable. 757 | - ENH: Allow to store an arbitrary number of marks via named slots that can 758 | optionally be passed to :MarkLoad / :MarkSave. If the slot is all-uppercase, 759 | the marks will also be persisted across Vim invocations. 760 | 761 | ##### 2.7.2 15-Oct-2012 762 | - Issue an error message "No marks defined" instead of moving the cursor by 763 | one character when there are no marks (e.g. initially or after :MarkClear). 764 | - Enable custom integrations via new mark#GetNum() and mark#GetPattern() 765 | functions. 766 | 767 | ##### 2.7.1 14-Sep-2012 768 | - Enable alternative \* / # mappings that do not remember the last search type 769 | through new <Plug>MarkSearchOrCurNext, <Plug>MarkSearchOrCurPrev, 770 | <Plug>MarkSearchOrAnyNext, <Plug>MarkSearchOrAnyPrev mappings. Based on an 771 | inquiry from Kevin Huanpeng Du. 772 | 773 | ##### 2.7.0 04-Jul-2012 774 | - ENH: Implement :MarkPalette command to switch mark highlighting on-the-fly 775 | during runtime. 776 | - Add "maximum" palette contributed by rockybalboa4. 777 | 778 | ##### 2.6.5 24-Jun-2012 779 | - Don't define the default <Leader>m and <Leader>r mappings in select mode, 780 | just visual mode. Thanks to rockybalboa4 for pointing this out. 781 | 782 | ##### 2.6.4 23-Apr-2012 783 | - Allow to override 'ignorecase' setting via g:mwIgnoreCase. Thanks to fanhe 784 | for the idea and sending a patch. 785 | 786 | ##### 2.6.3 27-Mar-2012 787 | - ENH: Allow choosing of palette and limiting of default mark highlight groups 788 | via g:mwDefaultHighlightingPalette and g:mwDefaultHighlightingNum. 789 | - ENH: Offer an extended color palette in addition to the original 6-color one. 790 | Enable this via :let g:mwDefaultHighlightingPalette = "extended" in your 791 | vimrc. 792 | 793 | ##### 2.6.2 26-Mar-2012 794 | - ENH: When a [count] exceeding the number of available mark groups is given, 795 | a summary of marks is given and the user is asked to select a mark group. 796 | This allows to interactively choose a color via 99<Leader>m. 797 | If you use the mark-whitespace-indifferent mappings, 798 | 799 | __PLEASE UPDATE THE 800 | vnoremap <Plug>MarkWhitespaceIndifferent DEFINITION__ 801 | - ENH: Include count of alternative patterns in :Marks list. 802 | - CHG: Use ">" for next mark and "/" for last search in :Marks. 803 | 804 | ##### 2.6.1 23-Mar-2012 805 | - ENH: Add :Marks command that prints all mark highlight groups and their 806 | search patterns, plus information about the current search mark, next mark 807 | group, and whether marks are disabled. 808 | - ENH: Show which mark group a pattern was set / added / removed / cleared. 809 | - FIX: When the cursor is positioned on the current mark, [N]<Leader>n / 810 | <Plug>MarkClear with [N] appended the pattern for the current mark (again 811 | and again) instead of clearing it. Must not pass current mark pattern when 812 | [N] is given. 813 | - CHG: Show mark group number in same-mark search and rename search types from 814 | "any-mark", "same-mark", and "new-mark" to the shorter "mark-\*", "mark-N", 815 | and "mark-N!", respectively. 816 | 817 | ##### 2.6.0 22-Mar-2012 818 | - ENH: Allow [count] for <Leader>m and :Mark to add / subtract match to / from 819 | highlight group [count], and use [count]<Leader>n to clear only highlight 820 | group [count]. This was also requested by Philipp Marek. 821 | - FIX: :Mark and <Leader>n actually toggled marks back on when they were 822 | already off. Now, they stay off on multiple invocations. Use :call 823 | mark#Toggle() / <Plug>MarkToggle if you want toggling. 824 | 825 | ##### 2.5.3 02-Mar-2012 826 | - BUG: Version check mistakenly excluded Vim 7.1 versions that do have the 827 | matchadd() function. Thanks to Philipp Marek for sending a patch. 828 | 829 | ##### 2.5.2 09-Nov-2011 830 | - Fixed various problems with wrap-around warnings: 831 | - BUG: With a single match and 'wrapscan' set, a search error was issued. 832 | - FIX: Backwards search with single match leads to wrong error message 833 | instead. 834 | - FIX: Wrong logic for determining l:isWrapped lets wrap-around go undetected. 835 | 836 | ##### 2.5.1 17-May-2011 837 | - FIX: == comparison in s:DoMark() leads to wrong regexp (\\A vs. \\a) being 838 | cleared when 'ignorecase' is set. Use case-sensitive comparison ==# instead. 839 | - Refine :MarkLoad messages 840 | - Add whitespace-indifferent visual mark configuration example. Thanks to Greg 841 | Klein for the suggestion. 842 | 843 | ##### 2.5.0 07-May-2011 844 | - ENH: Add explicit mark persistence via :MarkLoad and :MarkSave commands and 845 | automatic persistence via the g:mwAutoLoadMarks and g:mwAutoSaveMarks 846 | configuration flags. (Request from Mun Johl, 16-Apr-2010) 847 | - Expose toggling of mark display (keeping the mark patterns) via new 848 | <Plug>MarkToggle mapping. Offer :MarkClear command as a replacement for the 849 | old argumentless :Mark command, which now just disables, but not clears all 850 | marks. 851 | 852 | ##### 2.4.4 18-Apr-2011 853 | - BUG: Include trailing newline character in check for current mark, so that a 854 | mark that matches the entire line (e.g. created by V<Leader>m) can be 855 | cleared via <Leader>n. Thanks to ping for reporting this. 856 | - FIX: On overlapping marks, mark#CurrentMark() returned the lowest, not the 857 | highest visible mark. So on overlapping marks, the one that was not visible 858 | at the cursor position was removed; very confusing! Use reverse iteration 859 | order. 860 | - FIX: To avoid an arbitrary ordering of highlightings when the highlighting 861 | group names roll over, and to avoid order inconsistencies across different 862 | windows and tabs, we assign a different priority based on the highlighting 863 | group. 864 | 865 | ##### 2.4.3 16-Apr-2011 866 | - Avoid losing the mark highlightings on :syn on or :colorscheme commands. 867 | Thanks to Zhou YiChao for alerting me to this issue and suggesting a fix. 868 | - Made the script more robust when somehow no highlightings have been defined 869 | or when the window-local reckoning of match IDs got lost. I had very 870 | occasionally encountered such script errors in the past. 871 | - Made global housekeeping variables script-local, only g:mwHistAdd is used 872 | for configuration. 873 | 874 | ##### 2.4.2 14-Jan-2011 (unreleased) 875 | - FIX: Capturing the visual selection could still clobber the blockwise yank 876 | mode of the unnamed register. 877 | 878 | ##### 2.4.1 13-Jan-2011 879 | - FIX: Using a named register for capturing the visual selection on 880 | {Visual}<Leader>m and {Visual}<Leader>r clobbered the unnamed register. Now 881 | using the unnamed register. 882 | 883 | ##### 2.4.0 13-Jul-2010 884 | - ENH: The MarkSearch mappings (<Leader>[\*#/?]) add the original cursor 885 | position to the jump list, like the built-in [/?\*#nN] commands. This allows 886 | to use the regular jump commands for mark matches, like with regular search 887 | matches. 888 | 889 | ##### 2.3.3 19-Feb-2010 890 | - BUG: Clearing of an accidental zero-width match (e.g. via :Mark \\zs) results 891 | in endless loop. Thanks to Andy Wokula for the patch. 892 | 893 | ##### 2.3.2 17-Nov-2009 894 | - BUG: Creation of literal pattern via '\\V' in {Visual}<Leader>m mapping 895 | collided with individual escaping done in <Leader>m mapping so that an 896 | escaped '\\\*' would be interpreted as a multi item when both modes are used 897 | for marking. Thanks to Andy Wokula for the patch. 898 | 899 | ##### 2.3.1 06-Jul-2009 900 | - Now working correctly when 'smartcase' is set. All mappings and the :Mark 901 | command use 'ignorecase', but not 'smartcase'. 902 | 903 | ##### 2.3.0 04-Jul-2009 904 | - All jump commands now take an optional [count], so you can quickly skip over 905 | some marks, as with the built-in \*/# and n/N commands. For this, the entire 906 | core search algorithm has been rewritten. The script's logic has been 907 | simplified through the use of Vim 7 features like Lists. 908 | - Now also printing a Vim-alike search error message when 'nowrapscan' is set. 909 | 910 | ##### 2.2.0 02-Jul-2009 911 | - Split off functions into autoload script. 912 | - Initialization of global variables and autocommands is now done lazily on 913 | the first use, not during loading of the plugin. This reduces Vim startup 914 | time and footprint as long as the functionality isn't yet used. 915 | - Split off documentation into separate help file. Now packaging as VimBall. 916 | 917 | ##### 2.1.0 06-Jun-2009 918 | - Replaced highlighting via :syntax with matchadd() / matchdelete(). This 919 | requires Vim 7.2 / 7.1 with patches. This method is faster, there are no 920 | more clashes with syntax highlighting (:match always has preference), and 921 | the background highlighting does not disappear under 'cursorline'. 922 | - Using winrestcmd() to fix effects of :windo: By entering a window, its 923 | height is potentially increased from 0 to 1. 924 | - Handling multiple tabs by calling s:UpdateScope() on the TabEnter event. 925 | 926 | ##### 2.0.0 01-Jun-2009 927 | - Now using Vim List for g:mwWord and thus requiring Vim 7. g:mwCycle is now 928 | zero-based, but the syntax groups "MarkWordx" are still one-based. 929 | - Factored :syntax operations out of s:DoMark() and s:UpdateMark() so that 930 | they can all be done in a single :windo. 931 | - Normal mode <Plug>MarkSet now has the same semantics as its visual mode 932 | cousin: If the cursor is on an existing mark, the mark is removed. 933 | Beforehand, one could only remove a visually selected mark via again 934 | selecting it. Now, one simply can invoke the mapping when on such a mark. 935 | 936 | ##### 1.6.1 31-May-2009 937 | - Publication of improved version by Ingo Karkat. 938 | - Now prepending search type ("any-mark", "same-mark", "new-mark") for better 939 | identification. 940 | - Retired the algorithm in s:PrevWord in favor of simply using <cword>, which 941 | makes mark.vim work like the \* command. At the end of a line, non-keyword 942 | characters may now be marked; the previous algorithm preferred any preceding 943 | word. 944 | - BF: If 'iskeyword' contains characters that have a special meaning in a 945 | regexp (e.g. [.\*]), these are now escaped properly. 946 | - Highlighting can now actually be overridden in the vimrc (anywhere _before_ 947 | sourcing this script) by using ':hi def'. 948 | - Added missing setter for re-inclusion guard. 949 | 950 | ##### 1.5.0 01-Sep-2008 951 | - Bug fixes and enhancements by Ingo Karkat. 952 | - Added <Plug>MarkAllClear (without a default mapping), which clears all 953 | marks, even when the cursor is on a mark. 954 | - Added <Plug>... mappings for hard-coded \\\*, \\#, \\/, \\?, \* and #, to allow 955 | re-mapping and disabling. Beforehand, there were some <Plug>... mappings 956 | and hard-coded ones; now, everything can be customized. 957 | - BF: Using :autocmd without <bang> to avoid removing _all_ autocmds for the 958 | BufWinEnter event. (Using a custom :augroup would be even better.) 959 | - BF: Explicitly defining s:current\_mark\_position; some execution paths left 960 | it undefined, causing errors. 961 | - ENH: Make the match according to the 'ignorecase' setting, like the star 962 | command. 963 | - ENH: The jumps to the next/prev occurrence now print 'search hit BOTTOM, 964 | continuing at TOP" and "Pattern not found:..." messages, like the \* and n/N 965 | Vim search commands. 966 | - ENH: Jumps now open folds if the occurrence is inside a closed fold, just 967 | like n/N do. 968 | 969 | ##### 1.1.8-g 25-Apr-2008 970 | - Last version published by Yuheng Xie on vim.org. 971 | 972 | ##### 1.1.2 22-Mar-2005 973 | - Initial version published by Yuheng Xie on vim.org. 974 | 975 | ------------------------------------------------------------------------------ 976 | Copyright: (C) 2008-2025 Ingo Karkat - 977 | (C) 2005-2008 Yuheng Xie - 978 | The [VIM LICENSE](http://vimdoc.sourceforge.net/htmldoc/uganda.html#license) applies to this plugin. 979 | 980 | Maintainer: Ingo Karkat <ingo@karkat.de> 981 | -------------------------------------------------------------------------------- /autoload/mark.vim: -------------------------------------------------------------------------------- 1 | " Script Name: mark.vim 2 | " Description: Highlight several words in different colors simultaneously. 3 | " 4 | " Copyright: (C) 2008-2025 Ingo Karkat 5 | " (C) 2005-2008 Yuheng Xie 6 | " The VIM LICENSE applies to this script; see ':help copyright'. 7 | " 8 | " Maintainer: Ingo Karkat 9 | " 10 | " DEPENDENCIES: 11 | " - ingo-library.vim plugin 12 | " - SearchSpecial.vim plugin (optional) 13 | " 14 | " Version: 3.4.0 15 | 16 | try 17 | call ingo#version#Has('1.047') 18 | catch /^ingo-library:/ 19 | echoerr v:exception 20 | catch /^Vim\%((\a\+)\)\=:/ 21 | echoerr printf('The ingo-library dependency is missing; see :help %s-dependencies', expand(':t:r')) 22 | endtry 23 | 24 | "- functions ------------------------------------------------------------------ 25 | 26 | silent! call SearchSpecial#DoesNotExist() " Execute a function to force autoload. 27 | if exists('*SearchSpecial#WrapMessage') 28 | function! s:WrapMessage( searchType, searchPattern, isBackward ) 29 | redraw 30 | call SearchSpecial#WrapMessage(a:searchType, a:searchPattern, a:isBackward) 31 | endfunction 32 | function! s:EchoSearchPattern( searchType, searchPattern, isBackward ) 33 | call SearchSpecial#EchoSearchPattern(a:searchType, a:searchPattern, a:isBackward) 34 | endfunction 35 | else 36 | function! s:Trim( message ) 37 | " Limit length to avoid "Hit ENTER" prompt. 38 | return strpart(a:message, 0, (&columns / 2)) . (len(a:message) > (&columns / 2) ? "..." : "") 39 | endfunction 40 | function! s:WrapMessage( searchType, searchPattern, isBackward ) 41 | redraw 42 | let v:warningmsg = printf('%s search hit %s, continuing at %s', a:searchType, (a:isBackward ? 'TOP' : 'BOTTOM'), (a:isBackward ? 'BOTTOM' : 'TOP')) 43 | echohl WarningMsg 44 | echo s:Trim(v:warningmsg) 45 | echohl None 46 | endfunction 47 | function! s:EchoSearchPattern( searchType, searchPattern, isBackward ) 48 | let l:message = (a:isBackward ? '?' : '/') . a:searchPattern 49 | echohl SearchSpecialSearchType 50 | echo a:searchType 51 | echohl None 52 | echon s:Trim(l:message) 53 | endfunction 54 | endif 55 | 56 | function! s:EscapeText( text ) 57 | return substitute( escape(a:text, '\' . '^$.*[~'), "\n", '\\n', 'ge' ) 58 | endfunction 59 | function! s:IsIgnoreCase( expr ) 60 | return ((exists('g:mwIgnoreCase') ? g:mwIgnoreCase : &ignorecase) && a:expr !~# '\\\@= 1 ? a:1 : 1) 66 | let l:regexp = (a:groupNum == 0 ? mark#CurrentMark()[0] : '') 67 | if empty(l:regexp) 68 | let l:cword = expand('') 69 | if ! empty(l:cword) 70 | let l:regexp = s:EscapeText(l:cword) 71 | " The star command only creates a \ search pattern if the 72 | " actually only consists of keyword characters. 73 | if l:cword =~# '^\k\+$' && l:markWholeWordOnly 74 | let l:regexp = '\<' . l:regexp . '\>' 75 | endif 76 | endif 77 | endif 78 | return (empty(l:regexp) ? 0 : mark#DoMark(a:groupNum, l:regexp)[0]) 79 | endfunction 80 | 81 | function! mark#GetVisualSelection() 82 | let save_clipboard = &clipboard 83 | set clipboard= " Avoid clobbering the selection and clipboard registers. 84 | let save_reg = getreg('"') 85 | let save_regmode = getregtype('"') 86 | silent normal! gvy 87 | let res = getreg('"') 88 | call setreg('"', save_reg, save_regmode) 89 | let &clipboard = save_clipboard 90 | return res 91 | endfunction 92 | function! mark#GetVisualSelectionAsLiteralPattern() 93 | return s:EscapeText(mark#GetVisualSelection()) 94 | endfunction 95 | function! mark#GetVisualSelectionAsRegexp() 96 | return substitute(mark#GetVisualSelection(), '\n', '', 'g') 97 | endfunction 98 | function! mark#GetVisualSelectionAsLiteralWhitespaceIndifferentPattern() 99 | return substitute(escape(mark#GetVisualSelection(), '\' . '^$.*[~'), '\_s\+', '\\_s\\+', 'g') 100 | endfunction 101 | 102 | " Manually input a regular expression. 103 | function! mark#MarkRegex( groupNum, regexpPreset ) 104 | call inputsave() 105 | echohl Question 106 | let l:regexp = input('Input pattern to mark: ', a:regexpPreset) 107 | echohl None 108 | call inputrestore() 109 | if empty(l:regexp) 110 | call ingo#err#Clear() 111 | return 0 112 | endif 113 | 114 | redraw " This is necessary when the user is queried for the mark group. 115 | return mark#DoMarkAndSetCurrent(a:groupNum, ingo#regexp#magic#Normalize(l:regexp))[0] 116 | endfunction 117 | 118 | function! s:Cycle( ... ) 119 | let l:currentCycle = s:cycle 120 | let l:newCycle = (a:0 ? a:1 : s:cycle) + 1 121 | let s:cycle = (l:newCycle < s:markNum ? l:newCycle : 0) 122 | return l:currentCycle 123 | endfunction 124 | function! s:FreeGroupIndex() 125 | let i = 0 126 | while i < s:markNum 127 | if empty(s:pattern[i]) 128 | return i 129 | endif 130 | let i += 1 131 | endwhile 132 | return -1 133 | endfunction 134 | function! mark#NextUsedGroupIndex( isBackward, isWrapAround, startIndex, count ) 135 | if a:isBackward 136 | let l:indices = range(a:startIndex - 1, 0, -1) 137 | if a:isWrapAround 138 | let l:indices += range(s:markNum - 1, a:startIndex + 1, -1) : 139 | endif 140 | else 141 | let l:indices = range(a:startIndex + 1, s:markNum - 1) 142 | if a:isWrapAround 143 | let l:indices += range(0, max([-1, a:startIndex - 1])) 144 | endif 145 | endif 146 | 147 | let l:count = a:count 148 | for l:i in l:indices 149 | if ! empty(s:pattern[l:i]) 150 | let l:count -= 1 151 | if l:count == 0 152 | return l:i 153 | endif 154 | endif 155 | endfor 156 | return -1 157 | endfunction 158 | 159 | function! mark#DefaultExclusionPredicate() 160 | return (exists('b:nomarks') && b:nomarks) || (exists('w:nomarks') && w:nomarks) || (exists('t:nomarks') && t:nomarks) 161 | endfunction 162 | 163 | " Set match / clear matches in the current window. 164 | function! s:MarkMatch( indices, expr ) 165 | if ! exists('w:mwMatch') 166 | let w:mwMatch = repeat([0], s:markNum) 167 | elseif len(w:mwMatch) != s:markNum 168 | " The number of marks has changed. 169 | if len(w:mwMatch) > s:markNum 170 | " Truncate the matches. 171 | for l:match in filter(w:mwMatch[s:markNum : ], 'v:val > 0') 172 | silent! call matchdelete(l:match) 173 | endfor 174 | let w:mwMatch = w:mwMatch[0 : (s:markNum - 1)] 175 | else 176 | " Expand the matches. 177 | let w:mwMatch += repeat([0], (s:markNum - len(w:mwMatch))) 178 | endif 179 | endif 180 | 181 | for l:index in a:indices 182 | if w:mwMatch[l:index] > 0 183 | silent! call matchdelete(w:mwMatch[l:index]) 184 | let w:mwMatch[l:index] = 0 185 | endif 186 | endfor 187 | 188 | if ! empty(a:expr) 189 | let l:index = a:indices[0] " Can only set one index for now. 190 | 191 | " Info: matchadd() does not consider the 'magic' (it's always on), 192 | " 'ignorecase' and 'smartcase' settings. 193 | " Make the match according to the 'ignorecase' setting, like the star command. 194 | " (But honor an explicit case-sensitive regexp via the /\C/ atom.) 195 | let l:expr = (s:IsIgnoreCase(a:expr) ? '\c' : '') . a:expr 196 | 197 | " To avoid an arbitrary ordering of highlightings, we assign a different 198 | " priority based on the highlight group. 199 | let l:priority = g:mwMaxMatchPriority - s:markNum + 1 + l:index 200 | 201 | let w:mwMatch[l:index] = matchadd('MarkWord' . (l:index + 1), l:expr, l:priority) 202 | endif 203 | endfunction 204 | " Initialize mark colors in a (new) window. 205 | function! mark#UpdateMark( ... ) 206 | for l:Predicate in g:mwExclusionPredicates 207 | if ingo#actions#EvaluateOrFunc(l:Predicate) 208 | " The window may have had marks applied previously. Clear any 209 | " existing matches. 210 | call s:MarkMatch(range(s:markNum), '') 211 | 212 | return 213 | endif 214 | endfor 215 | 216 | if a:0 217 | call call('s:MarkMatch', a:000) 218 | else 219 | let i = 0 220 | while i < s:markNum 221 | if ! s:enabled || empty(s:pattern[i]) 222 | call s:MarkMatch([i], '') 223 | else 224 | call s:MarkMatch([i], s:pattern[i]) 225 | endif 226 | let i += 1 227 | endwhile 228 | endif 229 | endfunction 230 | " Update matches in all windows. 231 | function! mark#UpdateScope( ... ) 232 | call call('ingo#window#iterate#All', [function('mark#UpdateMark')] + a:000) 233 | endfunction 234 | 235 | function! s:MarkEnable( enable, ...) 236 | if s:enabled != a:enable 237 | " En-/disable marks and perform a full refresh in all windows, unless 238 | " explicitly suppressed by passing in 0. 239 | let s:enabled = a:enable 240 | if g:mwAutoSaveMarks 241 | let g:MARK_ENABLED = s:enabled 242 | endif 243 | 244 | if ! a:0 || ! a:1 245 | call mark#UpdateScope() 246 | endif 247 | endif 248 | endfunction 249 | function! s:EnableAndMarkScope( indices, expr ) 250 | if s:enabled 251 | " Marks are already enabled, we just need to push the changes to all 252 | " windows. 253 | call mark#UpdateScope(a:indices, a:expr) 254 | else 255 | call s:MarkEnable(1) 256 | endif 257 | endfunction 258 | 259 | " Toggle visibility of marks, like :nohlsearch does for the regular search 260 | " highlighting. 261 | function! mark#Toggle() 262 | if s:enabled 263 | call s:MarkEnable(0) 264 | echo 'Disabled marks' 265 | else 266 | call s:MarkEnable(1) 267 | 268 | let l:markCnt = mark#GetCount() 269 | echo 'Enabled' (l:markCnt > 0 ? l:markCnt . ' ' : '') . 'marks' 270 | endif 271 | endfunction 272 | 273 | 274 | " Mark or unmark a regular expression. 275 | function! mark#Clear( groupNum ) 276 | if a:groupNum > 0 277 | return mark#DoMark(a:groupNum, '')[0] 278 | else 279 | let l:markText = mark#CurrentMark()[0] 280 | if empty(l:markText) 281 | return mark#DoMark(a:groupNum)[0] 282 | else 283 | return mark#DoMark(a:groupNum, l:markText)[0] 284 | endif 285 | endif 286 | endfunction 287 | function! s:SetPattern( index, pattern ) 288 | let s:pattern[a:index] = a:pattern 289 | 290 | if g:mwAutoSaveMarks 291 | call s:SavePattern() 292 | endif 293 | endfunction 294 | function! mark#ClearAll() 295 | let i = 0 296 | let indices = [] 297 | while i < s:markNum 298 | if ! empty(s:pattern[i]) 299 | call s:SetPattern(i, '') 300 | call add(indices, i) 301 | endif 302 | let i += 1 303 | endwhile 304 | let s:lastSearch = -1 305 | 306 | " Re-enable marks; not strictly necessary, since all marks have just been 307 | " cleared, and marks will be re-enabled, anyway, when the first mark is 308 | " added. It's just more consistent for mark persistence. But save the full 309 | " refresh, as we do the update ourselves. 310 | call s:MarkEnable(0, 0) 311 | 312 | call mark#UpdateScope(l:indices, '') 313 | 314 | if len(indices) > 0 315 | echo 'Cleared all' len(indices) 'marks' 316 | else 317 | echo 'All marks cleared' 318 | endif 319 | endfunction 320 | function! s:SetMark( index, regexp, ... ) 321 | if a:0 322 | if s:lastSearch == a:index 323 | let s:lastSearch = a:1 324 | endif 325 | endif 326 | call s:SetPattern(a:index, a:regexp) 327 | call s:EnableAndMarkScope([a:index], a:regexp) 328 | endfunction 329 | function! s:ClearMark( index ) 330 | " A last search there is reset. 331 | call s:SetMark(a:index, '', -1) 332 | endfunction 333 | function! s:RenderName( groupNum ) 334 | return (empty(s:names[a:groupNum - 1]) ? '' : ':' . s:names[a:groupNum - 1]) 335 | endfunction 336 | function! s:EnrichSearchType( searchType ) 337 | if a:searchType !=# 'mark*' 338 | return a:searchType 339 | endif 340 | 341 | let [l:markText, l:markPosition, l:markIndex] = mark#CurrentMark() 342 | return (l:markIndex >= 0 ? a:searchType . (l:markIndex + 1) . s:RenderName(l:markIndex + 1) : a:searchType) 343 | endfunction 344 | function! s:RenderMark( groupNum ) 345 | return 'mark-' . a:groupNum . s:RenderName(a:groupNum) 346 | endfunction 347 | function! s:EchoMark( groupNum, regexp ) 348 | call s:EchoSearchPattern(s:RenderMark(a:groupNum), a:regexp, 0) 349 | endfunction 350 | function! s:EchoMarkCleared( groupNum ) 351 | echohl SearchSpecialSearchType 352 | echo s:RenderMark(a:groupNum) 353 | echohl None 354 | echon ' cleared' 355 | endfunction 356 | function! s:EchoMarksDisabled() 357 | echo 'All marks disabled' 358 | endfunction 359 | 360 | function! s:SplitIntoAlternatives( pattern ) 361 | return ingo#regexp#split#TopLevelBranches(a:pattern) 362 | endfunction 363 | 364 | " Return [success, markGroupNum]. success is true when the mark has been set or 365 | " cleared. markGroupNum is the mark group number where the mark was set. It is 0 366 | " if the group was cleared. 367 | function! mark#DoMark( groupNum, ... ) 368 | call ingo#err#Clear() 369 | if s:markNum <= 0 370 | " Uh, somehow no mark highlightings were defined. Try to detect them again. 371 | call mark#Init() 372 | if s:markNum <= 0 373 | " Still no mark highlightings; complain. 374 | call ingo#err#Set('No mark highlightings defined') 375 | return [0, 0] 376 | endif 377 | endif 378 | 379 | let l:groupNum = a:groupNum 380 | if l:groupNum > s:markNum 381 | " This highlight group does not exist. 382 | let l:groupNum = mark#QueryMarkGroupNum() 383 | if l:groupNum < 1 || l:groupNum > s:markNum 384 | return [0, 0] 385 | endif 386 | endif 387 | 388 | let regexp = (a:0 ? a:1 : '') 389 | if empty(regexp) 390 | if l:groupNum == 0 391 | if a:0 392 | " :Mark // looks more like a typo than a command to disable all 393 | " marks; prevent that, and only accept :Mark for it. 394 | call ingo#err#Set('Do not pass empty pattern to disable all marks') 395 | return [0, 0] 396 | endif 397 | 398 | " Disable all marks. 399 | call s:MarkEnable(0) 400 | call s:EchoMarksDisabled() 401 | else 402 | " Clear the mark represented by the passed highlight group number. 403 | call s:ClearMark(l:groupNum - 1) 404 | if a:0 >= 2 | let s:names[l:groupNum - 1] = a:2 | endif 405 | call s:EchoMarkCleared(l:groupNum) 406 | endif 407 | 408 | return [1, 0] 409 | endif 410 | 411 | if l:groupNum == 0 412 | " Clear the mark if it has been marked. 413 | let i = 0 414 | while i < s:markNum 415 | if regexp ==# s:pattern[i] 416 | call s:ClearMark(i) 417 | if a:0 >= 2 | let s:names[i] = a:2 | endif 418 | call s:EchoMarkCleared(i + 1) 419 | return [1, 0] 420 | endif 421 | let i += 1 422 | endwhile 423 | else 424 | " Add / subtract the pattern as an alternative to the mark represented 425 | " by the passed highlight group number. 426 | let existingPattern = s:pattern[l:groupNum - 1] 427 | if ! empty(existingPattern) 428 | let alternatives = s:SplitIntoAlternatives(existingPattern) 429 | if index(alternatives, regexp) == -1 430 | let regexp = join(ingo#regexp#split#AddPatternByProjectedMatchLength(alternatives, regexp), '\|') 431 | else 432 | let regexp = join(filter(alternatives, 'v:val !=# regexp'), '\|') 433 | if empty(regexp) 434 | call s:ClearMark(l:groupNum - 1) 435 | if a:0 >= 2 | let s:names[l:groupNum - 1] = a:2 | endif 436 | call s:EchoMarkCleared(l:groupNum) 437 | return [1, 0] 438 | endif 439 | endif 440 | endif 441 | endif 442 | 443 | " add to history 444 | if stridx(g:mwHistAdd, '/') >= 0 445 | call histadd('/', regexp) 446 | endif 447 | if stridx(g:mwHistAdd, '@') >= 0 448 | call histadd('@', regexp) 449 | endif 450 | 451 | if l:groupNum == 0 452 | let i = s:FreeGroupIndex() 453 | if i != -1 454 | " Choose an unused highlight group. The last search is kept untouched. 455 | call s:Cycle(i) 456 | call s:SetMark(i, regexp) 457 | else 458 | " Choose a highlight group by cycle. A last search there is reset. 459 | let i = s:Cycle() 460 | call s:SetMark(i, regexp, -1) 461 | endif 462 | else 463 | let i = l:groupNum - 1 464 | " Use and extend the passed highlight group. A last search is updated 465 | " and thereby kept active. 466 | call s:SetMark(i, regexp, i) 467 | endif 468 | 469 | if a:0 >= 2 | let s:names[i] = a:2 | endif 470 | call s:EchoMark(i + 1, regexp) 471 | return [1, i + 1] 472 | endfunction 473 | function! mark#DoMarkAndSetCurrent( groupNum, ... ) 474 | " To avoid accepting an invalid regular expression (e.g. "\(blah") and then 475 | " causing ugly errors on every mark update, check the patterns passed by the 476 | " user for validity. (We assume that the expressions generated by the plugin 477 | " itself from literal text are all valid.) 478 | if a:0 && ! ingo#regexp#IsValid(a:1) 479 | return [0, 0] 480 | endif 481 | 482 | let l:result = call('mark#DoMark', [a:groupNum] + a:000) 483 | let l:markGroupNum = l:result[1] 484 | if l:markGroupNum > 0 485 | let s:lastSearch = l:markGroupNum - 1 486 | endif 487 | 488 | return l:result 489 | endfunction 490 | function! mark#SetMark( groupNum, ... ) 491 | " For the :Mark command, don't query when the passed mark group doesn't 492 | " exist (interactivity in Ex commands is unexpected). Instead, return an 493 | " error. 494 | if s:markNum > 0 && a:groupNum > s:markNum 495 | call ingo#err#Set(printf('Only %d mark highlight groups', s:markNum)) 496 | return 0 497 | endif 498 | if a:0 499 | let [l:pattern, l:nameArgument] = ingo#cmdargs#pattern#ParseUnescapedWithLiteralWholeWord(a:1, '\(\s\+as\%(\s\+\(.\{-}\)\)\?\)\?\s*') 500 | let l:pattern = ingo#regexp#magic#Normalize(l:pattern) " We'd strictly only have to do this for /{pattern}/, not for whole word(s), but as the latter doesn't contain magicness atoms, it doesn't hurt, and with this we don't need to explicitly distinguish between the two cases. 501 | if ! empty(l:nameArgument) 502 | let l:name = substitute(l:nameArgument, '^\s\+as\s*', '', '') 503 | return mark#DoMarkAndSetCurrent(a:groupNum, l:pattern, l:name) 504 | else 505 | return mark#DoMarkAndSetCurrent(a:groupNum, l:pattern) 506 | endif 507 | else 508 | return mark#DoMarkAndSetCurrent(a:groupNum) 509 | endif 510 | endfunction 511 | 512 | " Return [mark text, mark start position, mark index] of the mark under the 513 | " cursor (or ['', [], -1] if there is no mark). 514 | function! mark#CurrentMark() 515 | " Highlighting groups with higher numbers take precedence over lower numbers, 516 | " and therefore its marks appear "above" other marks. To retrieve the visible 517 | " mark in case of overlapping marks, we need to check from highest to lowest 518 | " highlight group. 519 | let i = s:markNum - 1 520 | while i >= 0 521 | if ! empty(s:pattern[i]) 522 | let l:matchPattern = (s:IsIgnoreCase(s:pattern[i]) ? '\c' : '\C') . s:pattern[i] 523 | 524 | let [l:startPosition, l:endPosition] = ingo#area#frompattern#GetCurrent(l:matchPattern, {'firstLnum': 1, 'lastLnum': line('$')}) 525 | if l:startPosition != [0, 0] 526 | return [s:pattern[i], l:startPosition, i] 527 | endif 528 | endif 529 | let i -= 1 530 | endwhile 531 | return ['', [], -1] 532 | endfunction 533 | 534 | " Search current mark. 535 | function! mark#SearchCurrentMark( isBackward ) 536 | let l:result = 0 537 | 538 | let [l:markText, l:markPosition, l:markIndex] = mark#CurrentMark() 539 | if empty(l:markText) 540 | if s:lastSearch == -1 541 | let l:result = mark#SearchAnyMark(a:isBackward) 542 | let s:lastSearch = mark#CurrentMark()[2] 543 | else 544 | let l:result = s:Search(s:pattern[s:lastSearch], v:count1, a:isBackward, [], s:RenderMark(s:lastSearch + 1)) 545 | endif 546 | else 547 | let l:result = s:Search(l:markText, v:count1, a:isBackward, l:markPosition, s:RenderMark(l:markIndex + 1) . (l:markIndex ==# s:lastSearch ? '' : '!')) 548 | let s:lastSearch = l:markIndex 549 | endif 550 | 551 | return l:result 552 | endfunction 553 | 554 | function! mark#SearchGroupMark( groupNum, count, isBackward, isSetLastSearch ) 555 | call ingo#err#Clear() 556 | if a:groupNum == 0 557 | " No mark group number specified; use last search, and fall back to 558 | " current mark if possible. 559 | if s:lastSearch == -1 560 | let [l:markText, l:markPosition, l:markIndex] = mark#CurrentMark() 561 | if empty(l:markText) 562 | return 0 563 | endif 564 | else 565 | let l:markIndex = s:lastSearch 566 | let l:markText = s:pattern[l:markIndex] 567 | let l:markPosition = [] 568 | endif 569 | else 570 | let l:groupNum = a:groupNum 571 | if l:groupNum > s:markNum 572 | " This highlight group does not exist. 573 | let l:groupNum = mark#QueryMarkGroupNum() 574 | if l:groupNum < 1 || l:groupNum > s:markNum 575 | return 0 576 | endif 577 | endif 578 | 579 | let l:markIndex = l:groupNum - 1 580 | let l:markText = s:pattern[l:markIndex] 581 | let l:markPosition = [] 582 | endif 583 | 584 | let l:result = s:Search(l:markText, a:count, a:isBackward, l:markPosition, s:RenderMark(l:markIndex + 1) . (l:markIndex ==# s:lastSearch ? '' : '!')) 585 | if a:isSetLastSearch 586 | let s:lastSearch = l:markIndex 587 | endif 588 | return l:result 589 | endfunction 590 | 591 | function! mark#SearchNextGroup( count, isBackward ) 592 | if s:lastSearch == -1 593 | " Fall back to current mark in case of no last search. 594 | let [l:markText, l:markPosition, l:markIndex] = mark#CurrentMark() 595 | if empty(l:markText) 596 | " Fall back to next group that would be taken. 597 | let l:groupIndex = s:GetNextGroupIndex() 598 | else 599 | let l:groupIndex = l:markIndex 600 | endif 601 | else 602 | let l:groupIndex = s:lastSearch 603 | endif 604 | 605 | let l:groupIndex = mark#NextUsedGroupIndex(a:isBackward, 1, l:groupIndex, a:count) 606 | if l:groupIndex == -1 607 | call ingo#err#Set(printf('No %s mark group%s used', (a:count == 1 ? '' : a:count . ' ') . (a:isBackward ? 'previous' : 'next'), (a:count == 1 ? '' : 's'))) 608 | return 0 609 | endif 610 | return mark#SearchGroupMark(l:groupIndex + 1, 1, a:isBackward, 1) 611 | endfunction 612 | 613 | 614 | function! mark#NoMarkErrorMessage() 615 | call ingo#err#Set('No marks defined') 616 | endfunction 617 | function! s:ErrorMessage( searchType, searchPattern, isBackward ) 618 | if &wrapscan 619 | let l:errmsg = a:searchType . ' not found: ' . a:searchPattern 620 | else 621 | let l:errmsg = printf('%s search hit %s without match for: %s', a:searchType, (a:isBackward ? 'TOP' : 'BOTTOM'), a:searchPattern) 622 | endif 623 | call ingo#err#Set(l:errmsg) 624 | endfunction 625 | 626 | " Wrapper around search() with additonal search and error messages and "wrapscan" warning. 627 | function! s:Search( pattern, count, isBackward, currentMarkPosition, searchType ) 628 | if empty(a:pattern) 629 | call mark#NoMarkErrorMessage() 630 | return 0 631 | endif 632 | 633 | let l:save_view = winsaveview() 634 | 635 | " searchpos() obeys the 'smartcase' setting; however, this setting doesn't 636 | " make sense for the mark search, because all patterns for the marks are 637 | " concatenated as branches in one large regexp, and because patterns that 638 | " result from the *-command-alike mappings should not obey 'smartcase' (like 639 | " the * command itself), anyway. If the :Mark command wants to support 640 | " 'smartcase', it'd have to emulate that into the regular expression. 641 | " Instead of temporarily unsetting 'smartcase', we force the correct 642 | " case-matching behavior through \c / \C. 643 | let l:searchPattern = (s:IsIgnoreCase(a:pattern) ? '\c' : '\C') . a:pattern 644 | 645 | let l:count = a:count 646 | let l:isWrapped = 0 647 | let l:isMatch = 0 648 | let l:line = 0 649 | while l:count > 0 650 | let [l:prevLine, l:prevCol] = [line('.'), col('.')] 651 | 652 | " Search for next match, 'wrapscan' applies. 653 | let [l:line, l:col] = searchpos( l:searchPattern, (a:isBackward ? 'b' : '') ) 654 | 655 | "****D echomsg '****' a:isBackward string([l:line, l:col]) string(a:currentMarkPosition) l:count 656 | if a:isBackward && l:line > 0 && [l:line, l:col] == a:currentMarkPosition && l:count == a:count 657 | " On a search in backward direction, the first match is the start of the 658 | " current mark (if the cursor was positioned on the current mark text, and 659 | " not at the start of the mark text). 660 | " In contrast to the normal search, this is not considered the first 661 | " match. The mark text is one entity; if the cursor is positioned anywhere 662 | " inside the mark text, the mark text is considered the current mark. The 663 | " built-in '*' and '#' commands behave in the same way; the entire 664 | " text is considered the current match, and jumps move outside that text. 665 | " In normal search, the cursor can be positioned anywhere (via offsets) 666 | " around the search, and only that single cursor position is considered 667 | " the current match. 668 | " Thus, the search is retried without a decrease of l:count, but only if 669 | " this was the first match; repeat visits during wrapping around count as 670 | " a regular match. The search also must not be retried when this is the 671 | " first match, but we've been here before (i.e. l:isMatch is set): This 672 | " means that there is only the current mark in the buffer, and we must 673 | " break out of the loop and indicate that search wrapped around and no 674 | " other mark was found. 675 | if l:isMatch 676 | let l:isWrapped = 1 677 | break 678 | endif 679 | 680 | " The l:isMatch flag is set so if the final mark cannot be reached, the 681 | " original cursor position is restored. This flag also allows us to detect 682 | " whether we've been here before, which is checked above. 683 | let l:isMatch = 1 684 | elseif l:line > 0 685 | let l:isMatch = 1 686 | let l:count -= 1 687 | 688 | " Note: No need to check 'wrapscan'; the wrapping can only occur if 689 | " 'wrapscan' is actually on. 690 | if ! a:isBackward && (l:prevLine > l:line || l:prevLine == l:line && l:prevCol >= l:col) 691 | let l:isWrapped = 1 692 | elseif a:isBackward && (l:prevLine < l:line || l:prevLine == l:line && l:prevCol <= l:col) 693 | let l:isWrapped = 1 694 | endif 695 | else 696 | break 697 | endif 698 | endwhile 699 | 700 | " We're not stuck when the search wrapped around and landed on the current 701 | " mark; that's why we exclude a possible wrap-around via a:count == 1. 702 | let l:isStuckAtCurrentMark = ([l:line, l:col] == a:currentMarkPosition && a:count == 1) 703 | "****D echomsg '****' l:line l:isStuckAtCurrentMark l:isWrapped l:isMatch string([l:line, l:col]) string(a:currentMarkPosition) 704 | if l:line > 0 && ! l:isStuckAtCurrentMark 705 | let l:matchPosition = getpos('.') 706 | 707 | " Open fold at the search result, like the built-in commands. 708 | normal! zv 709 | 710 | " Add the original cursor position to the jump list, like the 711 | " [/?*#nN] commands. 712 | " Implementation: Memorize the match position, restore the view to the state 713 | " before the search, then jump straight back to the match position. This 714 | " also allows us to set a jump only if a match was found. (:call 715 | " setpos("''", ...) doesn't work in Vim 7.2) 716 | call winrestview(l:save_view) 717 | normal! m' 718 | call setpos('.', l:matchPosition) 719 | 720 | " Enable marks (in case they were disabled) after arriving at the mark (to 721 | " avoid unnecessary screen updates) but before the error message (to avoid 722 | " it getting lost due to the screen updates). 723 | call s:MarkEnable(1) 724 | 725 | if l:isWrapped 726 | call s:WrapMessage(s:EnrichSearchType(a:searchType), a:pattern, a:isBackward) 727 | else 728 | call s:EchoSearchPattern(s:EnrichSearchType(a:searchType), a:pattern, a:isBackward) 729 | endif 730 | return 1 731 | else 732 | if l:isMatch 733 | " The view has been changed by moving through matches until the end / 734 | " start of file, when 'nowrapscan' forced a stop of searching before the 735 | " l:count'th match was found. 736 | " Restore the view to the state before the search. 737 | call winrestview(l:save_view) 738 | endif 739 | 740 | " Enable marks (in case they were disabled) after arriving at the mark (to 741 | " avoid unnecessary screen updates) but before the error message (to avoid 742 | " it getting lost due to the screen updates). 743 | call s:MarkEnable(1) 744 | 745 | if l:line > 0 && l:isStuckAtCurrentMark && l:isWrapped 746 | call s:WrapMessage(s:EnrichSearchType(a:searchType), a:pattern, a:isBackward) 747 | return 1 748 | else 749 | call s:ErrorMessage(a:searchType, a:pattern, a:isBackward) 750 | return 0 751 | endif 752 | endif 753 | endfunction 754 | 755 | " Combine all marks into one regexp. 756 | function! mark#AnyMarkPattern() 757 | return join(filter(copy(s:pattern), '! empty(v:val)'), '\|') 758 | endfunction 759 | 760 | " Search any mark. 761 | function! mark#SearchAnyMark( isBackward ) 762 | let l:markPosition = mark#CurrentMark()[1] 763 | let l:markText = mark#AnyMarkPattern() 764 | let s:lastSearch = -1 765 | return s:Search(l:markText, v:count1, a:isBackward, l:markPosition, 'mark*') 766 | endfunction 767 | 768 | " Search last searched mark. 769 | function! mark#SearchNext( isBackward, ... ) 770 | let l:markText = mark#CurrentMark()[0] 771 | if empty(l:markText) 772 | return 0 " Fall back to the built-in * / # command (done by the mapping). 773 | endif 774 | 775 | " Use the provided search type or choose depending on last use of 776 | " MarkSearchCurrentNext / MarkSearchAnyNext. 777 | call call(a:0 ? a:1 : (s:lastSearch == -1 ? 'mark#SearchAnyMark' : 'mark#SearchCurrentMark'), [a:isBackward]) 778 | return 1 779 | endfunction 780 | 781 | 782 | 783 | " Load mark patterns from list. 784 | function! mark#Load( marks, enabled ) 785 | if s:markNum > 0 && len(a:marks) > 0 786 | " Initialize mark patterns (and optional names) with the passed list. 787 | " Ensure that, regardless of the list length, s:pattern / s:names 788 | " contain exactly s:markNum elements. 789 | for l:index in range(s:markNum) 790 | call s:DeserializeMark(get(a:marks, l:index, ''), l:index) 791 | endfor 792 | 793 | let s:enabled = a:enabled 794 | 795 | call mark#UpdateScope() 796 | 797 | " The list of patterns may be sparse, return only the actual patterns. 798 | return mark#GetCount() 799 | endif 800 | return 0 801 | endfunction 802 | 803 | " Access the list of mark patterns. 804 | function! s:SerializeMark( index ) 805 | return (empty(s:names[a:index]) ? s:pattern[a:index] : {'pattern': s:pattern[a:index], 'name': s:names[a:index]}) 806 | endfunction 807 | function! s:Deserialize( mark ) 808 | return (type(a:mark) == type({}) ? [get(a:mark, 'pattern', ''), get(a:mark, 'name', '')] : [a:mark, '']) 809 | endfunction 810 | function! s:DeserializeMark( mark, index ) 811 | let [s:pattern[a:index], s:names[a:index]] = s:Deserialize(a:mark) 812 | endfunction 813 | function! mark#ToList() 814 | " Trim unused patterns from the end of the list, the amount of available marks 815 | " may differ on the next invocation (e.g. due to a different number of 816 | " highlight groups in Vim and GVIM). We want to keep empty patterns in the 817 | " front and middle to maintain the mapping to highlight groups, though. 818 | let l:highestNonEmptyIndex = s:markNum - 1 819 | while l:highestNonEmptyIndex >= 0 && empty(s:pattern[l:highestNonEmptyIndex]) && empty(s:names[l:highestNonEmptyIndex]) 820 | let l:highestNonEmptyIndex -= 1 821 | endwhile 822 | 823 | return (l:highestNonEmptyIndex < 0 ? [] : map(range(0, l:highestNonEmptyIndex), 's:SerializeMark(v:val)')) 824 | endfunction 825 | 826 | " Return the mark number that represents a:pattern (regexp / literal text (as 827 | " set from m or :Mark {pattern}) with a:isLiteral = 1), or 0 if not 828 | " found. With a:isConsiderAlternatives = 1, will also look for individual 829 | " alternatives (set from {N}m or :{N}Mark). 830 | function! mark#GetMarkNumber( pattern, isLiteral, isConsiderAlternatives ) abort 831 | let l:searchPattern = ingo#regexp#magic#Normalize(a:isLiteral ? ingo#regexp#FromLiteralText(a:pattern, 1, '') : a:pattern) 832 | if empty(l:searchPattern) | return 0 | endif 833 | 834 | let l:i = 0 835 | while l:i < s:markNum 836 | if l:searchPattern ==# s:pattern[l:i] || (a:isConsiderAlternatives && index(s:SplitIntoAlternatives(s:pattern[l:i]), l:searchPattern) != -1) 837 | return l:i + 1 838 | endif 839 | let l:i += 1 840 | endwhile 841 | 842 | return 0 843 | endfunction 844 | 845 | " Common functions for :MarkLoad and :MarkSave 846 | function! mark#MarksVariablesComplete( ArgLead, CmdLine, CursorPos ) 847 | return sort(map(filter(keys(g:), 'v:val !~# "^MARK_\\%(MARKS\\|ENABLED\\)$" && v:val =~# "\\V\\^MARK_' . (empty(a:ArgLead) ? '\\S' : escape(a:ArgLead, '\')) . '"'), 'v:val[5:]')) 848 | endfunction 849 | 850 | " :MarkLoad command. 851 | function! mark#LoadCommand( isShowMessages, marksVariable ) 852 | try 853 | let l:isEnabled = (a:0 ? exists('g:' . a:marksVariable) : (exists('g:MARK_ENABLED') ? g:MARK_ENABLED : 1)) 854 | 855 | let l:marks = ingo#plugin#persistence#Load(a:marksVariable, []) 856 | if empty(l:marks) 857 | call ingo#err#Set('No marks stored under ' . a:marksVariable . (ingo#plugin#persistence#CanPersist(a:marksVariable) ? '' : ', and persistence not configured; cp. :help mark-persistence')) 858 | return 0 859 | endif 860 | 861 | let l:loadedMarkNum = mark#Load(l:marks, l:isEnabled) 862 | 863 | if a:isShowMessages 864 | if l:loadedMarkNum == 0 865 | echomsg 'No persistent marks defined in ' . a:marksVariable 866 | else 867 | echomsg printf('Loaded %d mark%s', l:loadedMarkNum, (l:loadedMarkNum == 1 ? '' : 's')) . (s:enabled ? '' : '; marks currently disabled') 868 | endif 869 | endif 870 | 871 | return 1 872 | catch /^Load:/ 873 | if a:0 874 | call ingo#err#Set(printf('Corrupted persistent mark info in g:%s', a:marksVariable)) 875 | else 876 | call ingo#err#Set(printf('Corrupted persistent mark info in g:%s and g:MARK_ENABLED', a:marksVariable)) 877 | unlet! g:MARK_ENABLED 878 | endif 879 | execute 'unlet! g:' . a:marksVariable 880 | return 0 881 | endtry 882 | endfunction 883 | 884 | " :MarkSave command. 885 | function! s:SavePattern( ... ) 886 | let l:savedMarks = mark#ToList() 887 | 888 | let l:marksVariable = call('mark#early#GetMarksVariable', a:000) 889 | call ingo#plugin#persistence#Store(l:marksVariable, l:savedMarks) 890 | if ! a:0 891 | let g:MARK_ENABLED = s:enabled 892 | endif 893 | 894 | return (empty(l:savedMarks) ? 2 : 1) 895 | endfunction 896 | function! mark#SaveCommand( ... ) 897 | if ! ingo#plugin#persistence#CanPersist() 898 | if ! a:0 899 | call ingo#err#Set('Cannot persist marks; cp. :help mark-persistence') 900 | return 0 901 | elseif a:1 =~# '^\L\+$' 902 | call ingo#msg#WarningMsg('Cannot persist marks; cp. :help mark-persistence') 903 | endif 904 | endif 905 | 906 | let l:result = call('s:SavePattern', a:000) 907 | if l:result == 2 908 | call ingo#msg#WarningMsg('No marks defined') 909 | endif 910 | return l:result 911 | endfunction 912 | 913 | " :MarkYankDefinitions and :MarkYankDefinitionsOneLiner commands. 914 | function! mark#GetDefinitionCommands( isOneLiner ) 915 | let l:marks = mark#ToList() 916 | if empty(l:marks) 917 | return [] 918 | endif 919 | 920 | let l:commands = [] 921 | for l:i in range(len(l:marks)) 922 | if ! empty(l:marks[l:i]) 923 | let [l:pattern, l:name] = s:Deserialize(l:marks[l:i]) 924 | call add(l:commands, printf('%dMark! /%s/%s', l:i + 1, escape(l:pattern, '/'), (empty(l:name) ? '' : ' as ' . l:name))) 925 | endif 926 | endfor 927 | 928 | return (a:isOneLiner ? [join(map(l:commands, '"exe " . string(v:val)'), ' | ')] : l:commands) 929 | endfunction 930 | function! mark#YankDefinitions( isOneLiner, register ) 931 | let l:commands = mark#GetDefinitionCommands(a:isOneLiner) 932 | if empty(l:commands) 933 | call ingo#err#Set('No marks defined') 934 | return 0 935 | endif 936 | 937 | return ! setreg(a:register, join(l:commands, "\n")) 938 | endfunction 939 | 940 | " :MarkName command. 941 | function! s:HasNamedMarks() 942 | return (! empty(filter(copy(s:names), '! empty(v:val)'))) 943 | endfunction 944 | function! mark#SetName( isClearAll, groupNum, name ) 945 | if a:isClearAll 946 | if a:groupNum != 0 947 | call ingo#err#Set('Use either [!] to clear all names, or [N] to name a single group, but not both.') 948 | return 0 949 | endif 950 | let s:names = repeat([''], s:markNum) 951 | elseif a:groupNum > s:markNum 952 | call ingo#err#Set(printf('Only %d mark highlight groups', s:markNum)) 953 | return 0 954 | else 955 | let s:names[a:groupNum - 1] = a:name 956 | endif 957 | return 1 958 | endfunction 959 | 960 | 961 | " Query mark group number. 962 | function! s:GetNextGroupIndex() 963 | let l:nextGroupIndex = s:FreeGroupIndex() 964 | if l:nextGroupIndex == -1 965 | let l:nextGroupIndex = s:cycle 966 | endif 967 | return l:nextGroupIndex 968 | endfunction 969 | function! s:GetMarker( index, nextGroupIndex ) 970 | let l:marker = '' 971 | if s:lastSearch == a:index 972 | let l:marker .= '/' 973 | endif 974 | if a:index == a:nextGroupIndex 975 | let l:marker .= '>' 976 | endif 977 | return l:marker 978 | endfunction 979 | function! s:GetAlternativeCount( pattern ) 980 | return len(s:SplitIntoAlternatives(a:pattern)) 981 | endfunction 982 | function! s:PrintMarkGroup( nextGroupIndex ) 983 | for i in range(s:markNum) 984 | echon ' ' 985 | execute 'echohl MarkWord' . (i + 1) 986 | let c = s:GetAlternativeCount(s:pattern[i]) 987 | echon printf('%1s%s%2d ', s:GetMarker(i, a:nextGroupIndex), (c ? (c > 1 ? c : '') . '*' : ''), (i + 1)) 988 | echohl None 989 | endfor 990 | endfunction 991 | function! mark#QueryMarkGroupNum() 992 | echohl Question 993 | echo 'Mark?' 994 | echohl None 995 | let l:nextGroupIndex = s:GetNextGroupIndex() 996 | call s:PrintMarkGroup(l:nextGroupIndex) 997 | 998 | let l:nr = 0 999 | while 1 1000 | let l:char = nr2char(getchar()) 1001 | 1002 | if l:char ==# "\" 1003 | return (l:nr == 0 ? l:nextGroupIndex + 1 : l:nr) 1004 | elseif l:char !~# '\d' 1005 | return -1 1006 | endif 1007 | echon l:char 1008 | 1009 | let l:nr = 10 * l:nr + l:char 1010 | if s:markNum < 10 * l:nr 1011 | return l:nr 1012 | endif 1013 | endwhile 1014 | endfunction 1015 | 1016 | " :Marks command. 1017 | function! mark#List() 1018 | let l:hasNamedMarks = s:HasNamedMarks() 1019 | echohl Title 1020 | if l:hasNamedMarks 1021 | echo "group:name\tpattern" 1022 | else 1023 | echo 'group pattern' 1024 | endif 1025 | echohl None 1026 | echon ' (N) # of alternatives > next mark group / current search mark' 1027 | let l:nextGroupIndex = s:GetNextGroupIndex() 1028 | for i in range(s:markNum) 1029 | execute 'echohl MarkWord' . (i + 1) 1030 | let l:alternativeCount = s:GetAlternativeCount(s:pattern[i]) 1031 | let l:alternativeCountString = (l:alternativeCount > 1 ? ' (' . l:alternativeCount . ')' : '') 1032 | let [l:name, l:format] = (empty(s:names[i]) ? ['', '%-4s'] : [':' . s:names[i], '%-10s']) 1033 | echo printf('%1s%3d' . l:format . ' ', s:GetMarker(i, l:nextGroupIndex), (i + 1), l:name . l:alternativeCountString) 1034 | echohl None 1035 | echon (l:hasNamedMarks ? "\t" : ' ') . s:pattern[i] 1036 | endfor 1037 | 1038 | if ! s:enabled 1039 | echo 'Marks are currently disabled.' 1040 | endif 1041 | endfunction 1042 | 1043 | 1044 | " :Mark command completion. 1045 | function! mark#Complete( ArgLead, CmdLine, CursorPos ) 1046 | let l:cmdlineBeforeCursor = strpart(a:CmdLine, 0, a:CursorPos) 1047 | let l:matches = matchlist(l:cmdlineBeforeCursor, '\C\(\d*\)\s*Mark!\?\s\+\V' . escape(a:ArgLead, '\')) 1048 | if empty(l:matches) 1049 | return [] 1050 | endif 1051 | 1052 | " Complete from the command's mark group, or all groups when none is 1053 | " specified. 1054 | let l:groupNum = 0 + l:matches[1] 1055 | let l:patterns =(l:groupNum == 0 || empty(get(s:pattern, l:groupNum - 1, '')) ? s:GetUsedPatterns() : [s:pattern[l:groupNum - 1]]) 1056 | 1057 | " Complete both the entire pattern as well as its individual alternatives. 1058 | let l:expandedPatterns = [] 1059 | for l:pattern in l:patterns 1060 | if index(l:expandedPatterns, l:pattern) == -1 1061 | call add(l:expandedPatterns, l:pattern) 1062 | endif 1063 | let l:alternatives = s:SplitIntoAlternatives(l:pattern) 1064 | if len(l:alternatives) > 1 1065 | for l:alternative in l:alternatives 1066 | if index(l:expandedPatterns, l:alternative) == -1 1067 | call add(l:expandedPatterns, l:alternative) 1068 | endif 1069 | endfor 1070 | endif 1071 | endfor 1072 | 1073 | call map(l:expandedPatterns, '"/" . escape(v:val, "/") . "/"') 1074 | 1075 | " Filter according to the argument lead. Allow to omit the frequent initial 1076 | " \< atom in the lead. 1077 | return filter(l:expandedPatterns, "v:val =~ '^\\%(\\\\<\\)\\?\\V' . " . string(escape(a:ArgLead, '\'))) 1078 | endfunction 1079 | 1080 | 1081 | "- integrations ---------------------------------------------------------------- 1082 | 1083 | " Access the number of possible marks. 1084 | function! mark#GetGroupNum() 1085 | return s:markNum 1086 | endfunction 1087 | 1088 | " Access the number of defined marks. 1089 | function! s:GetUsedPatterns() 1090 | return filter(copy(s:pattern), '! empty(v:val)') 1091 | endfunction 1092 | function! mark#GetCount() 1093 | return len(s:GetUsedPatterns()) 1094 | endfunction 1095 | 1096 | " Access the current / passed index pattern. 1097 | function! mark#GetPattern( ... ) 1098 | if a:0 1099 | return s:pattern[a:1] 1100 | else 1101 | return (s:lastSearch == -1 ? '' : s:pattern[s:lastSearch]) 1102 | endif 1103 | endfunction 1104 | 1105 | " Are marks currently enabled? 1106 | function! mark#IsEnabled() abort 1107 | return s:enabled 1108 | endfunction 1109 | 1110 | 1111 | "- initializations ------------------------------------------------------------ 1112 | 1113 | augroup Mark 1114 | autocmd! 1115 | autocmd BufWinEnter * call mark#UpdateMark() 1116 | autocmd WinEnter * if ! exists('w:mwMatch') | call mark#UpdateMark() | endif 1117 | autocmd TabEnter * call mark#UpdateScope() 1118 | augroup END 1119 | 1120 | " Define global variables and initialize current scope. 1121 | function! mark#Init() 1122 | let s:markNum = 0 1123 | while hlexists('MarkWord' . (s:markNum + 1)) 1124 | let s:markNum += 1 1125 | endwhile 1126 | let s:pattern = repeat([''], s:markNum) 1127 | let s:names = repeat([''], s:markNum) 1128 | let s:cycle = 0 1129 | let s:lastSearch = -1 1130 | let s:enabled = 1 1131 | endfunction 1132 | function! mark#ReInit( newMarkNum ) 1133 | if a:newMarkNum < s:markNum " There are less marks than before. 1134 | " Clear the additional highlight groups. 1135 | for i in range(a:newMarkNum + 1, s:markNum) 1136 | execute 'highlight clear MarkWord' . (i + 1) 1137 | endfor 1138 | 1139 | " Truncate the mark patterns. 1140 | let s:pattern = s:pattern[0 : (a:newMarkNum - 1)] 1141 | let s:names = s:names[0 : (a:newMarkNum - 1)] 1142 | 1143 | " Correct any indices. 1144 | let s:cycle = min([s:cycle, (a:newMarkNum - 1)]) 1145 | let s:lastSearch = (s:lastSearch < a:newMarkNum ? s:lastSearch : -1) 1146 | elseif a:newMarkNum > s:markNum " There are more marks than before. 1147 | " Expand the mark patterns. 1148 | let s:pattern += repeat([''], (a:newMarkNum - s:markNum)) 1149 | let s:names += repeat([''], (a:newMarkNum - s:markNum)) 1150 | endif 1151 | 1152 | let s:markNum = a:newMarkNum 1153 | endfunction 1154 | 1155 | call mark#Init() 1156 | if exists('g:mwDoDeferredLoad') && g:mwDoDeferredLoad 1157 | unlet g:mwDoDeferredLoad 1158 | call mark#LoadCommand(0, mark#early#GetMarksVariable()) 1159 | else 1160 | call mark#UpdateScope() 1161 | endif 1162 | 1163 | " vim: ts=4 sts=0 sw=4 noet 1164 | -------------------------------------------------------------------------------- /autoload/mark/cascade.vim: -------------------------------------------------------------------------------- 1 | " mark/cascade.vim: Cascading search through all used mark groups. 2 | " 3 | " DEPENDENCIES: 4 | " - ingo-library.vim plugin 5 | " 6 | " Copyright: (C) 2015-2020 Ingo Karkat 7 | " The VIM LICENSE applies to this script; see ':help copyright'. 8 | " 9 | " Maintainer: Ingo Karkat 10 | " 11 | " Version: 3.0.0 12 | 13 | let [s:cascadingLocation, s:cascadingPosition, s:cascadingGroupIndex, s:cascadingIsBackward, s:cascadingStop] = [[], [], -1, -1, -1] 14 | function! s:GetLocation() 15 | return [tabpagenr(), winnr(), bufnr('')] 16 | endfunction 17 | function! s:SetCascade() 18 | let s:cascadingLocation = s:GetLocation() 19 | let [l:markText, s:cascadingPosition, s:cascadingGroupIndex] = mark#CurrentMark() 20 | endfunction 21 | function! mark#cascade#Start( count, isStopBeforeCascade ) 22 | " Try passed mark group, current mark, last search, first used mark group, in that order. 23 | 24 | let s:cascadingIsBackward = -1 25 | let s:cascadingVisitedBuffers = {} 26 | call s:SetCascade() 27 | if (! a:count && s:cascadingGroupIndex != -1) || (a:count && s:cascadingGroupIndex + 1 == a:count) 28 | " We're already on a mark [with its group corresponding to count]. Take 29 | " that as the start and stay there (as we don't know which direction 30 | " (forward / backward) to take). 31 | return 1 32 | endif 33 | 34 | " Search for next mark and start cascaded search there. 35 | if ! mark#SearchGroupMark(a:count, 1, 0, 1) 36 | if a:count 37 | return 0 38 | elseif ! mark#SearchGroupMark(mark#NextUsedGroupIndex(0, 0, -1, 1) + 1, 1, 0, 1) 39 | call mark#NoMarkErrorMessage() 40 | return 0 41 | endif 42 | endif 43 | call s:SetCascade() 44 | return 1 45 | endfunction 46 | function! mark#cascade#Next( count, isStopBeforeCascade, isBackward ) 47 | if s:cascadingIsBackward == -1 48 | let s:cascadingIsBackward = a:isBackward 49 | endif 50 | 51 | if s:cascadingGroupIndex == -1 52 | call ingo#err#Set('No cascaded search defined') 53 | return 0 54 | elseif get(s:cascadingVisitedBuffers, bufnr(''), -1) == s:cascadingGroupIndex && s:cascadingLocation != s:GetLocation() 55 | " We've returned to a buffer that had previously already been searched 56 | " for the current mark. Instead of searching again, cascade to the next 57 | " group. 58 | let s:cascadingLocation = s:GetLocation() 59 | return s:Cascade(a:count, 0, a:isBackward) 60 | elseif s:cascadingStop != -1 61 | if s:cascadingLocation == s:GetLocation() 62 | " Within the same location: Switch to the next mark group. 63 | call s:SwitchToNextGroup(s:cascadingStop, a:isBackward) 64 | else 65 | " Allow to continue searching for the current mark group in other 66 | " locations. 67 | call s:ClearLocationAndPosition() 68 | endif 69 | let s:cascadingStop = -1 70 | elseif s:cascadingIsBackward != a:isBackward && s:cascadingLocation == s:GetLocation() && s:cascadingPosition == getpos('.')[1:2] 71 | " We've just cascaded to the next mark group, and now want back to the 72 | " previous one (by reversing search direction). 73 | return s:Cascade(a:count, a:isStopBeforeCascade, a:isBackward) 74 | endif 75 | 76 | let l:save_wrapscan = &wrapscan 77 | set wrapscan 78 | let l:save_view = winsaveview() 79 | try 80 | if ! mark#SearchGroupMark(s:cascadingGroupIndex + 1, a:count, a:isBackward, 1) 81 | return s:Cascade(a:count, a:isStopBeforeCascade, a:isBackward) 82 | endif 83 | if s:cascadingLocation == s:GetLocation() 84 | if s:cascadingPosition == getpos('.')[1:2] 85 | if s:cascadingIsBackward == a:isBackward 86 | " We're returned to the first match from that group. Undo 87 | " that last jump, and then cascade to the next one. 88 | call winrestview(l:save_view) 89 | return s:Cascade(a:count, a:isStopBeforeCascade, a:isBackward) 90 | else 91 | " Search direction has been reversed (from what it was when 92 | " the cascading position has been established). The current 93 | " match is now the last valid mark before cascading (in the 94 | " other direction). To recognize that, search for the next 95 | " match in the reversed direction, and set its position and 96 | " direction, then stay put here. 97 | let l:save_view = winsaveview() 98 | silent call mark#SearchGroupMark(s:cascadingGroupIndex + 1, 1, a:isBackward, 1) 99 | let s:cascadingPosition = getpos('.')[1:2] 100 | let s:cascadingIsBackward = a:isBackward 101 | call winrestview(l:save_view) 102 | return 1 103 | endif 104 | endif 105 | endif 106 | 107 | if empty(s:cascadingLocation) && empty(s:cascadingPosition) 108 | call s:SetCascade() 109 | endif 110 | 111 | return 1 112 | finally 113 | let &wrapscan = l:save_wrapscan 114 | endtry 115 | endfunction 116 | function! s:Cascade( count, isStopBeforeCascade, isBackward ) 117 | let l:nextGroupIndex = mark#NextUsedGroupIndex(a:isBackward, 0, s:cascadingGroupIndex, 1) 118 | if l:nextGroupIndex == -1 119 | redraw " Get rid of the previous mark search message. 120 | call ingo#err#Set(printf('Cascaded search ended with %s used group', (a:isBackward ? 'first' : 'last'))) 121 | return 0 122 | endif 123 | 124 | let s:cascadingVisitedBuffers[bufnr('')] = s:cascadingGroupIndex 125 | if a:isStopBeforeCascade 126 | let s:cascadingStop = l:nextGroupIndex 127 | redraw " Get rid of the previous mark search message. 128 | call ingo#msg#WarningMsg('Cascaded search reached last match of current group') 129 | return 1 130 | else 131 | call s:SwitchToNextGroup(l:nextGroupIndex, a:isBackward) 132 | return mark#cascade#Next(a:count, a:isStopBeforeCascade, a:isBackward) 133 | endif 134 | endfunction 135 | function! s:SwitchToNextGroup( nextGroupIndex, isBackward ) 136 | let s:cascadingGroupIndex = a:nextGroupIndex 137 | let s:cascadingIsBackward = a:isBackward 138 | call s:ClearLocationAndPosition() 139 | endfunction 140 | function! s:ClearLocationAndPosition() 141 | let [s:cascadingLocation, s:cascadingPosition] = [[], []] " Clear so that the next mark match will re-initialize them with the base match for the new mark group. 142 | endfunction 143 | 144 | " vim: ts=4 sts=0 sw=4 noet 145 | -------------------------------------------------------------------------------- /autoload/mark/early.vim: -------------------------------------------------------------------------------- 1 | " mark/early.vim: Early autoload functions. 2 | " 3 | " DEPENDENCIES: 4 | " - ingo-library.vim plugin 5 | " 6 | " Copyright: (C) 2025 Ingo Karkat 7 | " The VIM LICENSE applies to this script; see ':help copyright'. 8 | " 9 | " Maintainer: Ingo Karkat 10 | " 11 | " Version: 3.4.0 12 | 13 | function! mark#early#GetMarksVariable( ... ) 14 | return printf('MARK_%s', (a:0 ? a:1 : (ingo#plugin#persistence#CanPersist() == 2 ? 'marks': 'MARKS'))) " DWIM: Default to g:MARK_marks if only persistence for :mksession is configured 15 | endfunction 16 | 17 | " vim: set ts=8 sts=4 sw=4 noexpandtab ff=unix fdm=syntax : 18 | -------------------------------------------------------------------------------- /autoload/mark/palettes.vim: -------------------------------------------------------------------------------- 1 | " mark/palettes.vim: Additional palettes for mark highlighting. 2 | " 3 | " DEPENDENCIES: 4 | " 5 | " Copyright: (C) 2012-2019 Ingo Karkat 6 | " The VIM LICENSE applies to this script; see ':help copyright'. 7 | " 8 | " Maintainer: Ingo Karkat 9 | " Contributors: rockybalboa4 10 | " 11 | " Version: 3.1.0 12 | 13 | function! mark#palettes#Extended() 14 | return [ 15 | \ { 'ctermbg':'Blue', 'ctermfg':'Black', 'guibg':'#A1B7FF', 'guifg':'#001E80' }, 16 | \ { 'ctermbg':'Magenta', 'ctermfg':'Black', 'guibg':'#FFA1C6', 'guifg':'#80005D' }, 17 | \ { 'ctermbg':'Green', 'ctermfg':'Black', 'guibg':'#ACFFA1', 'guifg':'#0F8000' }, 18 | \ { 'ctermbg':'Yellow', 'ctermfg':'Black', 'guibg':'#FFE8A1', 'guifg':'#806000' }, 19 | \ { 'ctermbg':'DarkCyan', 'ctermfg':'Black', 'guibg':'#D2A1FF', 'guifg':'#420080' }, 20 | \ { 'ctermbg':'Cyan', 'ctermfg':'Black', 'guibg':'#A1FEFF', 'guifg':'#007F80' }, 21 | \ { 'ctermbg':'DarkBlue', 'ctermfg':'Black', 'guibg':'#A1DBFF', 'guifg':'#004E80' }, 22 | \ { 'ctermbg':'DarkMagenta','ctermfg':'Black', 'guibg':'#A29CCF', 'guifg':'#120080' }, 23 | \ { 'ctermbg':'DarkRed', 'ctermfg':'Black', 'guibg':'#F5A1FF', 'guifg':'#720080' }, 24 | \ { 'ctermbg':'Brown', 'ctermfg':'Black', 'guibg':'#FFC4A1', 'guifg':'#803000' }, 25 | \ { 'ctermbg':'DarkGreen', 'ctermfg':'Black', 'guibg':'#D0FFA1', 'guifg':'#3F8000' }, 26 | \ { 'ctermbg':'Red', 'ctermfg':'Black', 'guibg':'#F3FFA1', 'guifg':'#6F8000' }, 27 | \ { 'ctermbg':'White', 'ctermfg':'Gray', 'guibg':'#E3E3D2', 'guifg':'#999999' }, 28 | \ { 'ctermbg':'LightGray', 'ctermfg':'White', 'guibg':'#D3D3C3', 'guifg':'#666666' }, 29 | \ { 'ctermbg':'Gray', 'ctermfg':'Black', 'guibg':'#A3A396', 'guifg':'#222222' }, 30 | \ { 'ctermbg':'Black', 'ctermfg':'White', 'guibg':'#53534C', 'guifg':'#DDDDDD' }, 31 | \ { 'ctermbg':'Black', 'ctermfg':'Gray', 'guibg':'#131311', 'guifg':'#AAAAAA' }, 32 | \ { 'ctermbg':'Blue', 'ctermfg':'White', 'guibg':'#0000FF', 'guifg':'#F0F0FF' }, 33 | \ { 'ctermbg':'DarkRed', 'ctermfg':'White', 'guibg':'#FF0000', 'guifg':'#FFFFFF' }, 34 | \ { 'ctermbg':'DarkGreen', 'ctermfg':'White', 'guibg':'#00FF00', 'guifg':'#355F35' }, 35 | \ { 'ctermbg':'DarkYellow', 'ctermfg':'White', 'guibg':'#FFFF00', 'guifg':'#6F6F4C' }, 36 | \] 37 | endfunction 38 | 39 | function! mark#palettes#Soft() 40 | return [ 41 | \ { 'guibg':'#d1dbff', 'guifg':'#001250' }, 42 | \ { 'guibg':'#ffd1e2', 'guifg':'#500039' }, 43 | \ { 'guibg':'#d6ffd1', 'guifg':'#095000' }, 44 | \ { 'guibg':'#fff3d1', 'guifg':'#503c00' }, 45 | \ { 'guibg':'#e8d1ff', 'guifg':'#280050' }, 46 | \ { 'guibg':'#d1feff', 'guifg':'#004e50' }, 47 | \ { 'guibg':'#d1edff', 'guifg':'#003050' }, 48 | \ { 'guibg':'#d6d1ff', 'guifg':'#0a0050' }, 49 | \ { 'guibg':'#f9d1ff', 'guifg':'#460050' }, 50 | \ { 'guibg':'#ffe1d1', 'guifg':'#501d00' }, 51 | \ { 'guibg':'#e8ffd1', 'guifg':'#265000' }, 52 | \ { 'guibg':'#f8ffd1', 'guifg':'#455000' }, 53 | \ { 'guibg':'#f8f8f8', 'guifg':'#101010' }, 54 | \ { 'guibg':'#f0f0f0', 'guifg':'#202020' }, 55 | \ { 'guibg':'#e8e8e8', 'guifg':'#303030' }, 56 | \ { 'guibg':'#d8d8d8', 'guifg':'#404040' }, 57 | \ { 'guibg':'#c8c8c8', 'guifg':'#505050' }, 58 | \ { 'guibg':'#b0b0ff', 'guifg':'#4c4c6f' }, 59 | \ { 'guibg':'#ffb0b0', 'guifg':'#5f4141' }, 60 | \ { 'guibg':'#b0ffb0', 'guifg':'#415f41' }, 61 | \ { 'guibg':'#ffff80', 'guifg':'#4f4f27' }, 62 | \] 63 | endfunction 64 | function! mark#palettes#Softer() 65 | return [ 66 | \ { 'guibg':'#e9edff', 'guifg':'#001250' }, 67 | \ { 'guibg':'#ffe9f1', 'guifg':'#500039' }, 68 | \ { 'guibg':'#ebffe9', 'guifg':'#095000' }, 69 | \ { 'guibg':'#fff9e9', 'guifg':'#503c00' }, 70 | \ { 'guibg':'#f4e9ff', 'guifg':'#280050' }, 71 | \ { 'guibg':'#e9feff', 'guifg':'#004e50' }, 72 | \ { 'guibg':'#e9f6ff', 'guifg':'#003050' }, 73 | \ { 'guibg':'#ebe9ff', 'guifg':'#0a0050' }, 74 | \ { 'guibg':'#fce9ff', 'guifg':'#460050' }, 75 | \ { 'guibg':'#fff0e9', 'guifg':'#501d00' }, 76 | \ { 'guibg':'#f4ffe9', 'guifg':'#265000' }, 77 | \ { 'guibg':'#fbffe9', 'guifg':'#455000' }, 78 | \ { 'guibg':'#f8f8f8', 'guifg':'#101010' }, 79 | \ { 'guibg':'#f2f2f2', 'guifg':'#202020' }, 80 | \ { 'guibg':'#ececec', 'guifg':'#303030' }, 81 | \ { 'guibg':'#e3e3e3', 'guifg':'#404040' }, 82 | \ { 'guibg':'#dcdcdc', 'guifg':'#505050' }, 83 | \ { 'guibg':'#c8c8ff', 'guifg':'#4c4c6f' }, 84 | \ { 'guibg':'#ffc8c8', 'guifg':'#5f4141' }, 85 | \ { 'guibg':'#c8ffc8', 'guifg':'#415f41' }, 86 | \ { 'guibg':'#ffff98', 'guifg':'#4f4f27' }, 87 | \] 88 | endfunction 89 | 90 | function! mark#palettes#Maximum() 91 | let l:palette = [ 92 | \ { 'ctermbg':'Cyan', 'ctermfg':'Black', 'guibg':'#8CCBEA', 'guifg':'Black' }, 93 | \ { 'ctermbg':'Green', 'ctermfg':'Black', 'guibg':'#A4E57E', 'guifg':'Black' }, 94 | \ { 'ctermbg':'Yellow', 'ctermfg':'Black', 'guibg':'#FFDB72', 'guifg':'Black' }, 95 | \ { 'ctermbg':'Red', 'ctermfg':'Black', 'guibg':'#FF7272', 'guifg':'Black' }, 96 | \ { 'ctermbg':'Magenta', 'ctermfg':'Black', 'guibg':'#FFB3FF', 'guifg':'Black' }, 97 | \ { 'ctermbg':'Blue', 'ctermfg':'Black', 'guibg':'#9999FF', 'guifg':'Black' }, 98 | \] 99 | if has('gui_running') || &t_Co >= 88 100 | let l:palette += [ 101 | \ { 'ctermfg':'White', 'ctermbg':'17', 'guifg':'White', 'guibg':'#00005f' }, 102 | \ { 'ctermfg':'White', 'ctermbg':'22', 'guifg':'White', 'guibg':'#005f00' }, 103 | \ { 'ctermfg':'White', 'ctermbg':'23', 'guifg':'White', 'guibg':'#005f5f' }, 104 | \ { 'ctermfg':'White', 'ctermbg':'27', 'guifg':'White', 'guibg':'#005fff' }, 105 | \ { 'ctermfg':'White', 'ctermbg':'29', 'guifg':'White', 'guibg':'#00875f' }, 106 | \ { 'ctermfg':'White', 'ctermbg':'34', 'guifg':'White', 'guibg':'#00af00' }, 107 | \ { 'ctermfg':'Black', 'ctermbg':'37', 'guifg':'Black', 'guibg':'#00afaf' }, 108 | \ { 'ctermfg':'Black', 'ctermbg':'43', 'guifg':'Black', 'guibg':'#00d7af' }, 109 | \ { 'ctermfg':'Black', 'ctermbg':'47', 'guifg':'Black', 'guibg':'#00ff5f' }, 110 | \ { 'ctermfg':'White', 'ctermbg':'52', 'guifg':'White', 'guibg':'#5f0000' }, 111 | \ { 'ctermfg':'White', 'ctermbg':'53', 'guifg':'White', 'guibg':'#5f005f' }, 112 | \ { 'ctermfg':'White', 'ctermbg':'58', 'guifg':'White', 'guibg':'#5f5f00' }, 113 | \ { 'ctermfg':'White', 'ctermbg':'60', 'guifg':'White', 'guibg':'#5f5f87' }, 114 | \ { 'ctermfg':'White', 'ctermbg':'64', 'guifg':'White', 'guibg':'#5f8700' }, 115 | \ { 'ctermfg':'White', 'ctermbg':'65', 'guifg':'White', 'guibg':'#5f875f' }, 116 | \ { 'ctermfg':'Black', 'ctermbg':'66', 'guifg':'Black', 'guibg':'#5f8787' }, 117 | \ { 'ctermfg':'Black', 'ctermbg':'72', 'guifg':'Black', 'guibg':'#5faf87' }, 118 | \ { 'ctermfg':'Black', 'ctermbg':'74', 'guifg':'Black', 'guibg':'#5fafd7' }, 119 | \ { 'ctermfg':'Black', 'ctermbg':'78', 'guifg':'Black', 'guibg':'#5fd787' }, 120 | \ { 'ctermfg':'Black', 'ctermbg':'79', 'guifg':'Black', 'guibg':'#5fd7af' }, 121 | \ { 'ctermfg':'Black', 'ctermbg':'85', 'guifg':'Black', 'guibg':'#5fffaf' }, 122 | \] 123 | endif 124 | if has('gui_running') || &t_Co >= 256 125 | let l:palette += [ 126 | \ { 'ctermfg':'White', 'ctermbg':'90', 'guifg':'White', 'guibg':'#870087' }, 127 | \ { 'ctermfg':'White', 'ctermbg':'95', 'guifg':'White', 'guibg':'#875f5f' }, 128 | \ { 'ctermfg':'White', 'ctermbg':'96', 'guifg':'White', 'guibg':'#875f87' }, 129 | \ { 'ctermfg':'Black', 'ctermbg':'101', 'guifg':'Black', 'guibg':'#87875f' }, 130 | \ { 'ctermfg':'Black', 'ctermbg':'107', 'guifg':'Black', 'guibg':'#87af5f' }, 131 | \ { 'ctermfg':'Black', 'ctermbg':'114', 'guifg':'Black', 'guibg':'#87d787' }, 132 | \ { 'ctermfg':'Black', 'ctermbg':'117', 'guifg':'Black', 'guibg':'#87d7ff' }, 133 | \ { 'ctermfg':'Black', 'ctermbg':'118', 'guifg':'Black', 'guibg':'#87ff00' }, 134 | \ { 'ctermfg':'Black', 'ctermbg':'122', 'guifg':'Black', 'guibg':'#87ffd7' }, 135 | \ { 'ctermfg':'White', 'ctermbg':'130', 'guifg':'White', 'guibg':'#af5f00' }, 136 | \ { 'ctermfg':'White', 'ctermbg':'131', 'guifg':'White', 'guibg':'#af5f5f' }, 137 | \ { 'ctermfg':'Black', 'ctermbg':'133', 'guifg':'Black', 'guibg':'#af5faf' }, 138 | \ { 'ctermfg':'Black', 'ctermbg':'138', 'guifg':'Black', 'guibg':'#af8787' }, 139 | \ { 'ctermfg':'Black', 'ctermbg':'142', 'guifg':'Black', 'guibg':'#afaf00' }, 140 | \ { 'ctermfg':'Black', 'ctermbg':'152', 'guifg':'Black', 'guibg':'#afd7d7' }, 141 | \ { 'ctermfg':'White', 'ctermbg':'160', 'guifg':'White', 'guibg':'#d70000' }, 142 | \ { 'ctermfg':'Black', 'ctermbg':'166', 'guifg':'Black', 'guibg':'#d75f00' }, 143 | \ { 'ctermfg':'Black', 'ctermbg':'169', 'guifg':'Black', 'guibg':'#d75faf' }, 144 | \ { 'ctermfg':'Black', 'ctermbg':'174', 'guifg':'Black', 'guibg':'#d78787' }, 145 | \ { 'ctermfg':'Black', 'ctermbg':'175', 'guifg':'Black', 'guibg':'#d787af' }, 146 | \ { 'ctermfg':'Black', 'ctermbg':'186', 'guifg':'Black', 'guibg':'#d7d787' }, 147 | \ { 'ctermfg':'Black', 'ctermbg':'190', 'guifg':'Black', 'guibg':'#d7ff00' }, 148 | \ { 'ctermfg':'White', 'ctermbg':'198', 'guifg':'White', 'guibg':'#ff0087' }, 149 | \ { 'ctermfg':'Black', 'ctermbg':'202', 'guifg':'Black', 'guibg':'#ff5f00' }, 150 | \ { 'ctermfg':'Black', 'ctermbg':'204', 'guifg':'Black', 'guibg':'#ff5f87' }, 151 | \ { 'ctermfg':'Black', 'ctermbg':'209', 'guifg':'Black', 'guibg':'#ff875f' }, 152 | \ { 'ctermfg':'Black', 'ctermbg':'212', 'guifg':'Black', 'guibg':'#ff87d7' }, 153 | \ { 'ctermfg':'Black', 'ctermbg':'215', 'guifg':'Black', 'guibg':'#ffaf5f' }, 154 | \ { 'ctermfg':'Black', 'ctermbg':'220', 'guifg':'Black', 'guibg':'#ffd700' }, 155 | \ { 'ctermfg':'Black', 'ctermbg':'224', 'guifg':'Black', 'guibg':'#ffd7d7' }, 156 | \ { 'ctermfg':'Black', 'ctermbg':'228', 'guifg':'Black', 'guibg':'#ffff87' }, 157 | \] 158 | endif 159 | if has('gui_running') 160 | let l:palette += [ 161 | \ { 'guifg':'Black', 'guibg':'#b3dcff' }, 162 | \ { 'guifg':'Black', 'guibg':'#99cbd6' }, 163 | \ { 'guifg':'Black', 'guibg':'#7afff0' }, 164 | \ { 'guifg':'Black', 'guibg':'#a6ffd2' }, 165 | \ { 'guifg':'Black', 'guibg':'#a2de9e' }, 166 | \ { 'guifg':'Black', 'guibg':'#bcff80' }, 167 | \ { 'guifg':'Black', 'guibg':'#e7ff8c' }, 168 | \ { 'guifg':'Black', 'guibg':'#f2e19d' }, 169 | \ { 'guifg':'Black', 'guibg':'#ffcc73' }, 170 | \ { 'guifg':'Black', 'guibg':'#f7af83' }, 171 | \ { 'guifg':'Black', 'guibg':'#fcb9b1' }, 172 | \ { 'guifg':'Black', 'guibg':'#ff8092' }, 173 | \ { 'guifg':'Black', 'guibg':'#ff73bb' }, 174 | \ { 'guifg':'Black', 'guibg':'#fc97ef' }, 175 | \ { 'guifg':'Black', 'guibg':'#c8a3d9' }, 176 | \ { 'guifg':'Black', 'guibg':'#ac98eb' }, 177 | \ { 'guifg':'Black', 'guibg':'#6a6feb' }, 178 | \ { 'guifg':'Black', 'guibg':'#8caeff' }, 179 | \ { 'guifg':'Black', 'guibg':'#70b9fa' }, 180 | \] 181 | endif 182 | return l:palette 183 | endfunction 184 | 185 | " vim: ts=4 sts=0 sw=4 noet 186 | -------------------------------------------------------------------------------- /doc/mark.txt: -------------------------------------------------------------------------------- 1 | *mark.txt* Highlight several words in different colors simultaneously. 2 | 3 | MARK by Ingo Karkat 4 | (original version by Yuheng Xie) 5 | *mark.vim* 6 | description |mark-description| 7 | usage |mark-usage| 8 | installation |mark-installation| 9 | configuration |mark-configuration| 10 | integration |mark-integration| 11 | limitations |mark-limitations| 12 | known problems |mark-known-problems| 13 | todo |mark-todo| 14 | history |mark-history| 15 | 16 | ============================================================================== 17 | DESCRIPTION *mark-description* 18 | 19 | This plugin adds mappings and a :Mark command to highlight several words in 20 | different colors simultaneously, similar to the built-in 'hlsearch' 21 | highlighting of search results and the * |star| command. For example, when you 22 | are browsing a big program file, you could highlight multiple identifiers in 23 | parallel. This will make it easier to trace the source code. 24 | 25 | This is a continuation of vimscript #1238 by Yuheng Xie, who doesn't maintain 26 | his original version anymore and recommends switching to this fork. This 27 | plugin offers the following advantages over the original: 28 | - Much faster, all colored words can now be highlighted, no more clashes with 29 | syntax highlighting (due to use of matchadd()). 30 | - Many bug fixes. 31 | - Jumps behave like the built-in search, including wrap and error messages. 32 | - Like the built-in commands, jumps take an optional [count] to quickly skip 33 | over some marks. 34 | - Marks can be persisted, and patterns can be added / subtracted from 35 | mark highlight groups. 36 | 37 | SEE ALSO * 38 | 39 | - SearchAlternatives.vim (vimscript #4146) provides mappings and commands to 40 | add and subtract alternative branches to the current search pattern. 41 | - SearchHighlighting.vim (vimscript #4320) can change the semantics of the 42 | start command *, extends it to visual mode (like Mark) and has auto-search 43 | functionality which instantly highlights the word under the cursor when 44 | typing or moving around, like in many IDEs. 45 | - MarkMarkup.vim (vimscript #5777) extends mark.vim with rendering the 46 | highlightings as markup directly inside the text: as HTML tags that 47 | reproduce the mark's colors, or as appended numbers or symbols and a legend 48 | to look up the mark names. Any markup-based export format can be defined. 49 | 50 | RELATED WORKS * 51 | 52 | - MultipleSearch (vimscript #479) can highlight in a single window and in all 53 | buffers, but still relies on the :syntax highlighting method, which is 54 | slower and less reliable. 55 | - http://vim.wikia.com/wiki/Highlight_multiple_words offers control over the 56 | color used by mapping the 1-9 keys on the numeric keypad, persistence, and 57 | highlights only a single window. 58 | - highlight.vim (vimscript #1599) highlights lines or patterns of interest in 59 | different colors, using mappings that start with CTRL-H and work on cword. 60 | - quickhl.vim (vimscript #3692) can also list the matches with colors and in 61 | addition offers on-the-fly highlighting of the current word (like many IDEs 62 | do). 63 | - Highlight (http://www.drchip.org/astronaut/vim/index.html#HIGHLIGHT) has 64 | commands and mappings for highlighting and searching, uses matchadd(), but 65 | limits the scope of highlightings to the current window. 66 | - TempKeyword (vimscript #4636) is a simple plugin that can matchadd() the 67 | word under the cursor with \0 - \9 mappings. (And clear with \c0 etc.) 68 | - simple_highlighting (vimscript #4688) has commands and mappings to highlight 69 | 8 different slots in all buffers. 70 | - searchmatch (vimscript #4869) has commands and mappings for :[1,2,3]match, 71 | in the current window only. 72 | - highlight-groups.vim (vimscript #5612) can do buffer-local as well as 73 | tab-scoped highlighting via :syntax, and has multiple groups whose 74 | highlighting is defined in an external CSV file. 75 | - Syntax match (vimscript #5376) provides various (color-based) shortcut 76 | commands for :syntax match, and saves and restores those definitions, for 77 | text and log files. 78 | - SelX (vimscript #5875) provides multiple multi-colored highlights per-tab 79 | (that can be stored in a session), mappings that mirror the built-in search 80 | commands, as a special feature automatically displays a Highlight Usage Map. 81 | - hi (vimscript #5887) highlights words, sentences or regular expressions 82 | using many configured colors, and can search; also offers separate windows 83 | for filtering and configuration editing catered towards log analysis. 84 | - vim-highlight-hero (vimscript #5922) can also highlight the current word or 85 | selection, has some flexibility with regard to whitespace matching, is 86 | limited to the current window. 87 | - high-str (https://github.com/Pocco81/high-str.nvim) is a Neovim-only plugin 88 | that can highlight the visual selection in multiple, configurable colors. 89 | 90 | ============================================================================== 91 | USAGE *mark-usage* 92 | 93 | HIGHLIGHTING *mark-highlighting* 94 | *m* *gm* *v_m* 95 | m Mark the word under the cursor, similar to the |star| 96 | command. The next free highlight group is used. 97 | If already on a mark: Clear the mark, like 98 | |n|. 99 | gm Variant of m that marks the word under the 100 | cursor, but doesn't put "\<" and "\>" around the word, 101 | similar to the |gstar| command. 102 | {Visual}m Mark or unmark the visual selection. 103 | {N}m With {N}, mark the word under the cursor with the 104 | {N}gm named highlight group {N}. When that group is not 105 | empty, the word is added as an alternative match, so 106 | you can highlight multiple words with the same color. 107 | When the word is already contained in the list of 108 | alternatives, it is removed. 109 | 110 | When {N} is greater than the number of defined mark 111 | groups, a summary of marks is printed. Active mark 112 | groups are prefixed with "*" (or "M*" when there are 113 | M pattern alternatives), the default next group with 114 | ">", the last used search with "/" (like |:Marks| 115 | does). Input the mark group, accept the default with 116 | , or abort with or any other key. 117 | This way, when unsure about which number represents 118 | which color, just use 99n and pick the color 119 | interactively! 120 | 121 | {Visual}[N]m Ditto, based on the visual selection. 122 | 123 | *r* *v_r* 124 | [N]r Manually input a regular expression to mark. 125 | {Visual}[N]r Ditto, based on the visual selection. 126 | 127 | In accordance with the built-in |star| command, 128 | all these mappings use 'ignorecase', but not 129 | 'smartcase'. 130 | *n* 131 | n Clear the mark under the cursor. 132 | If not on a mark: Disable all marks, similar to 133 | |:nohlsearch|. 134 | Note: Marks that span multiple lines are not detected, 135 | so the use of n on such a mark will 136 | unintentionally disable all marks! Use 137 | {Visual}r or :Mark {pattern} to clear 138 | multi-line marks (or pass [N] if you happen to know 139 | the group number). 140 | {N}n Clear the marks represented by highlight group {N}. 141 | 142 | *:Mark* 143 | :{N}Mark Clear the marks represented by highlight group {N}. 144 | :[N]Mark[!] [/]{pattern}[/] 145 | Mark or unmark {pattern}. Unless [N] is given, the 146 | next free highlight group is used for marking. 147 | With [N], mark {pattern} with the named highlight 148 | group [N]. When that group is not empty, the word is 149 | added as an alternative match, so you can highlight 150 | multiple words with the same color, unless [!] is 151 | given; then, {pattern} overrides the existing mark. 152 | When the word is already contained in the list of 153 | alternatives, it is removed. 154 | For implementation reasons, {pattern} cannot use the 155 | 'smartcase' setting, only 'ignorecase'. 156 | Without [/], only literal whole words are matched. 157 | |:search-args| 158 | :Mark Disable all marks, similar to |:nohlsearch|. Marks 159 | will automatically re-enable when a mark is added or 160 | removed, or a search for marks is performed. 161 | *:MarkClear* 162 | :MarkClear Clear all marks. In contrast to disabling marks, the 163 | actual mark information is cleared, the next mark will 164 | use the first highlight group. This cannot be undone. 165 | *:MarkName* 166 | :[N]Mark[!] /{pattern}/ as [name] 167 | Mark or unmark {pattern}, and give it [name]. 168 | :{N}MarkName [name] 169 | Give [name] to mark group {N}. 170 | :MarkName! Clear names for all mark groups. 171 | 172 | 173 | SEARCHING *mark-searching* 174 | *star* *#* */* *?* 175 | [count]* [count]# 176 | [count]* [count]# 177 | [count]/ [count]? 178 | Use these six keys to jump to the [count]'th next / 179 | previous occurrence of a mark. 180 | You could also use Vim's / and ? to search, since the 181 | mark patterns are (optionally, see configuration) 182 | added to the search history, too. 183 | 184 | Cursor over mark Cursor not over mark 185 | --------------------------------------------------------------------------- 186 | * Jump to the next occurrence of Jump to the next occurrence of 187 | current mark, and remember it "last mark". 188 | as "last mark". 189 | 190 | / Jump to the next occurrence of Same as left. 191 | ANY mark. 192 | 193 | * If * is the most recently Do Vim's original * command. 194 | used, do a *; otherwise 195 | (/ is the most recently 196 | used), do a /. 197 | 198 | Note: When the cursor is on a mark, the backwards 199 | search does not jump to the beginning of the current 200 | mark (like the built-in search), but to the previous 201 | mark. The entire mark text is treated as one entity. 202 | 203 | You can use Vim's |jumplist| to go back to previous 204 | mark matches and the position before a mark search. 205 | 206 | *mark-keypad-searching* 207 | If you work with multiple highlight groups and assign special meaning to them 208 | (e.g. group 1 for notable functions, 2 for variables, 3 for includes), you can 209 | use the 1-9 keys on the numerical keypad to jump to occurrences of a 210 | particular highlight group. With the general * and # commands above, you'd 211 | first need to locate a nearby occurrence of the desired highlight group if 212 | it's not the last mark used. 213 | ** ** 214 | .. Jump to the [count]'th next occurrence of the mark 215 | belonging to highlight group 1..9. 216 | .. Jump to the [count]'th previous occurrence of the mark 217 | belonging to highlight group 1..9. 218 | Note that these commands only work in GVIM or if your 219 | terminal sends different key codes; sadly, most still 220 | don't. 221 | https://unix.stackexchange.com/questions/552297/make-gnome-terminal-send-correct-numeric-keypad-keycodes-to-vim 222 | The "Num Lock" indicator of your keyboard has 223 | to be ON; otherwise, the keypad is used for cursor 224 | movement. If the keypad doesn't work for you, you can 225 | still remap these mappings to alternatives; see below. 226 | Alternatively, you can set up mappings to search in a next / previous used 227 | group, see |mark-group-cycle|. 228 | 229 | *mark-cascaded-group-searching* 230 | [...] 231 | After a stop, retriggering the cascaded search in the same buffer and window 232 | moves to the next used group (you can jump inside the current buffer to choose 233 | a different starting point first). If you instead switch to another window or 234 | buffer, the current mark group continues to be searched (to allow you to 235 | keep searching for the current group in other locations, until those are all 236 | exhausted too). 237 | 238 | MARK PERSISTENCE *mark-persistence* 239 | 240 | The marks can be kept and restored across Vim sessions, using the |viminfo| 241 | file. For this to work, the "!" flag must be part of the 'viminfo' setting: > 242 | set viminfo^=! " Save and restore global variables. 243 | Marks can also be stored in |session-file|s with this setting: > 244 | set sessionoptions+=globals " Save and restore global variables. 245 | If you've configured the session-files but not viminfo, |:MarkLoad| and 246 | |:MarkSave| will default to a slot name that works with session files, i.e. 247 | the variable g:MARK_marks, instead of g:MARK_MARKS. Alternatively, if you've 248 | configured both but want to explicitly persist the marks into the session file 249 | and have it restored when that session is loaded, use > 250 | :MarkSave marks 251 | and either set |g:mwAutoLoadMarks| (automatic restore on session load) or use > 252 | :MarkLoad marks 253 | < *:MarkLoad* 254 | :MarkLoad Restore the marks from the previous Vim session. All 255 | current marks are discarded. 256 | :MarkLoad {slot} Restore the marks stored in the named {slot}. All 257 | current marks are discarded. 258 | *:MarkSave* 259 | :MarkSave Save the currently defined marks (or clear the 260 | persisted marks if no marks are currently defined) for 261 | use in a future Vim session. 262 | :MarkSave {slot} Save the currently defined marks in the named {slot}. 263 | If {slot} is all UPPERCASE, the marks are persisted 264 | and can be |:MarkLoad|ed in a future Vim session (to 265 | persist without closing Vim, use |:wviminfo|; an 266 | already running Vim session can import marks via 267 | |:rviminfo| followed by |:MarkLoad|). 268 | If {slot} contains at least one lowercase letter, the 269 | marks are persisted to any user sessions created by 270 | |:mksession|. 271 | If variable persistence isn't configured or the above 272 | conditions for {slot} aren't met (e.g. when using 273 | numbered slots), you can just recall within the 274 | current session. 275 | When no marks are currently defined, the {slot} is 276 | cleared. 277 | 278 | By default, automatic persistence is enabled (so you don't need to explicitly 279 | |:MarkSave|), but you have to explicitly load the persisted marks in a new Vim 280 | session via |:MarkLoad|, to avoid that you accidentally drag along outdated 281 | highlightings from Vim session to session, and be surprised by the arbitrary 282 | highlight groups and occasional appearance of forgotten marks. If you want to 283 | automatically restore any marks, set |g:mwAutoLoadMarks|. 284 | 285 | You can also initialize some marks (even using particular highlight groups) to 286 | static values, e.g. by including this in |vimrc|: > 287 | runtime plugin/mark.vim 288 | silent MarkClear 289 | silent 5Mark foo 290 | silent 6Mark /bar/ 291 | Or you can define custom commands that preset certain marks: > 292 | command -bar MyMarks exe '5Mark! foo' | exe '6Mark! /bar/' 293 | Or a command that adds to the existing marks and then toggles them: > 294 | command -bar ToggleFooBarMarks exe 'Mark foo' | exe 'Mark /bar/' 295 | The following commands help with setting these up: 296 | *:MarkYankDefinitions* *:MarkYankDefinitionsOneLiner* 297 | :MarkYankDefinitions [x] 298 | Place definitions for all current marks into the 299 | default register / [x], like this: > 300 | 1Mark! /\/ 301 | 2Mark! /bar/ 302 | 9Mark! /quux/ 303 | :MarkYankDefinitionsOneLiner [x] 304 | Like |:MarkYankDefinitions|, but place all definitions 305 | into a single line, like this: > 306 | exe '1Mark! /\/' | exe '2Mark! /bar/' | exe '9Mark! /quux/' 307 | Alternatively, the mark#GetDefinitionCommands(isOneLiner) function can be used 308 | to obtain a List of |:Mark| commands instead of using a register. With that, 309 | you could for example build a custom alternative to |:MarkSave| that stores 310 | Marks in separate files (using |writefile()|, read by |:source| or even 311 | automatically via a local vimrc plugin) instead of the |viminfo| file. 312 | 313 | MARK INFORMATION *mark-information* 314 | 315 | Both |mark-highlighting| and |mark-searching| commands print information about 316 | the mark and search pattern, e.g. 317 | mark-1/\ ~ 318 | This is especially useful when you want to add or subtract patterns to a mark 319 | highlight group via [N]. 320 | 321 | *:Marks* 322 | :Marks List all mark highlight groups and the search patterns 323 | defined for them. 324 | The group that will be used for the next |:Mark| or 325 | |m| command (without {N}) is shown with a ">". 326 | The last mark used for a search (via *) is 327 | shown with a "/". 328 | 329 | MARK HIGHLIGHTING PALETTES *mark-palette* 330 | 331 | The plugin comes with three predefined palettes: original, extended, and 332 | maximum. You can dynamically toggle between them, e.g. when you need more 333 | marks or a different set of colors. 334 | *:MarkPalette* 335 | :MarkPalette {palette} Highlight existing and future marks with the colors 336 | defined in {palette}. If the new palette contains less 337 | mark groups than the current one, the additional marks 338 | are lost. 339 | You can use |:command-completion| for {palette}. 340 | 341 | See |g:mwDefaultHighlightingPalette| for how to change the default palette, 342 | and |mark-palette-define| for how to add your own custom palettes. 343 | 344 | ============================================================================== 345 | INSTALLATION *mark-installation* 346 | 347 | The code is hosted in a Git repo at 348 | https://github.com/inkarkat/vim-mark 349 | You can use your favorite plugin manager, or "git clone" into a directory used 350 | for Vim |packages|. Releases are on the "stable" branch, the latest unstable 351 | development snapshot on "master". 352 | 353 | This script is also packaged as a |vimball|. If you have the "gunzip" 354 | decompressor in your PATH, simply edit the *.vmb.gz package in Vim; otherwise, 355 | decompress the archive first, e.g. using WinZip. Inside Vim, install by 356 | sourcing the vimball or via the |:UseVimball| command. > 357 | vim mark*.vmb.gz 358 | :so % 359 | To uninstall, use the |:RmVimball| command. 360 | 361 | DEPENDENCIES *mark-dependencies* 362 | 363 | - Requires Vim 7.1 with |matchadd()|, or Vim 7.2 or higher. 364 | - Requires the |ingo-library.vim| plugin (vimscript #4433), version 1.046 or 365 | higher. 366 | 367 | ============================================================================== 368 | CONFIGURATION *mark-configuration* 369 | 370 | For a permanent configuration, put the following commands into your |vimrc|. 371 | 372 | *mark-colors* *mark-highlight-colors* 373 | This plugin defines 6 mark groups: 374 | 1: Cyan 2:Green 3:Yellow 4:Red 5:Magenta 6:Blue ~ 375 | Higher numbers always take precedence and are displayed above lower ones. 376 | 377 | *g:mwDefaultHighlightingPalette* 378 | Especially if you use GVIM, you can switch to a richer palette of up to 18 379 | colors: > 380 | let g:mwDefaultHighlightingPalette = 'extended' 381 | Or, if you have both good eyes and display, you can try a palette that defines 382 | 27, 58, or even 77 colors, depending on the number of available colors: > 383 | let g:mwDefaultHighlightingPalette = 'maximum' 384 | Note: This only works for built-in palettes and those that you define prior to 385 | running the plugin. If you extend the built-ins after plugin initialization 386 | (|mark-palette-define|), use |:MarkPalette| instead. 387 | 388 | If you like the additional colors, but don't need that many of them, restrict 389 | their number via: > 390 | let g:mwDefaultHighlightingNum = 9 391 | < 392 | *mark-colors-redefine* 393 | If none of the default highlightings suits you, define your own colors in your 394 | vimrc file (or anywhere before this plugin is sourced, but after any 395 | |:colorscheme|), in the following form (where N = 1..): > 396 | highlight MarkWordN ctermbg=Cyan ctermfg=Black guibg=#8CCBEA guifg=Black 397 | You can also use this form to redefine only some of the default highlightings. 398 | If you want to avoid losing the highlightings on |:colorscheme| commands, you 399 | need to re-apply your highlights on the |ColorScheme| event, similar to how 400 | this plugin does. Or you define the palette not via :highlight commands, but 401 | use the plugin's infrastructure: > 402 | let g:mwDefaultHighlightingPalette = [ 403 | \ { 'ctermbg':'Cyan', 'ctermfg':'Black', 'guibg':'#8CCBEA', 'guifg':'Black' }, 404 | \ ... 405 | \] 406 | < *mark-palette-define* 407 | If you want to switch multiple palettes during runtime, you need to define 408 | them as proper palettes. 409 | a) To add your palette to the existing ones, do this _after_ the default 410 | palette has been defined (e.g. in ~/.vim/after/plugin/mark.vim): > 411 | if ! exists('g:mwPalettes') " (Optional) guard if the plugin isn't properly installed. 412 | finish 413 | endif 414 | 415 | let g:mwPalettes['mypalette'] = [ 416 | \ { 'ctermbg':'Cyan', 'ctermfg':'Black', 'guibg':'#8CCBEA', 'guifg':'Black' }, 417 | \ ... 418 | \] 419 | let g:mwPalettes['other'] = [ ... ] 420 | 421 | " Make it the default; you cannot use g:mwDefaultHighlightingPalette 422 | here, as the Mark plugin has already been initialized: 423 | MarkPalette mypalette 424 | b) Alternatively, you can completely override all built-in palettes in your 425 | |vimrc|: > 426 | let g:mwPalettes = { 427 | \ 'mypalette': [ 428 | \ { 'ctermbg':'Cyan', 'ctermfg':'Black', 'guibg':'#8CCBEA', 'guifg':'Black' }, 429 | \ ... 430 | \ ] 431 | \} 432 | 433 | " Make it the default: 434 | let g:mwDefaultHighlightingPalette = 'mypalette' 435 | < 436 | The search type highlighting (in the search message) can be changed via: > 437 | highlight link SearchSpecialSearchType MoreMsg 438 | < 439 | *g:mwHistAdd* 440 | By default, any marked words are also added to the search (/) and input (@) 441 | history; if you don't want that, remove the corresponding symbols from: > 442 | let g:mwHistAdd = '/@' 443 | < 444 | *g:mwAutoLoadMarks* 445 | To enable the automatic restore of marks from a previous Vim session: > 446 | let g:mwAutoLoadMarks = 1 447 | < *g:mwAutoSaveMarks* 448 | To turn off the automatic persistence of marks across Vim sessions: > 449 | let g:mwAutoSaveMarks = 0 450 | You can still explicitly save marks via |:MarkSave|. 451 | 452 | *g:mwIgnoreCase* 453 | If you have set 'ignorecase', but want marks to be case-insensitive, you can 454 | override the default behavior of using 'ignorecase' by setting: > 455 | let g:mwIgnoreCase = 0 456 | < 457 | *g:mwExclusionPredicates* 458 | To exclude some tab pages, windows, or buffers / filetypes from showing mark 459 | highlightings (you can still "blindly" navigate to marks in there with the 460 | corresponding mappings), you can define a List of expressions or Funcrefs that 461 | are evaluated in every window; if one returns 1, the window will not show 462 | marks. > 463 | " Don't mark temp files, Python filetype, and scratch files as defined by 464 | " a custom function. 465 | let g:mwExclusionPredicates = 466 | \ ['expand("%:p") =~# "/tmp"', '&filetype == "python", function('ExcludeScratchFiles')] 467 | < *t:nomarks* *w:nomarks* *b:nomarks* 468 | By default, tab pages / windows / buffers that have t:nomarks / w:nomarks / 469 | b:nomarks with a true value are excluded. Therefore, to suppress mark 470 | highlighting in a buffer, you can simply > 471 | :let b:nomarks = 1 472 | If the predicate changes after a window has already been visible, you can 473 | update the mark highlighting by either: 474 | - switching tab pages back and forth 475 | - toggling marks on / off (via MarkToggle) 476 | - :call mark#UpdateMark() (for current buffer) 477 | - :call mark#UpdateScope() (for all windows in the current tab page) 478 | 479 | *g:mwMaxMatchPriority* 480 | This plugin uses |matchadd()| for the highlightings. Each mark group has its 481 | own priority, with higher group values having higher priority; i.e. going "on 482 | top". The maximum priority (used for the last defined mark group) can be 483 | changed via: > 484 | let g:mwMaxMatchPriority = -10 485 | For example when another plugin or customization also uses matches and you 486 | would like to change their relative priorities. The default is negative to 487 | step back behind the default search highlighting. 488 | 489 | 490 | *g:mw_no_mappings* 491 | If you want no or only a few of the available mappings, you can completely 492 | turn off the creation of the default mappings by defining (before the plugin 493 | is loaded): > 494 | :let g:mw_no_mappings = 1 495 | This saves you from mapping dummy keys to all unwanted mapping targets. 496 | *mark-mappings* *mark-remap* 497 | If you want to use different mappings, map your keys to the Mark... 498 | mapping targets _before_ sourcing the script (e.g. in your |vimrc|): > 499 | nmap m MarkSet 500 | nmap gm MarkPartialWord 501 | xmap m MarkSet 502 | nmap r MarkRegex 503 | xmap r MarkRegex 504 | nmap n MarkClear 505 | nmap * MarkSearchCurrentNext 506 | nmap # MarkSearchCurrentPrev 507 | nmap / MarkSearchAnyNext 508 | nmap ? MarkSearchAnyPrev 509 | nmap * MarkSearchNext 510 | nmap # MarkSearchPrev 511 | < 512 | There are no default mappings for toggling all marks and for the |:MarkClear| 513 | command, but you can define some yourself: > 514 | nmap M MarkToggle 515 | nmap N MarkAllClear 516 | As the latter is irreversible, there's also an alternative with an additional 517 | confirmation: > 518 | nmap N MarkConfirmAllClear 519 | < 520 | To remove the default overriding of * and #, use: > 521 | nmap IgnoreMarkSearchNext MarkSearchNext 522 | nmap IgnoreMarkSearchPrev MarkSearchPrev 523 | < 524 | If you don't want the * and # mappings remember the last search type and 525 | instead always search for the next occurrence of the current mark, with a 526 | fallback to Vim's original * command, use: > 527 | nmap * MarkSearchOrCurNext 528 | nmap # MarkSearchOrCurPrev 529 | Or for search for the next occurrence of any mark with fallback to *: > 530 | nmap * MarkSearchOrAnyNext 531 | nmap # MarkSearchOrAnyPrev 532 | < 533 | Mark searches could also be combined with the built-in search. This mapping 534 | overloads the default |n|/|N| commands to search for any mark if there is any 535 | mark defined and marks are enabled, and fall back to the default search if 536 | not: > 537 | nmap n MarkSearchAnyOrDefaultNext 538 | nmap N MarkSearchAnyOrDefaultPrev 539 | < 540 | The search mappings (*, #, etc.) interpret [count] as the number of 541 | occurrences to jump over. If you don't want to use the separate 542 | |mark-keypad-searching| mappings, and rather want [count] select the highlight 543 | group to target (and you can live with jumps restricted to the very next 544 | match), (re-)define to these mapping targets: > 545 | nmap * MarkSearchGroupNext 546 | nmap # MarkSearchGroupPrev 547 | < 548 | You can remap the direct group searches (by default via the keypad 1-9 keys): > 549 | nmap 1 MarkSearchGroup1Next 550 | nmap ! MarkSearchGroup1Prev 551 | < *g:mwDirectGroupJumpMappingNum* 552 | If you need more / less groups, this can be configured via: > 553 | let g:mwDirectGroupJumpMappingNum = 20 554 | Set to 0 to completely turn off the keypad mappings. This is easier than 555 | remapping all -mappings. 556 | *mark-group-cycle* 557 | As an alternative to the direct group searches, you can also define mappings 558 | that search a next / previous used group: > 559 | nmap +* MarkSearchUsedGroupNext 560 | nmap -* MarkSearchUsedGroupPrev 561 | < 562 | *mark-whitespace-indifferent* 563 | Some people like to create a mark based on the visual selection, like 564 | |v_m|, but have whitespace in the selection match any whitespace when 565 | searching (searching for "hello world" will also find "helloworld" as 566 | well as "hello" at the end of a line, with "world" at the start of the next 567 | line). The Vim Tips Wiki describes such a setup for the built-in search at 568 | http://vim.wikia.com/wiki/Search_for_visually_selected_text 569 | You can achieve the same with the Mark plugin through the MarkIWhiteSet 570 | mapping target: Using this, you can assign a new visual mode mapping * > 571 | xmap * MarkIWhiteSet 572 | or override the default |v_m| mapping, in case you always want this 573 | behavior: > 574 | vmap IgnoreMarkSet MarkSet 575 | xmap m MarkIWhiteSet 576 | < 577 | ============================================================================== 578 | INTEGRATION *mark-integration* 579 | 580 | The following functions offer (read-only) access to the script's internals: 581 | - mark#GetGroupNum(): number of available groups 582 | - mark#GetCount(): number of defined marks 583 | - mark#GetPattern([{index}]): search regular expression for an individual mark 584 | - mark#GetMarkNumber({pattern}, {isLiteral}, {isConsiderAlternatives}): mark 585 | number of a pattern / literal text 586 | 587 | ============================================================================== 588 | LIMITATIONS *mark-limitations* 589 | 590 | - If the 'ignorecase' setting is changed, there will be discrepancies between 591 | the highlighted marks and subsequent jumps to marks. 592 | - If {pattern} in a :Mark command contains atoms that change the semantics of 593 | the entire (|/\c|, |/\C|) regular expression, there may be discrepancies 594 | between the highlighted marks and subsequent jumps to marks. 595 | 596 | KNOWN PROBLEMS *mark-known-problems* 597 | 598 | TODO *mark-todo* 599 | 600 | IDEAS *mark-ideas* 601 | 602 | CONTRIBUTING *mark-contribute* 603 | 604 | Report any bugs, send patches, or suggest features via the issue tracker at 605 | https://github.com/inkarkat/vim-mark/issues or email (address below). 606 | 607 | ============================================================================== 608 | HISTORY *mark-history* 609 | 610 | 3.4.0 RELEASEME 611 | - ENH: Support mark persistence to sessions created via :mksession, too. 612 | *** You need to update to ingo-library (vimscript #4433) version 1.047! *** 613 | 614 | 3.3.0 17-Jan-2025 615 | - Expose mark#mark#AnyMarkPattern(). 616 | - Robustness: Place the ColorScheme initialization also in the 617 | MarkInitialization autocommand group. 618 | - Robustness: Add check for existence and compatible version of ingo-library. 619 | - ENH: Add gm mapping for non-whole word matching like |gstar|. 620 | Contributed by Carl Smith. 621 | *** You need to update to ingo-library (vimscript #4433) version 1.046! *** 622 | 623 | 3.2.0 15-Feb-2022 624 | - Add mark#GetMarkNumber(), based on feedback by Snorch in #36. 625 | - Mark updates across windows now use win_execute() (since Vim 8.1.1418) 626 | instead of :windo. This hopefully addresses the changes in window sizes that 627 | have been reported (e.g. in #34). 628 | - Add MarkSearchAnyOrDefaultNext and MarkSearchAnyOrDefaultPrev 629 | for an any-mark search with fallback to the built-in search pattern. 630 | Suggested by Denis Kasak. 631 | *** You need to update to ingo-library (vimscript #4433) version 1.043! *** 632 | 633 | 3.1.1 03-Aug-2020 634 | - Compatibility: After Vim 8.1.1241, a :range outside the number of buffers 635 | (e.g. :99Mark[Name]) causes an error. 636 | - ENH: Add (GUI-only) additional palettes "soft" and "softer" that are 637 | variants of "extended" with less saturation / higher brightness of 638 | background colors (for when the default colors are too distracting). 639 | - ENH: Marks that cover multiple lines (created through a visual selection or 640 | |:Mark| /{pattern}/) now also can be jumped to when the cursor is not on the 641 | mark's first line. 642 | *** You need to update to ingo-library (vimscript #4433) version 1.042! *** 643 | 644 | 3.1.0 23-Mar-2019 645 | - ENH: Handle magicness atoms (\V, \m) in regexps entered via r or 646 | :Mark /{pattern}/. 647 | - ENH: Choose a more correct insertion point with multiple alternatives for a 648 | mark by projecting the length of the existing and alternatives and the added 649 | pattern. 650 | - BUG: Regression: n without {N} and not on an existing mark prints 651 | error "Do not pass empty pattern to disable all marks". 652 | - ENH: Allow to exclude certain tab pages, windows, or buffers / filetypes 653 | from showing mark highlightings via g:mwExclusionPredicates or (with the 654 | default predicate) t:nomarks / w:nomarks / b:nomarks flags. 655 | - ENH: Allow to tweak the maximum match priority via g:mwMaxMatchPriority for 656 | better coexistence with other customizations that use :match / matchadd(). 657 | - ENH: Allow to disable all default mappings via a single g:mw_no_mappings 658 | configuration flag. 659 | - ENH: Appended (strong) green and yellow highlightings to the extended 660 | palette. 661 | - Refactoring: Move mark persistence implementation to ingo-library. No need 662 | to serialize into String type for viminfo beyond Vim 7.3.030. 663 | - BUG: Avoid creating jump when updating marks. Need to use :keepjumps windo. 664 | Reported by epheien. 665 | *** You need to update to ingo-library (vimscript #4433) version 1.036! *** 666 | 667 | 3.0.0 18-Sep-2017 668 | - CHG: Parse :Mark arguments as either /{pattern}/ or whole {word}. This 669 | better addresses the common use case of searching for whole words, and is 670 | consistent with built-in commands like :djump. 671 | - ENH: Keep previous (last accessed) window on :windo. 672 | - Consistently use :noautocmd during window iteration. 673 | - ENH: Add :MarkYankDefinitions and :MarkYankDefinitionsOneLiner commands. 674 | These make it easier to persist marks for specific files (e.g. by putting 675 | the :Mark commands into a local vimrc) or occasions (by defining a custom 676 | command or mapping with these commands), and are an alternative to 677 | :MarkSave/Load. 678 | - ENH: Add MarkSearchUsedGroupNext and MarkSearchUsedGroupPrev to 679 | search in a next / previous used group. Suggested by Louis Pan. 680 | - ENH: Add MarkSearchCascadeStartWithStop, 681 | MarkSearchCascadeNextWithStop, MarkSearchCascadeStartNoStop, 682 | MarkSearchCascadeNextNoStop to search in cascading mark groups, i.e. 683 | first all matches for group 1, then all for group 2, and so on. 684 | - CHG: Duplicate mark#GetNum() and mark#GetGroupNum(). Rename the former into 685 | mark#GetCount() and have it return the number of actually defined (i.e. 686 | non-empty) marks. 687 | - ENH: Allow to give names to mark groups via :MarkName and new :Mark 688 | /{pattern}/ as {name} command syntax. Names will be shown during searching, 689 | and persisted together with the marks. This makes it easier to handle 690 | several marks and enforce custom semantics for particular groups. 691 | - Properly abort on error by using :echoerr. 692 | - Add dependency to ingo-library (vimscript #4433). *** You need to separately 693 | install ingo-library (vimscript #4433) version 1.020 (or higher)! *** 694 | 695 | 2.8.5 29-Oct-2014 696 | - ENH: Add alternative MarkConfirmAllClear optional command that works 697 | like MarkAllClear, but with confirmation. Thanks to Marcelo Montu for 698 | suggesting this! 699 | 700 | 2.8.4 19-Jun-2014 701 | - To avoid accepting an invalid regular expression (e.g. "\(blah") and then 702 | causing ugly errors on every mark update, check the patterns passed by the 703 | user for validity. 704 | - CHG: The :Mark command doesn't query for a mark when the passed mark group 705 | doesn't exist (interactivity in Ex commands is unexpected). Instead, it 706 | returns an error. 707 | 708 | 2.8.3 23-May-2014 709 | - The additional mapping described under :help mark-whitespace-indifferent got 710 | broken again by the refactoring of mark#DoMark() on 31-Jan-2013. Finally 711 | include this in the script as MarkIWhiteSet and 712 | mark#GetVisualSelectionAsLiteralWhitespaceIndifferentPattern(). Thanks to 713 | Greg Klein for noticing and prodding me to include it. 714 | 715 | 2.8.2 16-Dec-2013 716 | - BUG: :Mark cannot highlight patterns starting with a number. Use -range=0 717 | instead of -count. Thanks to Vladimir Marek for reporting this. 718 | 719 | 2.8.1 22-Nov-2013 720 | - Allow to override the adding to existing marks via :[N]Mark! {pattern}. 721 | - ENH: Implement command completion for :[N]Mark that offers existing mark 722 | patterns (from group [N] / all groups), both as one regular expression and 723 | individual alternatives. The leading \< can be omitted. 724 | 725 | 2.8.0 01-Jun-2013 726 | - Also allow a [count] for r to select (or query for) a mark group, as 727 | with m. 728 | - CHG: Also set the current mark to the used mark group when a mark was set 729 | via r and :Mark so that it is easier to determine whether the 730 | entered pattern actually matches anywhere. Thanks to Xiaopan Zhang for 731 | notifying me about this problem. 732 | - Add MarkSearchGroupNext / MarkSearchGroupPrev to enable 733 | searching for particular mark groups. Thanks to Xiaopan Zhang for the 734 | suggestion. 735 | - Define default mappings for keys 1-9 on the numerical keypad to jump to a 736 | particular group (backwards with ). Their definition is controlled by 737 | the new g:mwDirectGroupJumpMappingNum variable. 738 | - ENH: Allow to store an arbitrary number of marks via named slots that can 739 | optionally be passed to :MarkLoad / :MarkSave. If the slot is all-uppercase, 740 | the marks will also be persisted across Vim invocations. 741 | 742 | 2.7.2 15-Oct-2012 743 | - Issue an error message "No marks defined" instead of moving the cursor by 744 | one character when there are no marks (e.g. initially or after :MarkClear). 745 | - Enable custom integrations via new mark#GetNum() and mark#GetPattern() 746 | functions. 747 | 748 | 2.7.1 14-Sep-2012 749 | - Enable alternative * / # mappings that do not remember the last search type 750 | through new MarkSearchOrCurNext, MarkSearchOrCurPrev, 751 | MarkSearchOrAnyNext, MarkSearchOrAnyPrev mappings. Based on an 752 | inquiry from Kevin Huanpeng Du. 753 | 754 | 2.7.0 04-Jul-2012 755 | - ENH: Implement :MarkPalette command to switch mark highlighting on-the-fly 756 | during runtime. 757 | - Add "maximum" palette contributed by rockybalboa4. 758 | 759 | 2.6.5 24-Jun-2012 760 | - Don't define the default m and r mappings in select mode, 761 | just visual mode. Thanks to rockybalboa4 for pointing this out. 762 | 763 | 2.6.4 23-Apr-2012 764 | - Allow to override 'ignorecase' setting via g:mwIgnoreCase. Thanks to fanhe 765 | for the idea and sending a patch. 766 | 767 | 2.6.3 27-Mar-2012 768 | - ENH: Allow choosing of palette and limiting of default mark highlight groups 769 | via g:mwDefaultHighlightingPalette and g:mwDefaultHighlightingNum. 770 | - ENH: Offer an extended color palette in addition to the original 6-color one. 771 | Enable this via :let g:mwDefaultHighlightingPalette = "extended" in your 772 | vimrc. 773 | 774 | 2.6.2 26-Mar-2012 775 | - ENH: When a [count] exceeding the number of available mark groups is given, 776 | a summary of marks is given and the user is asked to select a mark group. 777 | This allows to interactively choose a color via 99m. 778 | If you use the |mark-whitespace-indifferent| mappings, *** PLEASE UPDATE THE 779 | vnoremap MarkWhitespaceIndifferent DEFINITION *** 780 | - ENH: Include count of alternative patterns in :Marks list. 781 | - CHG: Use ">" for next mark and "/" for last search in :Marks. 782 | 783 | 2.6.1 23-Mar-2012 784 | - ENH: Add :Marks command that prints all mark highlight groups and their 785 | search patterns, plus information about the current search mark, next mark 786 | group, and whether marks are disabled. 787 | - ENH: Show which mark group a pattern was set / added / removed / cleared. 788 | - FIX: When the cursor is positioned on the current mark, [N]n / 789 | MarkClear with [N] appended the pattern for the current mark (again 790 | and again) instead of clearing it. Must not pass current mark pattern when 791 | [N] is given. 792 | - CHG: Show mark group number in same-mark search and rename search types from 793 | "any-mark", "same-mark", and "new-mark" to the shorter "mark-*", "mark-N", 794 | and "mark-N!", respectively. 795 | 796 | 2.6.0 22-Mar-2012 797 | - ENH: Allow [count] for m and :Mark to add / subtract match to / from 798 | highlight group [count], and use [count]n to clear only highlight 799 | group [count]. This was also requested by Philipp Marek. 800 | - FIX: :Mark and n actually toggled marks back on when they were 801 | already off. Now, they stay off on multiple invocations. Use :call 802 | mark#Toggle() / MarkToggle if you want toggling. 803 | 804 | 2.5.3 02-Mar-2012 805 | - BUG: Version check mistakenly excluded Vim 7.1 versions that do have the 806 | matchadd() function. Thanks to Philipp Marek for sending a patch. 807 | 808 | 2.5.2 09-Nov-2011 809 | Fixed various problems with wrap-around warnings: 810 | - BUG: With a single match and 'wrapscan' set, a search error was issued. 811 | - FIX: Backwards search with single match leads to wrong error message 812 | instead. 813 | - FIX: Wrong logic for determining l:isWrapped lets wrap-around go undetected. 814 | 815 | 2.5.1 17-May-2011 816 | - FIX: == comparison in s:DoMark() leads to wrong regexp (\A vs. \a) being 817 | cleared when 'ignorecase' is set. Use case-sensitive comparison ==# instead. 818 | - Refine :MarkLoad messages 819 | - Add whitespace-indifferent visual mark configuration example. Thanks to Greg 820 | Klein for the suggestion. 821 | 822 | 2.5.0 07-May-2011 823 | - ENH: Add explicit mark persistence via :MarkLoad and :MarkSave commands and 824 | automatic persistence via the g:mwAutoLoadMarks and g:mwAutoSaveMarks 825 | configuration flags. (Request from Mun Johl, 16-Apr-2010) 826 | - Expose toggling of mark display (keeping the mark patterns) via new 827 | MarkToggle mapping. Offer :MarkClear command as a replacement for the 828 | old argumentless :Mark command, which now just disables, but not clears all 829 | marks. 830 | 831 | 2.4.4 18-Apr-2011 832 | - BUG: Include trailing newline character in check for current mark, so that a 833 | mark that matches the entire line (e.g. created by Vm) can be 834 | cleared via n. Thanks to ping for reporting this. 835 | - FIX: On overlapping marks, mark#CurrentMark() returned the lowest, not the 836 | highest visible mark. So on overlapping marks, the one that was not visible 837 | at the cursor position was removed; very confusing! Use reverse iteration 838 | order. 839 | - FIX: To avoid an arbitrary ordering of highlightings when the highlighting 840 | group names roll over, and to avoid order inconsistencies across different 841 | windows and tabs, we assign a different priority based on the highlighting 842 | group. 843 | 844 | 2.4.3 16-Apr-2011 845 | - Avoid losing the mark highlightings on :syn on or :colorscheme commands. 846 | Thanks to Zhou YiChao for alerting me to this issue and suggesting a fix. 847 | - Made the script more robust when somehow no highlightings have been defined 848 | or when the window-local reckoning of match IDs got lost. I had very 849 | occasionally encountered such script errors in the past. 850 | - Made global housekeeping variables script-local, only g:mwHistAdd is used 851 | for configuration. 852 | 853 | 2.4.2 14-Jan-2011 (unreleased) 854 | - FIX: Capturing the visual selection could still clobber the blockwise yank 855 | mode of the unnamed register. 856 | 857 | 2.4.1 13-Jan-2011 858 | - FIX: Using a named register for capturing the visual selection on 859 | {Visual}m and {Visual}r clobbered the unnamed register. Now 860 | using the unnamed register. 861 | 862 | 2.4.0 13-Jul-2010 863 | - ENH: The MarkSearch mappings ([*#/?]) add the original cursor 864 | position to the jump list, like the built-in [/?*#nN] commands. This allows 865 | to use the regular jump commands for mark matches, like with regular search 866 | matches. 867 | 868 | 2.3.3 19-Feb-2010 869 | - BUG: Clearing of an accidental zero-width match (e.g. via :Mark \zs) results 870 | in endless loop. Thanks to Andy Wokula for the patch. 871 | 872 | 2.3.2 17-Nov-2009 873 | - BUG: Creation of literal pattern via '\V' in {Visual}m mapping 874 | collided with individual escaping done in m mapping so that an 875 | escaped '\*' would be interpreted as a multi item when both modes are used 876 | for marking. Thanks to Andy Wokula for the patch. 877 | 878 | 2.3.1 06-Jul-2009 879 | - Now working correctly when 'smartcase' is set. All mappings and the :Mark 880 | command use 'ignorecase', but not 'smartcase'. 881 | 882 | 2.3.0 04-Jul-2009 883 | - All jump commands now take an optional [count], so you can quickly skip over 884 | some marks, as with the built-in */# and n/N commands. For this, the entire 885 | core search algorithm has been rewritten. The script's logic has been 886 | simplified through the use of Vim 7 features like Lists. 887 | - Now also printing a Vim-alike search error message when 'nowrapscan' is set. 888 | 889 | 2.2.0 02-Jul-2009 890 | - Split off functions into autoload script. 891 | - Initialization of global variables and autocommands is now done lazily on 892 | the first use, not during loading of the plugin. This reduces Vim startup 893 | time and footprint as long as the functionality isn't yet used. 894 | - Split off documentation into separate help file. Now packaging as VimBall. 895 | 896 | 897 | 2.1.0 06-Jun-2009 898 | - Replaced highlighting via :syntax with matchadd() / matchdelete(). This 899 | requires Vim 7.2 / 7.1 with patches. This method is faster, there are no 900 | more clashes with syntax highlighting (:match always has preference), and 901 | the background highlighting does not disappear under 'cursorline'. 902 | - Using winrestcmd() to fix effects of :windo: By entering a window, its 903 | height is potentially increased from 0 to 1. 904 | - Handling multiple tabs by calling s:UpdateScope() on the TabEnter event. 905 | 906 | 2.0.0 01-Jun-2009 907 | - Now using Vim List for g:mwWord and thus requiring Vim 7. g:mwCycle is now 908 | zero-based, but the syntax groups "MarkWordx" are still one-based. 909 | - Factored :syntax operations out of s:DoMark() and s:UpdateMark() so that 910 | they can all be done in a single :windo. 911 | - Normal mode MarkSet now has the same semantics as its visual mode 912 | cousin: If the cursor is on an existing mark, the mark is removed. 913 | Beforehand, one could only remove a visually selected mark via again 914 | selecting it. Now, one simply can invoke the mapping when on such a mark. 915 | 916 | 1.6.1 31-May-2009 917 | Publication of improved version by Ingo Karkat. 918 | - Now prepending search type ("any-mark", "same-mark", "new-mark") for better 919 | identification. 920 | - Retired the algorithm in s:PrevWord in favor of simply using , which 921 | makes mark.vim work like the * command. At the end of a line, non-keyword 922 | characters may now be marked; the previous algorithm preferred any preceding 923 | word. 924 | - BF: If 'iskeyword' contains characters that have a special meaning in a 925 | regexp (e.g. [.*]), these are now escaped properly. 926 | - Highlighting can now actually be overridden in the vimrc (anywhere _before_ 927 | sourcing this script) by using ':hi def'. 928 | - Added missing setter for re-inclusion guard. 929 | 930 | 1.5.0 01-Sep-2008 931 | Bug fixes and enhancements by Ingo Karkat. 932 | - Added MarkAllClear (without a default mapping), which clears all 933 | marks, even when the cursor is on a mark. 934 | - Added ... mappings for hard-coded \*, \#, \/, \?, * and #, to allow 935 | re-mapping and disabling. Beforehand, there were some ... mappings 936 | and hard-coded ones; now, everything can be customized. 937 | - BF: Using :autocmd without to avoid removing _all_ autocmds for the 938 | BufWinEnter event. (Using a custom :augroup would be even better.) 939 | - BF: Explicitly defining s:current_mark_position; some execution paths left 940 | it undefined, causing errors. 941 | - ENH: Make the match according to the 'ignorecase' setting, like the star 942 | command. 943 | - ENH: The jumps to the next/prev occurrence now print 'search hit BOTTOM, 944 | continuing at TOP" and "Pattern not found:..." messages, like the * and n/N 945 | Vim search commands. 946 | - ENH: Jumps now open folds if the occurrence is inside a closed fold, just 947 | like n/N do. 948 | 949 | 1.1.8-g 25-Apr-2008 950 | Last version published by Yuheng Xie on vim.org. 951 | 952 | 1.1.2 22-Mar-2005 953 | Initial version published by Yuheng Xie on vim.org. 954 | 955 | ============================================================================== 956 | Copyright: (C) 2008-2025 Ingo Karkat 957 | (C) 2005-2008 Yuheng Xie 958 | The VIM LICENSE applies to this plugin; see |copyright|. 959 | 960 | Maintainer: Ingo Karkat 961 | ============================================================================== 962 | vim:tw=78:ts=8:ft=help:norl: 963 | -------------------------------------------------------------------------------- /mark.manifest: -------------------------------------------------------------------------------- 1 | autoload/mark.vim 2 | autoload/mark/cascade.vim 3 | autoload/mark/early.vim 4 | autoload/mark/palettes.vim 5 | plugin/mark.vim 6 | doc/mark.txt 7 | -------------------------------------------------------------------------------- /plugin/mark.vim: -------------------------------------------------------------------------------- 1 | " Script Name: mark.vim 2 | " Description: Highlight several words in different colors simultaneously. 3 | " 4 | " Copyright: (C) 2008-2025 Ingo Karkat 5 | " (C) 2005-2008 Yuheng Xie 6 | " The VIM LICENSE applies to this script; see ':help copyright'. 7 | " 8 | " Maintainer: Ingo Karkat 9 | " Orig Author: Yuheng Xie 10 | " Contributors:Luc Hermitte, Ingo Karkat 11 | " 12 | " DEPENDENCIES: 13 | " - Requires Vim 7.1 with "matchadd()", or Vim 7.2 or higher. 14 | " - ingo-library.vim plugin 15 | " 16 | " Version: 3.4.0 17 | 18 | " Avoid installing twice or when in unsupported Vim version. 19 | if exists('g:loaded_mark') || (v:version == 701 && ! exists('*matchadd')) || (v:version < 701) 20 | finish 21 | endif 22 | let g:loaded_mark = 1 23 | let s:save_cpo = &cpo 24 | set cpo&vim 25 | 26 | "- configuration -------------------------------------------------------------- 27 | 28 | if ! exists('g:mwHistAdd') 29 | let g:mwHistAdd = '/@' 30 | endif 31 | 32 | if ! exists('g:mwAutoLoadMarks') 33 | let g:mwAutoLoadMarks = 0 34 | endif 35 | 36 | if ! exists('g:mwAutoSaveMarks') 37 | let g:mwAutoSaveMarks = 1 38 | endif 39 | 40 | if ! exists('g:mwDefaultHighlightingNum') 41 | let g:mwDefaultHighlightingNum = -1 42 | endif 43 | if ! exists('g:mwDefaultHighlightingPalette') 44 | let g:mwDefaultHighlightingPalette = 'original' 45 | endif 46 | if ! exists('g:mwPalettes') 47 | let g:mwPalettes = { 48 | \ 'original': [ 49 | \ { 'ctermbg':'Cyan', 'ctermfg':'Black', 'guibg':'#8CCBEA', 'guifg':'Black' }, 50 | \ { 'ctermbg':'Green', 'ctermfg':'Black', 'guibg':'#A4E57E', 'guifg':'Black' }, 51 | \ { 'ctermbg':'Yellow', 'ctermfg':'Black', 'guibg':'#FFDB72', 'guifg':'Black' }, 52 | \ { 'ctermbg':'Red', 'ctermfg':'Black', 'guibg':'#FF7272', 'guifg':'Black' }, 53 | \ { 'ctermbg':'Magenta', 'ctermfg':'Black', 'guibg':'#FFB3FF', 'guifg':'Black' }, 54 | \ { 'ctermbg':'Blue', 'ctermfg':'Black', 'guibg':'#9999FF', 'guifg':'Black' }, 55 | \], 56 | \ 'extended': function('mark#palettes#Extended'), 57 | \ 'maximum': function('mark#palettes#Maximum') 58 | \} 59 | if has('gui_running') 60 | call extend(g:mwPalettes, { 61 | \ 'soft': function('mark#palettes#Soft'), 62 | \ 'softer': function('mark#palettes#Softer'), 63 | \}) 64 | endif 65 | endif 66 | 67 | if ! exists('g:mwDirectGroupJumpMappingNum') 68 | let g:mwDirectGroupJumpMappingNum = 9 69 | endif 70 | 71 | if ! exists('g:mwExclusionPredicates') 72 | let g:mwExclusionPredicates = (v:version == 702 && has('patch61') || v:version > 702 ? [function('mark#DefaultExclusionPredicate')] : []) 73 | endif 74 | 75 | if ! exists('g:mwMaxMatchPriority') 76 | " Default the highest match priority to -10, so that we do not override the 77 | " 'hlsearch' of 0, and still allow other custom highlightings to sneak in 78 | " between. 79 | let g:mwMaxMatchPriority = -10 80 | endif 81 | 82 | 83 | "- default highlightings ------------------------------------------------------ 84 | 85 | function! s:GetPalette() 86 | let l:palette = [] 87 | if type(g:mwDefaultHighlightingPalette) == type([]) 88 | " There are custom color definitions, not a named built-in palette. 89 | return g:mwDefaultHighlightingPalette 90 | endif 91 | if ! has_key(g:mwPalettes, g:mwDefaultHighlightingPalette) 92 | if ! empty(g:mwDefaultHighlightingPalette) 93 | call ingo#msg#WarningMsg('Mark: Unknown value for g:mwDefaultHighlightingPalette: ' . g:mwDefaultHighlightingPalette) 94 | endif 95 | 96 | return [] 97 | endif 98 | 99 | if type(g:mwPalettes[g:mwDefaultHighlightingPalette]) == type([]) 100 | return g:mwPalettes[g:mwDefaultHighlightingPalette] 101 | elseif type(g:mwPalettes[g:mwDefaultHighlightingPalette]) == type(function('tr')) 102 | return call(g:mwPalettes[g:mwDefaultHighlightingPalette], []) 103 | else 104 | call ingo#msg#ErrorMsg(printf('Mark: Invalid value type for g:mwPalettes[%s]', g:mwDefaultHighlightingPalette)) 105 | return [] 106 | endif 107 | endfunction 108 | function! s:DefineHighlightings( palette, isOverride ) 109 | let l:command = (a:isOverride ? 'highlight' : 'highlight def') 110 | let l:highlightingNum = (g:mwDefaultHighlightingNum == -1 ? len(a:palette) : g:mwDefaultHighlightingNum) 111 | for i in range(1, l:highlightingNum) 112 | execute l:command 'MarkWord' . i join(map(items(a:palette[i - 1]), 'join(v:val, "=")')) 113 | endfor 114 | return l:highlightingNum 115 | endfunction 116 | call s:DefineHighlightings(s:GetPalette(), 0) 117 | augroup MarkInitialization 118 | autocmd! 119 | autocmd ColorScheme * call DefineHighlightings(GetPalette(), 0) 120 | augroup END 121 | 122 | " Default highlighting for the special search type. 123 | " You can override this by defining / linking the 'SearchSpecialSearchType' 124 | " highlight group before this script is sourced. 125 | highlight def link SearchSpecialSearchType MoreMsg 126 | 127 | 128 | 129 | "- marks persistence ---------------------------------------------------------- 130 | 131 | if g:mwAutoLoadMarks 132 | " As the viminfo is only processed after sourcing of the runtime files, the 133 | " persistent global variables are not yet available here. Defer this until Vim 134 | " startup has completed. 135 | function! s:AutoLoadMarks( marksVariable ) 136 | if g:mwAutoLoadMarks && exists('g:' . a:marksVariable) && ! empty(ingo#plugin#persistence#Load(a:marksVariable, [])) 137 | " Note: Avoid triggering the autoload unless there actually are persistent 138 | " marks. 139 | if ! exists('g:MARK_ENABLED') || g:MARK_ENABLED 140 | " There are persistent marks and they haven't been disabled; we need to 141 | " show them right now. 142 | call mark#LoadCommand(0, a:marksVariable) 143 | else 144 | " Though there are persistent marks, they have been disabled. We avoid 145 | " sourcing the autoload script and its invasive autocmds right now; 146 | " maybe the marks are never turned on. We just inform the autoload 147 | " script that it should do this once it is sourced on-demand by a 148 | " mark mapping or command. 149 | let g:mwDoDeferredLoad = 1 150 | endif 151 | endif 152 | endfunction 153 | 154 | augroup MarkInitialization 155 | autocmd! VimEnter * call AutoLoadMarks(mark#early#GetMarksVariable()) 156 | autocmd! SessionLoadPost * call AutoLoadMarks('MARK_marks') 157 | augroup END 158 | endif 159 | 160 | 161 | 162 | "- commands ------------------------------------------------------------------- 163 | 164 | let s:hasOtherArgumentAddressing = v:version == 801 && has('patch560') || v:version > 801 165 | 166 | if s:hasOtherArgumentAddressing 167 | command! -bang -range=0 -addr=other -nargs=? -complete=customlist,mark#Complete Mark if 0 | silent call mark#DoMark(, '') | endif | if ! mark#SetMark(, )[0] | echoerr ingo#err#Get() | endif 168 | else 169 | command! -bang -range=0 -nargs=? -complete=customlist,mark#Complete Mark if 0 | silent call mark#DoMark(, '') | endif | if ! mark#SetMark(, )[0] | echoerr ingo#err#Get() | endif 170 | endif 171 | command! -bar MarkClear call mark#ClearAll() 172 | command! -bar Marks call mark#List() 173 | 174 | command! -bar -nargs=? -complete=customlist,mark#MarksVariablesComplete MarkLoad if ! mark#LoadCommand(1, mark#early#GetMarksVariable()) | echoerr ingo#err#Get() | endif 175 | command! -bar -nargs=? -complete=customlist,mark#MarksVariablesComplete MarkSave if ! mark#SaveCommand() | echoerr ingo#err#Get() | endif 176 | command! -bar -register MarkYankDefinitions if ! mark#YankDefinitions(0, ) | echoerr ingo#err#Get()| endif 177 | command! -bar -register MarkYankDefinitionsOneLiner if ! mark#YankDefinitions(1, ) | echoerr ingo#err#Get()| endif 178 | function! s:SetPalette( paletteName ) 179 | if type(g:mwDefaultHighlightingPalette) == type([]) 180 | " Convert the directly defined list to a palette named "default". 181 | let g:mwPalettes['default'] = g:mwDefaultHighlightingPalette 182 | unlet! g:mwDefaultHighlightingPalette " Avoid E706. 183 | endif 184 | let g:mwDefaultHighlightingPalette = a:paletteName 185 | 186 | let l:palette = s:GetPalette() 187 | if empty(l:palette) 188 | return 189 | endif 190 | 191 | call mark#ReInit(s:DefineHighlightings(l:palette, 1)) 192 | call mark#UpdateScope() 193 | endfunction 194 | function! s:MarkPaletteComplete( ArgLead, CmdLine, CursorPos ) 195 | return sort(filter(keys(g:mwPalettes), 'v:val =~ ''\V\^'' . escape(a:ArgLead, "\\")')) 196 | endfunction 197 | command! -bar -nargs=1 -complete=customlist,MarkPaletteComplete MarkPalette call SetPalette() 198 | if s:hasOtherArgumentAddressing 199 | command! -bar -bang -range=0 -addr=other -nargs=? MarkName if ! mark#SetName(0, , ) | echoerr ingo#err#Get() | endif 200 | else 201 | command! -bar -bang -range=0 -nargs=? MarkName if ! mark#SetName(0, , ) | echoerr ingo#err#Get() | endif 202 | endif 203 | 204 | unlet s:hasOtherArgumentAddressing 205 | 206 | 207 | 208 | "- mappings ------------------------------------------------------------------- 209 | 210 | nnoremap MarkSet :if ! mark#MarkCurrentWord(v:count)execute "normal! \C-\>\C-n>\Esc>"endif 211 | nnoremap MarkPartialWord :if ! mark#MarkCurrentWord(v:count, 0)execute "normal! \C-\>\C-n>\Esc>"endif 212 | vnoremap MarkSet :if ! mark#DoMark(v:count, mark#GetVisualSelectionAsLiteralPattern())[0]execute "normal! \C-\>\C-n>\Esc>"endif 213 | vnoremap MarkIWhiteSet :if ! mark#DoMark(v:count, mark#GetVisualSelectionAsLiteralWhitespaceIndifferentPattern())[0]execute "normal! \C-\>\C-n>\Esc>"endif 214 | nnoremap MarkRegex :if ! mark#MarkRegex(v:count, '')execute "normal! \C-\>\C-n>\Esc>"echoerr ingo#err#Get()endif 215 | vnoremap MarkRegex :if ! mark#MarkRegex(v:count, mark#GetVisualSelectionAsRegexp())execute "normal! \C-\>\C-n>\Esc>"echoerr ingo#err#Get()endif 216 | nnoremap MarkClear :if ! mark#Clear(v:count)execute "normal! \C-\>\C-n>\Esc>"echoerr ingo#err#Get()endif 217 | nnoremap MarkAllClear :call mark#ClearAll() 218 | nnoremap MarkConfirmAllClear :if confirm('Really delete all marks? This cannot be undone.', "&Yes\n&No") == 1call mark#ClearAll()endif 219 | nnoremap MarkToggle :call mark#Toggle() 220 | 221 | nnoremap MarkSearchCurrentNext :if ! mark#SearchCurrentMark(0)echoerr ingo#err#Get()endif 222 | nnoremap MarkSearchCurrentPrev :if ! mark#SearchCurrentMark(1)echoerr ingo#err#Get()endif 223 | nnoremap MarkSearchAnyNext :if ! mark#SearchAnyMark(0)echoerr ingo#err#Get()endif 224 | nnoremap MarkSearchAnyPrev :if ! mark#SearchAnyMark(1)echoerr ingo#err#Get()endif 225 | " When typed, [*#nN] open the fold at the search result, but inside a mapping or 226 | " :normal this must be done explicitly via 'zv'. 227 | nnoremap MarkSearchNext :if ! mark#SearchNext(0)execute 'normal!' v:count1 . '*zv'endif 228 | nnoremap MarkSearchPrev :if ! mark#SearchNext(1)execute 'normal!' v:count1 . '#zv'endif 229 | nnoremap MarkSearchOrCurNext :if ! mark#SearchNext(0,'mark#SearchCurrentMark')execute 'normal!' v:count1 . '*zv'endif 230 | nnoremap MarkSearchOrCurPrev :if ! mark#SearchNext(1,'mark#SearchCurrentMark')execute 'normal!' v:count1 . '#zv'endif 231 | nnoremap MarkSearchOrAnyNext :if ! mark#SearchNext(0,'mark#SearchAnyMark')execute 'normal!' v:count1 . '*zv'endif 232 | nnoremap MarkSearchOrAnyPrev :if ! mark#SearchNext(1,'mark#SearchAnyMark')execute 'normal!' v:count1 . '#zv'endif 233 | nnoremap MarkSearchAnyOrDefaultNext :if mark#IsEnabled() && mark#GetCount() > 0if ! mark#SearchAnyMark(0)echoerr ingo#err#Get()endifelseexecute 'normal!' v:count1 . 'nzv'endif 234 | nnoremap MarkSearchAnyOrDefaultPrev :if mark#IsEnabled() && mark#GetCount() > 0if ! mark#SearchAnyMark(1)echoerr ingo#err#Get()endifelseexecute 'normal!' v:count1 . 'Nzv'endif 235 | nnoremap MarkSearchGroupNext :if ! mark#SearchGroupMark(v:count, 1, 0, 1)execute "normal! \C-\>\C-n>\Esc>"echoerr ingo#err#Get()endif 236 | nnoremap MarkSearchGroupPrev :if ! mark#SearchGroupMark(v:count, 1, 1, 1)execute "normal! \C-\>\C-n>\Esc>"echoerr ingo#err#Get()endif 237 | nnoremap MarkSearchUsedGroupNext :if ! mark#SearchNextGroup(v:count1, 0)execute "normal! \C-\>\C-n>\Esc>"echoerr ingo#err#Get()endif 238 | nnoremap MarkSearchUsedGroupPrev :if ! mark#SearchNextGroup(v:count1, 1)execute "normal! \C-\>\C-n>\Esc>"echoerr ingo#err#Get()endif 239 | nnoremap MarkSearchCascadeStartWithStop :if ! mark#cascade#Start(v:count, 1)execute "normal! \C-\>\C-n>\Esc>" echoerr ingo#err#Get()endif 240 | nnoremap MarkSearchCascadeNextWithStop :if ! mark#cascade#Next(v:count1, 1, 0)execute "normal! \C-\>\C-n>\Esc>"echoerr ingo#err#Get()endif 241 | nnoremap MarkSearchCascadePrevWithStop :if ! mark#cascade#Next(v:count1, 1, 1)execute "normal! \C-\>\C-n>\Esc>"echoerr ingo#err#Get()endif 242 | nnoremap MarkSearchCascadeStartNoStop :if ! mark#cascade#Start(v:count, 0)execute "normal! \C-\>\C-n>\Esc>" echoerr ingo#err#Get()endif 243 | nnoremap MarkSearchCascadeNextNoStop :if ! mark#cascade#Next(v:count1, 0, 0)execute "normal! \C-\>\C-n>\Esc>"echoerr ingo#err#Get()endif 244 | nnoremap MarkSearchCascadePrevNoStop :if ! mark#cascade#Next(v:count1, 0, 1)execute "normal! \C-\>\C-n>\Esc>"echoerr ingo#err#Get()endif 245 | 246 | function! s:MakeDirectGroupMappings( isDefineDefaultMappings ) 247 | for l:cnt in range(1, g:mwDirectGroupJumpMappingNum) 248 | for [l:isBackward, l:direction, l:keyModifier] in [[0, 'Next', ''], [1, 'Prev', 'C-']] 249 | let l:plugMappingName = printf('MarkSearchGroup%d%s', l:cnt, l:direction) 250 | execute printf('nnoremap %s :if ! mark#SearchGroupMark(%d, v:count1, %d, 1)execute "normal! \C-\>\C-n>\Esc>"echoerr ingo#err#Get()endif', l:plugMappingName, l:cnt, l:isBackward) 251 | if a:isDefineDefaultMappings && ! hasmapto(l:plugMappingName, 'n') 252 | execute printf('nmap <%sk%d> %s', l:keyModifier, l:cnt, l:plugMappingName) 253 | endif 254 | endfor 255 | endfor 256 | endfunction 257 | call s:MakeDirectGroupMappings(! exists('g:mw_no_mappings')) 258 | delfunction s:MakeDirectGroupMappings 259 | 260 | if exists('g:mw_no_mappings') 261 | let &cpo = s:save_cpo 262 | unlet s:save_cpo 263 | finish 264 | endif 265 | 266 | if !hasmapto('MarkSet', 'n') 267 | nmap m MarkSet 268 | endif 269 | if !hasmapto('MarkPartialWord', 'n') 270 | nmap gm MarkPartialWord 271 | endif 272 | if !hasmapto('MarkSet', 'x') 273 | xmap m MarkSet 274 | endif 275 | " No default mapping for MarkIWhiteSet. 276 | if !hasmapto('MarkRegex', 'n') 277 | nmap r MarkRegex 278 | endif 279 | if !hasmapto('MarkRegex', 'x') 280 | xmap r MarkRegex 281 | endif 282 | if !hasmapto('MarkClear', 'n') 283 | nmap n MarkClear 284 | endif 285 | " No default mapping for MarkAllClear. 286 | " No default mapping for MarkConfirmAllClear. 287 | " No default mapping for MarkToggle. 288 | 289 | if !hasmapto('MarkSearchCurrentNext', 'n') 290 | nmap * MarkSearchCurrentNext 291 | endif 292 | if !hasmapto('MarkSearchCurrentPrev', 'n') 293 | nmap # MarkSearchCurrentPrev 294 | endif 295 | if !hasmapto('MarkSearchAnyNext', 'n') 296 | nmap / MarkSearchAnyNext 297 | endif 298 | if !hasmapto('MarkSearchAnyPrev', 'n') 299 | nmap ? MarkSearchAnyPrev 300 | endif 301 | if !hasmapto('MarkSearchNext', 'n') 302 | nmap * MarkSearchNext 303 | endif 304 | if !hasmapto('MarkSearchPrev', 'n') 305 | nmap # MarkSearchPrev 306 | endif 307 | " No default mapping for MarkSearchOrCurNext 308 | " No default mapping for MarkSearchOrCurPrev 309 | " No default mapping for MarkSearchOrAnyNext 310 | " No default mapping for MarkSearchOrAnyPrev 311 | " No default mapping for MarkSearchAnyOrDefaultNext 312 | " No default mapping for MarkSearchAnyOrDefaultPrev 313 | " No default mapping for MarkSearchGroupNext 314 | " No default mapping for MarkSearchGroupPrev 315 | " No default mapping for MarkSearchUsedGroupNext 316 | " No default mapping for MarkSearchUsedGroupPrev 317 | 318 | let &cpo = s:save_cpo 319 | unlet s:save_cpo 320 | " vim: ts=4 sts=0 sw=4 noet 321 | --------------------------------------------------------------------------------