1 " ==============================================================================
3 " Description: Visually displays the location of marks.
4 " Authors: Anthony Kruize <trandor@labyrinth.net.au>
5 " Michael Geddes <michaelrgeddes@optushome.com.au>
7 " Modified: 17 August 2004
8 " License: Released into the public domain.
9 " ChangeLog: See :help showmarks-changelog
11 " Usage: Copy this file into the plugins directory so it will be
12 " automatically sourced.
14 " Default keymappings are:
15 " <Leader>mt - Toggles ShowMarks on and off.
16 " <Leader>mo - Turns ShowMarks on, and displays marks.
17 " <Leader>mh - Clears a mark.
18 " <Leader>ma - Clears all marks.
19 " <Leader>mm - Places the next available mark.
21 " Hiding a mark doesn't actually remove it, it simply moves it
22 " to line 1 and hides it visually.
24 " Configuration: ***********************************************************
25 " * PLEASE read the included help file(showmarks.txt) for a *
26 " * more thorough explanation of how to use ShowMarks. *
27 " ***********************************************************
28 " The following options can be used to customize the behavior
29 " of ShowMarks. Simply include them in your vimrc file with
30 " the desired settings.
32 " showmarks_enable (Default: 1)
33 " Defines whether ShowMarks is enabled by default.
34 " Example: let g:showmarks_enable=0
35 " showmarks_include (Default: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.'`^<>[]{}()\"")
36 " Defines all marks, in precedence order (only the highest
37 " precence will show on lines having more than one mark).
38 " Can be buffer-specific (set b:showmarks_include)
39 " showmarks_ignore_type (Default: "hq")
40 " Defines the buffer types to be ignored.
42 " h - Help p - preview
43 " q - quickfix r - readonly
45 " showmarks_textlower (Default: ">")
46 " Defines how the mark is to be displayed.
47 " A maximum of two characters can be displayed. To include
48 " the mark in the text use a tab(\t) character. A single
49 " character will display as the mark with the character
50 " suffixed (same as "\t<character>")
52 " To display the mark with a > suffixed:
53 " let g:showmarks_textlower="\t>"
55 " let g:showmarks_textlower=">"
56 " To display the mark with a ( prefixed:
57 " let g:showmarks_textlower="(\t"
58 " To display two > characters:
59 " let g:showmarks_textlower=">>"
60 " showmarks_textupper (Default: ">")
61 " Same as above but for the marks A-Z.
62 " Example: let g:showmarks_textupper="**"
63 " showmarks_textother (Default: ">")
64 " Same as above but for all other marks.
65 " Example: let g:showmarks_textother="--"
66 " showmarks_hlline_lower (Default: 0)
67 " showmarks_hlline_upper (Default: 0)
68 " showmarks_hlline_other (Default: 0)
69 " Defines whether the entire line for a particular mark
70 " should be highlighted.
71 " Example: let g:showmarks_hlline_lower=1
73 " Setting Highlighting Colours
74 " ShowMarks uses the following highlighting groups:
75 " ShowMarksHLl - For marks a-z
76 " ShowMarksHLu - For marks A-Z
77 " ShowMarksHLo - For all other marks
78 " ShowMarksHLm - For multiple marks on the same line.
79 " (Highest precendece mark is shown)
81 " By default they are set to a bold blue on light blue.
82 " Defining a highlight for each of these groups will
83 " override the default highlighting.
84 " See the VIM help for more information about highlighting.
85 " ==============================================================================
87 " Check if we should continue loading
88 if exists( "loaded_showmarks" )
91 let loaded_showmarks = 1
93 " Bail if Vim isn't compiled with signs support.
94 if has( "signs" ) == 0
96 echo "ShowMarks requires Vim to have +signs support."
101 " Options: Set up some nice defaults
102 if !exists('g:showmarks_enable' ) | let g:showmarks_enable = 1 | endif
103 if !exists('g:showmarks_textlower' ) | let g:showmarks_textlower = ">" | endif
104 if !exists('g:showmarks_textupper' ) | let g:showmarks_textupper = ">" | endif
105 if !exists('g:showmarks_textother' ) | let g:showmarks_textother = ">" | endif
106 if !exists('g:showmarks_ignore_type' ) | let g:showmarks_ignore_type = "hq" | endif
107 if !exists('g:showmarks_ignore_name' ) | let g:showmarks_ignore_name = "" | endif
108 if !exists('g:showmarks_hlline_lower') | let g:showmarks_hlline_lower = "0" | endif
109 if !exists('g:showmarks_hlline_upper') | let g:showmarks_hlline_upper = "0" | endif
110 if !exists('g:showmarks_hlline_other') | let g:showmarks_hlline_other = "0" | endif
112 " This is the default, and used in ShowMarksSetup to set up info for any
113 " possible mark (not just those specified in the possibly user-supplied list
114 " of marks to show -- it can be changed on-the-fly).
115 let s:all_marks = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.'`^<>[]{}()\""
118 com! -nargs=0 ShowMarksToggle :call <sid>ShowMarksToggle()
119 com! -nargs=0 ShowMarksOn :call <sid>ShowMarksOn()
120 com! -nargs=0 ShowMarksClearMark :call <sid>ShowMarksClearMark()
121 com! -nargs=0 ShowMarksClearAll :call <sid>ShowMarksClearAll()
122 com! -nargs=0 ShowMarksPlaceMark :call <sid>ShowMarksPlaceMark()
124 " Mappings (NOTE: Leave the '|'s immediately following the '<cr>' so the mapping does not contain any trailing spaces!)
125 if !hasmapto( '<Plug>ShowmarksShowMarksToggle' ) | map <silent> <unique> <leader>mt :ShowMarksToggle<cr>| endif
126 if !hasmapto( '<Plug>ShowmarksShowMarksOn' ) | map <silent> <unique> <leader>mo :ShowMarksOn<cr>| endif
127 if !hasmapto( '<Plug>ShowmarksClearMark' ) | map <silent> <unique> <leader>mh :ShowMarksClearMark<cr>| endif
128 if !hasmapto( '<Plug>ShowmarksClearAll' ) | map <silent> <unique> <leader>ma :ShowMarksClearAll<cr>| endif
129 if !hasmapto( '<Plug>ShowmarksPlaceMark' ) | map <silent> <unique> <leader>mm :ShowMarksPlaceMark<cr>| endif
130 noremap <unique> <script> \sm m
131 noremap <silent> m :exe 'norm \sm'.nr2char(getchar())<bar>call <sid>ShowMarks()<CR>
133 " AutoCommands: Only if ShowMarks is enabled
134 if g:showmarks_enable == 1
137 autocmd CursorHold * call s:ShowMarks()
141 " Highlighting: Setup some nice colours to show the mark positions.
142 hi default ShowMarksHLl ctermfg=darkblue ctermbg=blue cterm=bold guifg=blue guibg=lightblue gui=bold
143 hi default ShowMarksHLu ctermfg=darkblue ctermbg=blue cterm=bold guifg=blue guibg=lightblue gui=bold
144 hi default ShowMarksHLo ctermfg=darkblue ctermbg=blue cterm=bold guifg=blue guibg=lightblue gui=bold
145 hi default ShowMarksHLm ctermfg=darkblue ctermbg=blue cterm=bold guifg=blue guibg=lightblue gui=bold
147 " Function: IncludeMarks()
148 " Description: This function returns the list of marks (in priority order) to
149 " show in this buffer. Each buffer, if not already set, inherits the global
150 " setting; if the global include marks have not been set; that is set to the
152 fun! s:IncludeMarks()
153 if exists('b:showmarks_include') && exists('b:showmarks_previous_include') && b:showmarks_include != b:showmarks_previous_include
154 " The user changed the marks to include; hide all marks; change the
155 " included mark list, then show all marks. Prevent infinite
156 " recursion during this switch.
157 if exists('s:use_previous_include')
158 " Recursive call from ShowMarksHideAll()
159 return b:showmarks_previous_include
160 elseif exists('s:use_new_include')
161 " Recursive call from ShowMarks()
162 return b:showmarks_include
164 let s:use_previous_include = 1
165 call <sid>ShowMarksHideAll()
166 unlet s:use_previous_include
167 let s:use_new_include = 1
168 call <sid>ShowMarks()
169 unlet s:use_new_include
173 if !exists('g:showmarks_include')
174 let g:showmarks_include = s:all_marks
176 if !exists('b:showmarks_include')
177 let b:showmarks_include = g:showmarks_include
180 " Save this include setting so we can detect if it was changed.
181 let b:showmarks_previous_include = b:showmarks_include
183 return b:showmarks_include
186 " Function: NameOfMark()
187 " Paramaters: mark - Specifies the mark to find the name of.
188 " Description: Convert marks that cannot be used as part of a variable name to
189 " something that can be. i.e. We cannot use [ as a variable-name suffix (as
190 " in 'placed_['; this function will return something like 63, so the variable
191 " will be something like 'placed_63').
192 " 10 is added to the mark's index to avoid colliding with the numeric marks
193 " 0-9 (since a non-word mark could be listed in showmarks_include in the
194 " first 10 characters if the user overrides the default).
195 " Returns: The name of the requested mark.
196 fun! s:NameOfMark(mark)
199 let name = stridx(s:all_marks, a:mark) + 10
204 " Function: VerifyText()
205 " Paramaters: which - Specifies the variable to verify.
206 " Description: Verify the validity of a showmarks_text{upper,lower,other} setup variable.
207 " Default to ">" if it is found to be invalid.
208 fun! s:VerifyText(which)
209 if strlen(g:showmarks_text{a:which}) == 0 || strlen(g:showmarks_text{a:which}) > 2
211 echo "ShowMarks: text".a:which." must contain only 1 or 2 characters."
213 let g:showmarks_text{a:which}=">"
217 " Function: ShowMarksSetup()
218 " Description: This function sets up the sign definitions for each mark.
219 " It uses the showmarks_textlower, showmarks_textupper and showmarks_textother
220 " variables to determine how to draw the mark.
221 fun! s:ShowMarksSetup()
222 " Make sure the textlower, textupper, and textother options are valid.
223 call s:VerifyText('lower')
224 call s:VerifyText('upper')
225 call s:VerifyText('other')
228 let s:maxmarks = strlen(s:all_marks)
230 let c = strpart(s:all_marks, n, 1)
231 let nm = s:NameOfMark(c)
235 if strlen(g:showmarks_textlower) == 1
236 let text=c.g:showmarks_textlower
237 elseif strlen(g:showmarks_textlower) == 2
238 let t1 = strpart(g:showmarks_textlower,0,1)
239 let t2 = strpart(g:showmarks_textlower,1,1)
245 let text=g:showmarks_textlower
248 let s:ShowMarksDLink{nm} = 'ShowMarksHLl'
249 if g:showmarks_hlline_lower == 1
250 let lhltext = 'linehl='.s:ShowMarksDLink{nm}.nm
253 if strlen(g:showmarks_textupper) == 1
254 let text=c.g:showmarks_textupper
255 elseif strlen(g:showmarks_textupper) == 2
256 let t1 = strpart(g:showmarks_textupper,0,1)
257 let t2 = strpart(g:showmarks_textupper,1,1)
263 let text=g:showmarks_textupper
266 let s:ShowMarksDLink{nm} = 'ShowMarksHLu'
267 if g:showmarks_hlline_upper == 1
268 let lhltext = 'linehl='.s:ShowMarksDLink{nm}.nm
270 else " Other signs, like ', ., etc.
271 if strlen(g:showmarks_textother) == 1
272 let text=c.g:showmarks_textother
273 elseif strlen(g:showmarks_textother) == 2
274 let t1 = strpart(g:showmarks_textother,0,1)
275 let t2 = strpart(g:showmarks_textother,1,1)
281 let text=g:showmarks_textother
284 let s:ShowMarksDLink{nm} = 'ShowMarksHLo'
285 if g:showmarks_hlline_other == 1
286 let lhltext = 'linehl='.s:ShowMarksDLink{nm}.nm
290 " Define the sign with a unique highlight which will be linked when placed.
291 exe 'sign define ShowMark'.nm.' '.lhltext.' text='.text.' texthl='.s:ShowMarksDLink{nm}.nm
292 let b:ShowMarksLink{nm} = ''
298 call s:ShowMarksSetup()
300 " Function: ShowMarksOn
301 " Description: Enable showmarks, and show them now.
303 if g:showmarks_enable == 0
304 call <sid>ShowMarksToggle()
306 call <sid>ShowMarks()
310 " Function: ShowMarksToggle()
311 " Description: This function toggles whether marks are displayed or not.
312 fun! s:ShowMarksToggle()
313 if g:showmarks_enable == 0
314 let g:showmarks_enable = 1
315 call <sid>ShowMarks()
318 autocmd CursorHold * call s:ShowMarks()
321 let g:showmarks_enable = 0
322 call <sid>ShowMarksHideAll()
325 autocmd BufEnter * call s:ShowMarksHideAll()
330 " Function: ShowMarks()
331 " Description: This function runs through all the marks and displays or
332 " removes signs as appropriate. It is called on the CursorHold autocommand.
333 " We use the marked_{ln} variables (containing a timestamp) to track what marks
334 " we've shown (placed) in this call to ShowMarks; to only actually place the
335 " first mark on any particular line -- this forces only the first mark
336 " (according to the order of showmarks_include) to be shown (i.e., letters
337 " take precedence over marks like paragraph and sentence.)
339 if g:showmarks_enable == 0
343 if ((match(g:showmarks_ignore_type, "[Hh]") > -1) && (&buftype == "help" ))
344 \ || ((match(g:showmarks_ignore_type, "[Qq]") > -1) && (&buftype == "quickfix"))
345 \ || ((match(g:showmarks_ignore_type, "[Pp]") > -1) && (&pvw == 1 ))
346 \ || ((match(g:showmarks_ignore_type, "[Rr]") > -1) && (&readonly == 1 ))
347 \ || ((match(g:showmarks_ignore_type, "[Mm]") > -1) && (&modifiable == 0 ))
352 let s:maxmarks = strlen(s:IncludeMarks())
354 let c = strpart(s:IncludeMarks(), n, 1)
355 let nm = s:NameOfMark(c)
356 let id = n + (s:maxmarks * winbufnr(0))
359 if ln == 0 && (exists('b:placed_'.nm) && b:placed_{nm} != ln)
360 exe 'sign unplace '.id.' buffer='.winbufnr(0)
361 elseif ln > 1 || c !~ '[a-zA-Z]'
362 " Have we already placed a mark here in this call to ShowMarks?
363 if exists('mark_at'.ln)
364 " Already placed a mark, set the highlight to multiple
365 if c =~# '[a-zA-Z]' && b:ShowMarksLink{mark_at{ln}} != 'ShowMarksHLm'
366 let b:ShowMarksLink{mark_at{ln}} = 'ShowMarksHLm'
367 exe 'hi link '.s:ShowMarksDLink{mark_at{ln}}.mark_at{ln}.' '.b:ShowMarksLink{mark_at{ln}}
370 if !exists('b:ShowMarksLink'.nm) || b:ShowMarksLink{nm} != s:ShowMarksDLink{nm}
371 let b:ShowMarksLink{nm} = s:ShowMarksDLink{nm}
372 exe 'hi link '.s:ShowMarksDLink{nm}.nm.' '.b:ShowMarksLink{nm}
375 if !exists('b:placed_'.nm) || b:placed_{nm} != ln
376 exe 'sign unplace '.id.' buffer='.winbufnr(0)
377 exe 'sign place '.id.' name=ShowMark'.nm.' line='.ln.' buffer='.winbufnr(0)
378 let b:placed_{nm} = ln
386 " Function: ShowMarksClearMark()
387 " Description: This function hides the mark at the current line.
388 " It simply moves the mark to line 1 and removes the sign.
389 " Only marks a-z and A-Z are supported.
390 fun! s:ShowMarksClearMark()
393 let s:maxmarks = strlen(s:IncludeMarks())
395 let c = strpart(s:IncludeMarks(), n, 1)
396 if c =~# '[a-zA-Z]' && ln == line("'".c)
397 let nm = s:NameOfMark(c)
398 let id = n + (s:maxmarks * winbufnr(0))
399 exe 'sign unplace '.id.' buffer='.winbufnr(0)
401 let b:placed_{nm} = 1
407 " Function: ShowMarksClearAll()
408 " Description: This function clears all marks in the buffer.
409 " It simply moves the marks to line 1 and removes the signs.
410 " Only marks a-z and A-Z are supported.
411 fun! s:ShowMarksClearAll()
413 let s:maxmarks = strlen(s:IncludeMarks())
415 let c = strpart(s:IncludeMarks(), n, 1)
417 let nm = s:NameOfMark(c)
418 let id = n + (s:maxmarks * winbufnr(0))
419 exe 'sign unplace '.id.' buffer='.winbufnr(0)
421 let b:placed_{nm} = 1
427 " Function: ShowMarksHideAll()
428 " Description: This function hides all marks in the buffer.
429 " It simply removes the signs.
430 fun! s:ShowMarksHideAll()
432 let s:maxmarks = strlen(s:IncludeMarks())
434 let c = strpart(s:IncludeMarks(), n, 1)
435 let nm = s:NameOfMark(c)
436 if exists('b:placed_'.nm)
437 let id = n + (s:maxmarks * winbufnr(0))
438 exe 'sign unplace '.id.' buffer='.winbufnr(0)
445 " Function: ShowMarksPlaceMark()
446 " Description: This function will place the next unplaced mark (in priority
447 " order) to the current location. The idea here is to automate the placement
448 " of marks so the user doesn't have to remember which marks are placed or not.
449 " Hidden marks are considered to be unplaced.
450 " Only marks a-z are supported.
451 fun! s:ShowMarksPlaceMark()
452 " Find the first, next, and last [a-z] mark in showmarks_include (i.e.
453 " priority order), so we know where to "wrap".
454 let first_alpha_mark = -1
455 let last_alpha_mark = -1
458 if !exists('b:previous_auto_mark')
459 let b:previous_auto_mark = -1
462 " Find the next unused [a-z] mark (in priority order); if they're all
463 " used, find the next one after the previously auto-assigned mark.
465 let s:maxmarks = strlen(s:IncludeMarks())
467 let c = strpart(s:IncludeMarks(), n, 1)
470 " Found an unused [a-z] mark; we're done.
475 if first_alpha_mark < 0
476 let first_alpha_mark = n
478 let last_alpha_mark = n
479 if n > b:previous_auto_mark && next_mark == -1
486 if next_mark == -1 && (b:previous_auto_mark == -1 || b:previous_auto_mark == last_alpha_mark)
487 " Didn't find an unused mark, and haven't placed any auto-chosen marks yet,
488 " or the previously placed auto-chosen mark was the last alpha mark --
489 " use the first alpha mark this time.
490 let next_mark = first_alpha_mark
495 echo 'No marks in [a-z] included! (No "next mark" to choose from)'
500 let c = strpart(s:IncludeMarks(), next_mark, 1)
501 let b:previous_auto_mark = next_mark
503 call <sid>ShowMarks()
506 " -----------------------------------------------------------------------------