xaizek / vim-qthelp (License: Unspecified) (since 2018-12-07)
This plugin opens Qt help pages in browser from C++ source code.
Commit d8f89acca53b1a7227bf58a6f8d64ae409da1337

First commit.
Author: xaizek
Author date (UTC): 2010-11-28 20:41
Committer name: xaizek
Committer date (UTC): 2010-11-28 20:41
Parent(s):
Signing key:
Tree: cbe7e6349985854f8da011cedff2ce8bf803f726
File Lines added Lines deleted
plugin/qthelp.vim 413 0
File plugin/qthelp.vim added (mode: 100644) (index 0000000..302b7f8)
1 " Name: qthelp
2 " Author: xaizek (xaizek@gmail.com)
3 " Version: 1.0
4 "
5 " Description: This plugin would allow you to open Qt help pages in browser
6 " from your C++ source code. Currently it can show help if the word
7 " under your cursor is a class name, a variable name or a
8 " class-member name.
9 "
10 " Examples: QStr|ing trackTitle, artistName;
11 " QString track|Title, artistName("Unknown");
12 " track|Title = "Unknown";
13 " Running command QHelpOnThis will open help on QString.
14 "
15 " trackTitle.ri|ght(10);
16 " Running command :QHelpOnThis will open help on QString::right.
17 "
18 " Running command :QHelp QPixmap::QPixmap will open help on
19 " QPixmap constructor.
20 "
21 " Configuring: g:qthelp_browser - command to run your browser
22 " default: '' (so an error message will be printed if your try to
23 " use plugin)
24 " Note: on Windows use something like
25 " 'start c:\Program files\Mozilla Firefox\firefox.exe'
26 " or Vim will be waiting until you close your browser.
27 "
28 " Using: 1. Make tags-file for your html-qthelp.
29 " 2. Add that tags-file into your 'tags' option (WARNING: doing
30 " this in your .vimrc file can slow down and pollute completion
31 " list for not Qt-projects).
32 " 3. Let g:qthelp_browser command to run your browser.
33 " 4. Map command QHelpOnThis on some hotkey.
34 " 5. Use QHelp from command-line for faster navigating through
35 " help (to escape manual searching of needed method).
36 "
37 " Limitation: I didn't found a way to determine inheritance hierarchy using
38 " tags so trying
39 " QStringList strlst;
40 " strlst.in|sert("String");
41 " will open reference for QString, not for QList::insert.
42 "
43 " Warning: In some cases it can take a while for searching definition (for
44 " example when it doesn't exist) if this happens hit Ctrl-C to
45 " break searching.
46
47 if exists("g:loaded_qthelp")
48 finish
49 endif
50
51 let g:loaded_gthelp = 1
52
53 if !exists('g:qthelp_browser')
54 let g:qthelp_browser = ''
55 endif
56
57 " boolean for switching debug output
58 let s:debugging = 0
59
60 " regexps, as you can see
61 let s:idregex = '\%(\<const\>\)\?'.'[_a-zA-Z][_a-zA-Z0-9]*'.'\%(\<const\>\)\?'
62 let s:nsregex = '\%('.s:idregex.'::\)*'
63 let s:numregex = '[0-9]\+'
64 let s:varpfxregex = '\s*&\?\s*\%(\*\s*\)*'
65 let s:varsfxregex = '\s*\%('.
66 \'\%(([^)]*)\)'.
67 \'\|'.
68 \'\%(\['.s:numregex.'\]\)'.
69 \'\)\?'
70 let s:varregex = s:varpfxregex.s:idregex.s:varsfxregex
71 let s:varlstregex = '\%('.s:varregex.'\s*\%(,\s*\)\?\)*'
72 let s:typeregex = '^\s*\%(const\s\+\)\?'.s:nsregex.'\zs'.s:idregex.'\ze'
73 \.'\%(<.*>\)\?'.s:varpfxregex.'\s\+'
74
75 " buffer variables
76 " b:membername
77 " b:foundinfile
78 " b:foundatline
79
80 " some commands
81 command! -nargs=0 QHelpOnThis call QHHelp('')
82 command! -nargs=1 QHelp call QHHelp('<args>')
83
84 " the main function
85 " cword parameter is what we want to get help on or '' for analyzing word under
86 " cursor
87 function! QHHelp(query)
88 call s:QHDebug('QHDBG: QHHelp(cword="'.a:query.'")')
89 if a:query != ''
90 let l:lst = s:QHGetTagsList(a:query)
91 else
92 let l:lst = s:QHGetTagsListUC()
93 endif
94
95 if len(l:lst) == 0
96 call s:QHTellUser("Nothing was found")
97 else
98 call s:QHDebug('QHDBG: QHHelp, filename="'.l:lst[0]['filename'].'"')
99 call <SID>QHOpenBrowser(l:lst[0]['filename'])
100 endif
101 endfunction
102
103 " determines if a class name, a variable name or a class member name is under
104 " cursor and returns appropriate taglist
105 function! s:QHGetTagsListUC()
106 " wuc is for Word Under Cursor
107 let l:wuc = expand('<cword>')
108 let l:lst = taglist('//apple_ref/cpp/cl//'.l:wuc.'$')
109 if len(l:lst) == 0 " if WUC is var_name
110 let [l:class, b:membername] = QHGetVUCInfo()
111 if b:membername == ''
112 let l:lst = taglist('//apple_ref/cpp/cl//'.l:class.'$')
113 else
114 let l:lst = s:QHGetTagsListOnMember(l:class, b:membername)
115 if len(l:lst) == 0
116 let b:membername = ''
117 let l:lst = taglist('//apple_ref/cpp/cl//'.l:class.'$')
118 endif
119 endif
120 else
121 let b:membername = ''
122 endif
123 return l:lst
124 endfunction
125
126 " b:membername should be set before calling this function because it should
127 " modify it last
128 function! s:QHGetTagsListOnMember(class, member)
129 let l:lst = taglist('^'.a:member.'-typedef$')
130 if len(l:lst) != 0
131 let b:membername = a:member.'-typedef'
132 return l:lst
133 endif
134 let l:lst = taglist('^'.a:member.'-enum$')
135 if len(l:lst) != 0
136 let b:membername = a:member.'-enum'
137 return l:lst
138 endif
139
140 if len(l:lst) == 0
141 let l:lst = taglist('//apple_ref/cpp/clm/'.a:class.'/'.a:member.'$')
142 endif
143 if len(l:lst) == 0
144 let l:lst = taglist('//apple_ref/cpp/instm/'.a:class.'/'.a:member.'$')
145 endif
146 if len(l:lst) == 0
147 let l:lst = taglist('//apple_ref/cpp/econst/'.a:class.'/'.a:member.'$')
148 endif
149 if len(l:lst) == 0
150 let l:lst = taglist('//apple_ref/cpp/tag/'.a:class.'/'.a:member.'$')
151 endif
152 return l:lst
153 endfunction
154
155 " analyses query for class name or class name and its member name
156 function! s:QHGetTagsList(query)
157 call s:QHDebug('QHDBG: QHGetTagsList(query="'.a:query.'")')
158 let l:lst = []
159
160 let l:regex = '\('.s:idregex.'\)::\('.s:idregex.'\)'
161 if matchstr(a:query, l:regex) != ''
162 let l:class = substitute(a:query, l:regex, '\1', '')
163 call s:QHDebug('QHDBG: QHGetTagsList, class="'.l:class.'"')
164 let l:member = substitute(a:query, l:regex, '\2', '')
165 call s:QHDebug('QHDBG: QHGetTagsList, member="'.l:member.'"')
166 let l:lst = taglist('//apple_ref/cpp/clm/'.l:class.'/'.l:member.'$')
167 if len(l:lst) == 0
168 let l:lst = taglist('//apple_ref/cpp/instm/'
169 \.l:class.'/'.l:member.'$')
170 endif
171 if len(l:lst) == 0
172 let l:lst = taglist('//apple_ref/cpp/tag/'
173 \.l:class.'/'.l:member.'$')
174 endif
175 let b:membername = l:member
176 else
177 let l:lst = taglist('//apple_ref/cpp/cl//'.a:query.'$')
178 let b:membername = ''
179 endif
180
181 return l:lst
182 endfunction
183
184 " opens users browser
185 function! s:QHOpenBrowser(file)
186 if s:debugging == 1
187 return
188 endif
189 let l:browserargs = 'file://'.fnamemodify(a:file, ':p')
190 if b:membername != ''
191 let l:browserargs = l:browserargs.'\#'.b:membername
192 endif
193 let l:browserargs = shellescape(l:browserargs)
194 if !has('win32') && !has('win64')
195 let l:browserargs = l:browserargs.'&'
196 endif
197 try
198 exe ":silent !".shellescape(g:qthelp_browser)." ".l:browserargs
199 catch 'E484'
200 call s:QHTellUser('An error occuried while running your browser. '
201 \.'Maybe your g:qthelp_browser option is incorrect.')
202 endtry
203 endfunction
204
205 " lets tell user about what we have found
206 function! s:QHInformUser(varname, class, membername)
207 let l:msg = a:class
208 if a:membername != ''
209 let l:msg = l:msg.'::'
210 endif
211 if a:varname != ''
212 let l:msg = a:varname.' ('.l:msg.')'
213 endif
214 if b:foundinfile != ''
215 let l:msg = l:msg.'; was found in '.b:foundinfile
216 \.' at line '.b:foundatline
217 endif
218 call s:QHTellUser(l:msg)
219 endfunction
220
221 " prints some message to a user to let him know what are we doing
222 function! s:QHTellUser(msg)
223 execute 'echomsg("QtHelp: '.escape(a:msg, '"').'")'
224 endfunction
225
226 " prints debug message
227 function! s:QHDebug(msg)
228 if s:debugging == 1
229 execute 'echomsg("QtHelp: '.escape(a:msg, '"').'")'
230 endif
231 endfunction
232
233 " ------------------------------------------------------------------------------
234 " stuff below is for analyzing code --------------------------------------------
235 " ------------------------------------------------------------------------------
236
237 " returns list with information about Variable Under Cursor in form of
238 " [classname, membername]
239 function! QHGetVUCInfo()
240 let l:wuc = expand('<cword>')
241 let l:wuctype = s:QHGetWUCType(wuc)
242 if l:wuctype == 0 " we're assuming that this is varname
243 let l:varname = s:QHGetVarType(l:wuc)
244 if l:varname == ''
245 return [l:wuc, '']
246 else
247 return [l:varname, '']
248 endif
249 elseif l:wuctype == 1
250 return [s:QHGetNSName(l:wuc), l:wuc]
251 elseif l:wuctype == 2
252 return [s:QHGetVarType(s:QHGetVarName(l:wuc)), l:wuc]
253 elseif l:wuctype == 3
254 return [l:wuc, '']
255 endif
256 endfunction
257
258 " this function searches for variable definition in current file and header
259 " file if the current one is source
260 function! s:QHGetVarType(varname)
261 let b:foundatline = line('.')
262
263 " firstly lets try to find declaration in current file
264 " simple case
265 let l:type = s:QHSearchSimpleVarDef(a:varname)
266 if l:type != ''
267 call s:QHDebug('QHDBG: Simple definition search succesed')
268 return l:type
269 endif
270 " more complex
271 let l:type = s:QHSearchComplexVarDef(a:varname)
272 if l:type != ''
273 call s:QHDebug('QHDBG: Complex definition search succesed')
274 return l:type
275 endif
276
277 " try to find variable in function declaration
278 let l:type = s:QHSearchVarDefInArgs(a:varname)
279 if l:type != ''
280 call s:QHDebug('QHDBG: Arg search succesed')
281 return l:type
282 endif
283
284 " and now lets take a look at the header if it exists
285 if expand('%:e') == 'cpp'
286 if filereadable(expand('%:r').'.hpp')
287 return s:QHSearchVarDefInFile(a:varname, expand('%:r').'.hpp')
288 elseif filereadable(expand('%:r').'.h')
289 return s:QHSearchVarDefInFile(a:varname, expand('%:r').'.h')
290 else
291 call s:QHDebug('QHDBG: QHGetVarType, cant find companion file')
292 return ''
293 endif
294 endif
295
296 return ''
297 endfunction
298
299 " searches for simple declaration of varname above cursor position
300 function! s:QHSearchSimpleVarDef(varname)
301 let l:sdefregex = '\<'.s:nsregex.s:idregex.'\>'.'\%(<.*>\)\?'.'[^,()=]\+'
302 \.'\<'.a:varname.'\>[^&]'
303 let l:lnum = search(l:sdefregex, 'bcnW')
304 if l:lnum > 0
305 let l:type = matchstr(matchstr(getline(l:lnum), l:sdefregex)
306 \, s:typeregex)
307 if l:type != 'delete'
308 let b:foundinfile = 'this file'
309 let b:foundatline = l:lnum
310 return l:type
311 endif
312 endif
313 endfunction
314
315 " searches for complex declaration of varname above cursor position
316 function! s:QHSearchComplexVarDef(varname)
317 let l:defregex = '\s*'.s:nsregex.s:idregex.'\s\+'
318 \.s:varlstregex.s:varpfxregex.a:varname.s:varsfxregex
319 \.'\%(<.*>\)\?\s*\%(;\|,\|\%(=\)\)'
320 let l:lnum = search(l:defregex, 'bcnW')
321 if l:lnum > 0
322 call s:QHDebug('QHDBG: QHGetVarType, lnum="'.l:lnum.'"')
323 let l:type = matchstr(getline(l:lnum), s:typeregex)
324 if l:type != 'delete'
325 let b:foundinfile = 'this file'
326 let b:foundatline = l:lnum
327 return l:type
328 else
329 " continue the search but without search() to escape moving cursor
330 for l:l in range(l:lnum - 1, 1, -1)
331 let l:type = matchstr(getline(l:l), l:defregex)
332 if l:type != '' && l:type != 'delete'
333 let b:foundinfile = 'this file'
334 let b:foundatline = l:l
335 return l:type
336 endif
337 endfor
338 endif
339 endif
340 endfunction
341
342 " searches for declaration of varname in args of current function
343 function! s:QHSearchVarDefInArgs(varname)
344 let l:argdefregex = '[(,]\s*\%(const\)\?'.s:nsregex.s:idregex.s:varpfxregex
345 \.'\s\+'.a:varname.s:varsfxregex.'\s*[,)]\?'
346 let l:stoplnum = search('^}', 'bcnW')
347 let l:lnum = search(l:argdefregex, 'bcnW', l:stoplnum + 1)
348 if l:lnum > 0
349 let b:foundinfile = 'this file'
350 let b:foundatline = l:lnum
351 return matchstr(getline(l:lnum), l:argdefregex, 'bcnW')
352 endif
353 return ''
354 endfunction
355
356 " searches for declaration of varname in file filename
357 function! s:QHSearchVarDefInFile(varname, filename)
358 let l:defregex = '^\s*'.s:nsregex.s:idregex.'\>.*\<'.a:varname.'\>'
359 let l:headerfile = readfile(a:filename)
360 let l:lnum = 0
361 for l:line in l:headerfile
362 let l:definition = matchstr(l:line, l:defregex)
363 if l:definition != ''
364 let b:foundinfile = a:filename
365 let b:foundatline = l:lnum
366 return matchstr(l:line, s:typeregex)
367 endif
368 let l:lnum = l:lnum + 1
369 endfor
370 return ''
371 endfunction
372
373 " returns variable name that goes right before the cursor
374 function! s:QHGetVarName(funcname)
375 let [l:line, l:col] = searchpos(expand('<cword>'), 'bcn')
376 let l:varname = matchstr(getline('.'),
377 \ s:idregex.'\ze\%(\.\|->\)\%'.l:col.'c'.a:funcname)
378 return l:varname
379 endfunction
380
381 " returns namespace name that goes right before the cursor
382 function! s:QHGetNSName(funcname)
383 let [l:line, l:col] = searchpos(expand('<cword>'), 'bcn')
384 let l:varname = matchstr(getline('.'),
385 \ s:idregex.'\ze::\%'.l:col.'c'.a:funcname)
386 return l:varname
387 endfunction
388
389 " analyses what is before the wuc and returns:
390 " - 0 for class_n|ame or va|r_name
391 " - 1 for class_name::mem|ber_name
392 " - 2 for var_name.mem|ber_name or var_name->me|mber_name
393 " - 3 for class_n|ame
394 function! s:QHGetWUCType(wuc)
395 let [l:line, l:col] = searchpos(a:wuc, 'bcn')
396 let l:pref = matchstr(getline('.'),
397 \ s:idregex.'\zs\%(::\|\.\|->\)\ze\%'.l:col.'c'
398 \ .a:wuc)
399 let l:suf = matchstr(getline('.'),
400 \ '\%'.l:col.'c'
401 \ .a:wuc.'\zs\%(::\|\.\|->\)\ze'.s:idregex)
402 if l:pref == '.' || l:pref == '->'
403 return 2
404 elseif l:pref == '::'
405 return 1
406 elseif l:suf == '::'
407 return 3
408 else
409 return 0
410 endif
411 endfunction
412
413 " vim: set foldmethod=syntax foldlevel=0 :
Hints

Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://code.reversed.top/user/xaizek/vim-qthelp

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@code.reversed.top/user/xaizek/vim-qthelp

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a pull request:
... clone the repository ...
... make some changes and some commits ...
git push origin master