]> git.wincent.com - pinnacle.git/blob - autoload/pinnacle.vim
Use `empty` instead of `==`
[pinnacle.git] / autoload / pinnacle.vim
1 ""
2 " @plugin Pinnacle Highlight group manipulation for Vim
3 "
4 " # Intro
5 "
6 " Pinnacle provides functions for manipulating |:highlight| groups.
7 "
8 "
9 " # Installation
10 "
11 " To install Pinnacle, use your plug-in management system of choice.
12 "
13 " If you don't have a "plug-in management system of choice", I recommend
14 " Pathogen (https://github.com/tpope/vim-pathogen) due to its simplicity and
15 " robustness. Assuming that you have Pathogen installed and configured, and that
16 " you want to install Pinnacle into `~/.vim/bundle`, you can do so with:
17 "
18 " ```
19 " git clone https://github.com/wincent/pinnacle.git ~/.vim/bundle/pinnacle
20 " ```
21 "
22 " Alternatively, if you use a Git submodule for each Vim plug-in, you could do
23 " the following after `cd`-ing into the top-level of your Git superproject:
24 "
25 " ```
26 " git submodule add https://github.com/wincent/pinnacle.git ~/vim/bundle/pinnacle
27 " git submodule init
28 " ```
29 "
30 " To generate help tags under Pathogen, you can do so from inside Vim with:
31 "
32 " ```
33 " :call pathogen#helptags()
34 " ```
35 "
36 "
37 " # Website
38 "
39 " The official Pinnacle source code repo is at:
40 "
41 "   http://git.wincent.com/pinnacle.git
42 "
43 " Mirrors exist at:
44 "
45 "   - https://github.com/wincent/pinnacle
46 "   - https://gitlab.com/wincent/pinnacle
47 "   - https://bitbucket.org/ghurrell/pinnacle
48 "
49 " Official releases are listed at:
50 "
51 "   http://www.vim.org/scripts/script.php?script_id=5360
52 "
53 "
54 " # License
55 "
56 " Copyright (c) 2016-present Greg Hurrell
57 "
58 " Permission is hereby granted, free of charge, to any person obtaining
59 " a copy of this software and associated documentation files (the
60 " "Software"), to deal in the Software without restriction, including
61 " without limitation the rights to use, copy, modify, merge, publish,
62 " distribute, sublicense, and/or sell copies of the Software, and to
63 " permit persons to whom the Software is furnished to do so, subject to
64 " the following conditions:
65 "
66 " The above copyright notice and this permission notice shall be
67 " included in all copies or substantial portions of the Software.
68 "
69 " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
70 " EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
71 " MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
72 " NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
73 " LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
74 " OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
75 " WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
76 "
77 "
78 " # Development
79 "
80 " ## Contributing patches
81 "
82 " Patches can be sent via mail to greg@hurrell.net, or as GitHub pull requests
83 " at: https://github.com/wincent/pinnacle/pulls
84 "
85 " ## Cutting a new release
86 "
87 " At the moment the release process is manual:
88 "
89 " - Perform final sanity checks and manual testing
90 " - Update the |pinnacle-history| section of the documentation
91 " - Verify clean work tree:
92 "
93 " ```
94 " git status
95 " ```
96 "
97 " - Tag the release:
98 "
99 " ```
100 " git tag -s -m "$VERSION release" $VERSION
101 " ```
102 "
103 " - Publish the code:
104 "
105 " ```
106 " git push origin master --follow-tags
107 " git push github master --follow-tags
108 " ```
109 "
110 " - Produce the release archive:
111 "
112 " ```
113 " git archive -o pinnacle-$VERSION.zip HEAD -- .
114 " ```
115 "
116 " - Upload to http://www.vim.org/scripts/script.php?script_id=5360
117 "
118 "
119 " # Authors
120 "
121 " Pinnacle is written and maintained by Greg Hurrell <greg@hurrell.net>.
122 "
123 " Other contributors that have submitted patches include (in alphabetical
124 " order):
125 "
126 " - Kyle Poole
127 "
128 "
129 " # History
130 "
131 " ## 0.3 (6 June 2017)
132 "
133 " - Added `pinnacle#extract_bg` and `pinnacle#extract_fg`.
134 " - Fixed bug that could cause existing highlights to be incorrectly
135 "   augmented.
136 "
137 " ## 0.2 (9 January 2017)
138 "
139 " - Added `pinnacle#underline`.
140 "
141 " ## 0.1 (30 March 2016)
142 "
143 " - Initial release.
144
145 " Replaces newlines with spaces.
146 function! pinnacle#sub_newlines(string) abort
147   return tr(a:string, "\r\n", '  ')
148 endfunction
149
150 " Runs a command and returns the captured output as a single line.
151 "
152 " Useful when we don't want to let long lines on narrow windows produce unwanted
153 " embedded newlines.
154 function! pinnacle#capture_line(command) abort
155   redir => l:capture
156   execute a:command
157   redir END
158
159   return pinnacle#sub_newlines(l:capture)
160 endfunction
161
162 " Gets the current value of a highlight group.
163 function! pinnacle#capture_highlight(group) abort
164   return pinnacle#capture_line('0verbose silent highlight ' . a:group)
165 endfunction
166
167 " Extracts a highlight string from a group, recursively traversing linked
168 " groups, and returns a string suitable for passing to `:highlight`.
169 function! pinnacle#extract_highlight(group) abort
170   let l:group = pinnacle#capture_highlight(a:group)
171
172   " Traverse links back to authoritative group.
173   while l:group =~# 'links to'
174     let l:index = stridx(l:group, 'links to') + len('links to')
175     let l:linked = strpart(l:group, l:index + 1)
176     let l:group = pinnacle#capture_highlight(l:linked)
177   endwhile
178
179   " Extract the highlighting details (the bit after "xxx")
180   let l:matches = matchlist(l:group, '\<xxx\>\s\+\(.*\)')
181   let l:original = l:matches[1]
182   return l:original
183 endfunction
184
185 let s:prefix=has('gui') || has('termguicolors') ? 'gui' : 'cterm'
186
187 function! pinnacle#extract_bg(group) abort
188   return pinnacle#extract_component(a:group, 'bg')
189 endfunction
190
191 function! pinnacle#extract_fg(group) abort
192   return pinnacle#extract_component(a:group, 'fg')
193 endfunction
194
195 function! pinnacle#extract_component(group, component) abort
196   return synIDattr(synIDtrans(hlID(a:group)), a:component, s:prefix)
197 endfunction
198
199 function! pinnacle#highlight(highlight) abort
200   let l:result=[]
201   if has_key(a:highlight, 'bg')
202     call insert(l:result, s:prefix . 'bg=' . a:highlight['bg'])
203   endif
204   if has_key(a:highlight, 'fg')
205     call insert(l:result, s:prefix . 'fg=' . a:highlight['fg'])
206   endif
207   if has_key(a:highlight, 'term')
208     call insert(l:result, s:prefix . '=' . a:highlight['term'])
209   endif
210   return join(l:result, ' ')
211 endfunction
212
213 " Returns an italicized copy of `group` suitable for passing to `:highlight`.
214 function! pinnacle#italicize(group) abort
215   return pinnacle#decorate('italic', a:group)
216 endfunction
217
218 " Returns a bold copy of `group` suitable for passing to `:highlight`.
219 function! pinnacle#embolden(group) abort
220   return pinnacle#decorate('bold', a:group)
221 endfunction
222
223 " Returns an underlined copy of `group` suitable for passing to `:highlight`.
224 function! pinnacle#underline(group) abort
225   return pinnacle#decorate('underline', a:group)
226 endfunction
227
228 " Returns a copy of `group` decorated with `style` (eg. "bold", "italic" etc)
229 " suitable for passing to `:highlight`.
230 function! pinnacle#decorate(style, group) abort
231   let l:original = pinnacle#extract_highlight(a:group)
232
233   for l:lhs in ['gui', 'term', 'cterm']
234     " Check for existing setting.
235     let l:matches = matchlist(
236       \   l:original,
237       \   '^\(\%([^ ]\+ \)*\)' .
238       \   '\(' . l:lhs . '=[^ ]\+\)' .
239       \   '\(\%( [^ ]\+\)*\)$'
240       \ )
241     if empty(l:matches)
242       " No setting, add one with just a:style in it
243       let l:original .= ' ' . l:lhs . '=' . a:style
244     else
245       " Existing setting; check whether a:style is already in it.
246       let l:start = l:matches[1]
247       let l:value = l:matches[2]
248       let l:end = l:matches[3]
249       if l:value =~# '.*' . a:style . '.*'
250         continue
251       else
252         let l:original = l:start . l:value . ',' . a:style . l:end
253       endif
254     endif
255   endfor
256
257   return pinnacle#sub_newlines(l:original)
258 endfunction