My dotfiles. Period.

menu.vim 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. "=============================================================================
  2. " $Id: menu.vim 520 2012-03-19 18:09:15Z luc.hermitte $
  3. " File: autoload/lh/menu.vim {{{1
  4. " Author: Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
  5. " <URL:http://code.google.com/p/lh-vim/>
  6. " License: GPLv3 with exceptions
  7. " <URL:http://code.google.com/p/lh-vim/wiki/License>
  8. " Version: 3.0.0
  9. " Created: 13th Oct 2006
  10. " Last Update: $Date: 2012-03-19 19:09:15 +0100 (Mon, 19 Mar 2012) $ (07th Dec 2010)
  11. "------------------------------------------------------------------------
  12. " Description:
  13. " Defines the global function lh#menu#def_menu
  14. " Aimed at (ft)plugin writers.
  15. "
  16. "------------------------------------------------------------------------
  17. " Installation:
  18. " Drop this file into {rtp}/autoload/lh/
  19. " Requires Vim 7+
  20. " History:
  21. " v2.0.0: Moving to vim7
  22. " v2.0.1: :Toggle echoes the new value
  23. " v2.2.0: Support environment variables
  24. " Only one :Toggle command is defined.
  25. " v2.2.3: :Toggle can directly set the final value
  26. " (prefer this way of proceeding to update the menu to the new
  27. " value)
  28. " :Toggle suports auto-completion on possible values
  29. " v2.2.6: Toggle menus are silent, but not the actions executed
  30. " v3.0.0: GPLv3
  31. " TODO:
  32. " * Should the argument to :Toggle be simplified to use the variable name
  33. " instead ? May be a banged :Toggle! could work on the real variable
  34. " name, and on the real value.
  35. " * show all possible values in a sub menu (on demand)
  36. " }}}1
  37. "=============================================================================
  38. "=============================================================================
  39. let s:cpo_save=&cpo
  40. set cpo&vim
  41. "------------------------------------------------------------------------
  42. " ## Internal Variables {{{1
  43. let s:k_Toggle_cmd = 'Toggle'
  44. if !exists('s:toggle_commands')
  45. let s:toggle_commands = {}
  46. endif
  47. "------------------------------------------------------------------------
  48. " ## Functions {{{1
  49. " # Version {{{2
  50. let s:k_version = 300
  51. function! lh#menu#version()
  52. return s:k_version
  53. endfunction
  54. " # Debug {{{2
  55. let s:verbose = 0
  56. function! lh#menu#verbose(...)
  57. if a:0 > 0 | let s:verbose = a:1 | endif
  58. return s:verbose
  59. endfunction
  60. function! s:Verbose(expr)
  61. if exists('s:verbose') && s:verbose
  62. echomsg a:expr
  63. endif
  64. endfunction
  65. function! lh#menu#debug(expr)
  66. return eval(a:expr)
  67. endfunction
  68. "------------------------------------------------------------------------
  69. " # Common stuff {{{2
  70. " Function: lh#menu#text({text}) {{{3
  71. " @return a text to be used in menus where "\" and spaces have been escaped.
  72. function! lh#menu#text(text)
  73. return escape(a:text, '\ ')
  74. endfunction
  75. " # Toggling menu item {{{2
  76. " Function: s:Fetch({Data},{key}) {{{3
  77. " @param[in] Data Menu-item definition
  78. " @param[in] key Table table from which the result will be fetched
  79. " @return the current value, or text, whose index is Data.idx_crt_value.
  80. function! s:Fetch(Data, key)
  81. let len = len(a:Data[a:key])
  82. if a:Data.idx_crt_value >= len | let a:Data.idx_crt_value = 0 | endif
  83. let value = a:Data[a:key][a:Data.idx_crt_value]
  84. return value
  85. endfunction
  86. " Function: s:Search({Data},{value}) {{{3
  87. " Searches for the index of {value} in {Data.values} list. Return 0 if not
  88. " found.
  89. function! s:Search(Data, value)
  90. let idx = index(a:Data.values, a:value)
  91. " echo a:Data.variable . "[".idx."] == " . a:value
  92. return idx > 0 ? idx : 0 " default is first element
  93. endfunction
  94. " Function: s:SearchText({Data},{value}) {{{3
  95. " Searches for the index of {value} in {Data.values/text} list.
  96. " Returns -1 if not found.
  97. function! s:SearchText(Data, value)
  98. let labels_key = s:MenuKey(a:Data)
  99. let list = a:Data[labels_key]
  100. let idx = index(list, a:value)
  101. return idx
  102. endfunction
  103. " Function: s:Set({Data}) {{{3
  104. " @param[in,out] Data Menu item definition
  105. "
  106. " Sets the global variable associated to the menu item according to the item's
  107. " current value.
  108. function! s:Set(Data)
  109. let value = a:Data.values[a:Data.idx_crt_value]
  110. let variable = a:Data.variable
  111. if variable[0] == '$' " environment variabmes
  112. exe "let ".variable." = ".string(value)
  113. else
  114. let g:{variable} = value
  115. endif
  116. if has_key(a:Data, "actions")
  117. let l:Action = a:Data.actions[a:Data.idx_crt_value]
  118. if type(l:Action) == type(function('tr'))
  119. call l:Action()
  120. else
  121. exe l:Action
  122. endif
  123. endif
  124. if has_key(a:Data, "hook")
  125. let l:Action = a:Data.hook
  126. if type(l:Action) == type(function('tr'))
  127. call a:Data.hook()
  128. else
  129. exe l:Action
  130. endif
  131. endif
  132. return value
  133. endfunction
  134. " Function: s:MenuKey({Data}) {{{3
  135. " @return the table name from which the current value name (to dsplay in the
  136. " menu) must be fetched.
  137. " Priority is given to the optional "texts" table over the madatory "values" table.
  138. function! s:MenuKey(Data)
  139. if has_key(a:Data, "texts")
  140. let menu_id = "texts"
  141. else
  142. let menu_id = "values"
  143. endif
  144. return menu_id
  145. endfunction
  146. " Function: s:SetTextValue({Data},{TextValue}) {{{3
  147. " Force the value of the variable to the one associated to the {TextValue}
  148. " The menu, and the variable are updated in consequence.
  149. function! s:SetTextValue(Data, text)
  150. " Where the texts for values must be fetched
  151. let labels_key = s:MenuKey(a:Data)
  152. " Fetch the old current value
  153. let old = s:Fetch(a:Data, labels_key)
  154. let new_idx = s:SearchText(a:Data, a:text)
  155. if -1 == new_idx
  156. throw "toggle-menu: unsupported value for {".(a:Data.variable)."}"
  157. endif
  158. if a:Data.idx_crt_value == new_idx
  159. " value unchanged => abort
  160. return
  161. endif
  162. " Remove the entry from the menu
  163. call s:ClearMenu(a:Data.menu, old)
  164. " Cycle/increment the current value
  165. let a:Data.idx_crt_value = new_idx
  166. " Fetch it
  167. let new = s:Fetch(a:Data,labels_key)
  168. " Add the updated entry in the menu
  169. call s:UpdateMenu(a:Data.menu, new, a:Data.command)
  170. " Update the binded global variable
  171. let value = s:Set(a:Data)
  172. echo a:Data.variable.'='.value
  173. endfunction
  174. " Function: s:NextValue({Data}) {{{3
  175. " Change the value of the variable to the next in the list of value.
  176. " The menu, and the variable are updated in consequence.
  177. function! s:NextValue(Data)
  178. " Where the texts for values must be fetched
  179. let labels_key = s:MenuKey(a:Data)
  180. " Fetch the old current value
  181. let old = s:Fetch(a:Data, labels_key)
  182. " Remove the entry from the menu
  183. call s:ClearMenu(a:Data.menu, old)
  184. " Cycle/increment the current value
  185. let a:Data.idx_crt_value += 1
  186. " Fetch it
  187. let new = s:Fetch(a:Data,labels_key)
  188. " Add the updated entry in the menu
  189. call s:UpdateMenu(a:Data.menu, new, a:Data.command)
  190. " Update the binded global variable
  191. let value = s:Set(a:Data)
  192. echo a:Data.variable.'='.value
  193. endfunction
  194. " Function: s:ClearMenu({Menu}, {text}) {{{3
  195. " Removes a menu item
  196. "
  197. " @param[in] Menu.priority Priority of the new menu-item
  198. " @param[in] Menu.name Name of the new menu-item
  199. " @param[in] text Text of the previous value of the variable binded
  200. function! s:ClearMenu(Menu, text)
  201. if has('gui_running')
  202. let name = substitute(a:Menu.name, '&', '', 'g')
  203. let cmd = 'unmenu '.lh#menu#text(name.'<tab>('.a:text.')')
  204. silent! exe cmd
  205. endif
  206. endfunction
  207. " Function: s:UpdateMenu({Menu}, {text}, {command}) {{{3
  208. " Adds a new menu item, with the text associated to the current value in
  209. " braces.
  210. "
  211. " @param[in] Menu.priority Priority of the new menu-item
  212. " @param[in] Menu.name Name of the new menu-item
  213. " @param[in] text Text of the current value of the variable binded to
  214. " the menu-item
  215. " @param[in] command Toggle command to execute when the menu-item is selected
  216. function! s:UpdateMenu(Menu, text, command)
  217. if has('gui_running')
  218. let cmd = 'nnoremenu <silent> '.a:Menu.priority.' '.
  219. \ lh#menu#text(a:Menu.name.'<tab>('.a:text.')').
  220. \ ' :'.s:k_Toggle_cmd.' '.a:command."\<cr>"
  221. silent! exe cmd
  222. endif
  223. endfunction
  224. " Function: s:SaveData({Data}) {{{3
  225. " @param Data Menu-item definition
  226. " Saves {Data} as s:Data{s:data_id++}. The definition will be used by
  227. " automatically generated commands.
  228. " @return s:data_id
  229. let s:data_id = 0
  230. function! s:SaveData(Data)
  231. let s:Data{s:data_id} = a:Data
  232. let id = s:data_id
  233. let s:data_id += 1
  234. return id
  235. endfunction
  236. " Function: lh#menu#def_toggle_item({Data}) {{{3
  237. " @param Data.idx_crt_value
  238. " @param Data.definitions == [ {value:, menutext: } ]
  239. " @param Data.menu == { name:, position: }
  240. "
  241. " Sets a toggle-able menu-item defined by {Data}.
  242. "
  243. function! lh#menu#def_toggle_item(Data)
  244. " Save the menu data as an internal script variable
  245. let id = s:SaveData(a:Data)
  246. " If the index of the current value hasn't been set, fetch it from the
  247. " associated variable
  248. if !has_key(a:Data, "idx_crt_value")
  249. " Fetch the value of the associated variable
  250. let value = lh#option#get(a:Data.variable, 0, 'g')
  251. " echo a:Data.variable . " <- " . value
  252. " Update the index of the current value
  253. let a:Data.idx_crt_value = s:Search(a:Data, value)
  254. endif
  255. " Name of the auto-matically generated toggle command
  256. let cmdName = substitute(a:Data.menu.name, '[^a-zA-Z_]', '', 'g')
  257. " Lazy definition of the command
  258. if 2 != exists(':'.s:k_Toggle_cmd)
  259. exe 'command! -nargs=+ -complete=custom,lh#menu#_toggle_complete '
  260. \ . s:k_Toggle_cmd . ' :call s:Toggle(<f-args>)'
  261. endif
  262. " silent exe 'command! -nargs=0 '.cmdName.' :call s:NextValue(s:Data'.id.')'
  263. let s:toggle_commands[cmdName] = eval('s:Data'.id)
  264. let a:Data["command"] = cmdName
  265. " Add the menu entry according to the current value
  266. call s:UpdateMenu(a:Data.menu, s:Fetch(a:Data, s:MenuKey(a:Data)), cmdName)
  267. " Update the associated global variable
  268. call s:Set(a:Data)
  269. endfunction
  270. "------------------------------------------------------------------------
  271. function! s:Toggle(cmdName, ...)
  272. if !has_key(s:toggle_commands, a:cmdName)
  273. throw "toggle-menu: unknown toggable variable ".a:cmdName
  274. endif
  275. let data = s:toggle_commands[a:cmdName]
  276. if a:0 > 0
  277. call s:SetTextValue(data, a:1)
  278. else
  279. call s:NextValue(data)
  280. endif
  281. endfunction
  282. function! lh#menu#_toggle_complete(ArgLead, CmdLine, CursorPos)
  283. let cmdline = split(a:CmdLine)
  284. " echomsg "cmd line: " . string(cmdline)." # ". (a:CmdLine =~ ' $')
  285. let nb_args = len(cmdline)
  286. if (a:CmdLine !~ ' $')
  287. let nb_args -= 1
  288. endif
  289. " echomsg "nb args: ". nb_args
  290. if nb_args < 2
  291. return join(keys(s:toggle_commands),"\n")
  292. elseif nb_args == 2
  293. let variable = cmdline[1]
  294. if !has_key(s:toggle_commands, variable)
  295. throw "toggle-menu: unknown toggable variable ".variable
  296. endif
  297. let data = s:toggle_commands[variable]
  298. let labels_key = s:MenuKey(data)
  299. " echomsg "keys: ".string(data[labels_key])
  300. return join(data[labels_key], "\n")
  301. else
  302. return ''
  303. endif
  304. endfunction
  305. "------------------------------------------------------------------------
  306. " # IVN Menus {{{2
  307. " Function: s:CTRL_O({cmd}) {{{3
  308. " Build the command (sequence of ':ex commands') to be executed from
  309. " INSERT-mode.
  310. function! s:CTRL_O(cmd)
  311. return substitute(a:cmd, '\(^\|<CR>\):', '\1\<C-O>:', 'g')
  312. endfunction
  313. " Function: lh#menu#is_in_visual_mode() {{{3
  314. function! lh#menu#is_in_visual_mode()
  315. return exists('s:is_in_visual_mode') && s:is_in_visual_mode
  316. endfunction
  317. " Function: lh#menu#_CMD_and_clear_v({cmd}) {{{3
  318. " Internal function that executes the command and then clears the @v buffer
  319. " todo: save and restore @v,
  320. function! lh#menu#_CMD_and_clear_v(cmd)
  321. try
  322. let s:is_in_visual_mode = 1
  323. exe a:cmd
  324. finally
  325. let @v=''
  326. silent! unlet s:is_in_visual_mode
  327. endtry
  328. endfunction
  329. " Function: s:Build_CMD({prefix},{cmd}) {{{3
  330. " build the exact command to execute regarding the mode it is dedicated
  331. function! s:Build_CMD(prefix, cmd)
  332. if a:cmd[0] != ':' | return ' ' . a:cmd
  333. endif
  334. if a:prefix[0] == "i" | return ' ' . <SID>CTRL_O(a:cmd)
  335. elseif a:prefix[0] == "n" | return ' ' . a:cmd
  336. elseif a:prefix[0] == "v"
  337. if match(a:cmd, ":VCall") == 0
  338. return substitute(a:cmd, ':VCall', ' :call', ''). "\<cr>gV"
  339. " gV exit select-mode if we where in it!
  340. else
  341. return
  342. \ " \"vy\<C-C>:call lh#menu#_CMD_and_clear_v('" .
  343. \ substitute(a:cmd, "<CR>$", '', '') ."')\<cr>"
  344. endif
  345. elseif a:prefix[0] == "c" | return " \<C-C>" . a:cmd
  346. else | return ' ' . a:cmd
  347. endif
  348. endfunction
  349. " Function: lh#menu#map_all({map_type}, [{map args}...) {{{3
  350. " map the command to all the modes required
  351. function! lh#menu#map_all(map_type,...)
  352. let nore = (match(a:map_type, '[aincv]*noremap') != -1) ? "nore" : ""
  353. let prefix = matchstr(substitute(a:map_type, nore, '', ''), '[aincv]*')
  354. if a:1 == "<buffer>" | let i = 3 | let binding = a:1 . ' ' . a:2
  355. else | let i = 2 | let binding = a:1
  356. endif
  357. let binding = '<silent> ' . binding
  358. let cmd = a:{i}
  359. let i += 1
  360. while i <= a:0
  361. let cmd .= ' ' . a:{i}
  362. let i += 1
  363. endwhile
  364. let build_cmd = nore . 'map ' . binding
  365. while strlen(prefix)
  366. if prefix[0] == "a" | let prefix = "incv"
  367. else
  368. execute prefix[0] . build_cmd . <SID>Build_CMD(prefix[0],cmd)
  369. let prefix = strpart(prefix, 1)
  370. endif
  371. endwhile
  372. endfunction
  373. " Function: lh#menu#make({prefix},{code},{text},{binding},...) {{{3
  374. " Build the menu and map its associated binding to all the modes required
  375. function! lh#menu#make(prefix, code, text, binding, ...)
  376. let nore = (match(a:prefix, '[aincv]*nore') != -1) ? "nore" : ""
  377. let prefix = matchstr(substitute(a:prefix, nore, '', ''), '[aincv]*')
  378. let b = (a:1 == "<buffer>") ? 1 : 0
  379. let i = b + 1
  380. let cmd = a:{i}
  381. let i += 1
  382. while i <= a:0
  383. let cmd .= ' ' . a:{i}
  384. let i += 1
  385. endwhile
  386. let build_cmd = nore . "menu <silent> " . a:code . ' ' . lh#menu#text(a:text)
  387. if strlen(a:binding) != 0
  388. let build_cmd .= '<tab>' .
  389. \ substitute(lh#menu#text(a:binding), '&', '\0\0', 'g')
  390. if prefix == 'i' && exists('*IMAP')
  391. if b != 0
  392. call IMAP(a:binding, cmd, &ft)
  393. else
  394. call IMAP(a:binding, cmd)
  395. endif
  396. else
  397. if b != 0
  398. call lh#menu#map_all(prefix.nore."map", ' <buffer> '.a:binding, cmd)
  399. else
  400. call lh#menu#map_all(prefix.nore."map", a:binding, cmd)
  401. endif
  402. endif
  403. endif
  404. if has("gui_running")
  405. while strlen(prefix)
  406. execute <SID>BMenu(b).prefix[0].build_cmd.<SID>Build_CMD(prefix[0],cmd)
  407. let prefix = strpart(prefix, 1)
  408. endwhile
  409. endif
  410. endfunction
  411. " Function: s:BMenu({b}) {{{3
  412. " If <buffermenu.vim> is installed and the menu should be local, then the
  413. " apropriate string is returned.
  414. function! s:BMenu(b)
  415. let res = (a:b && exists(':Bmenu')
  416. \ && (1 == lh#option#get("want_buffermenu_or_global_disable", 1, "bg"))
  417. \) ? 'B' : ''
  418. " call confirm("BMenu(".a:b.")=".res, '&Ok', 1)
  419. return res
  420. endfunction
  421. " Function: lh#menu#IVN_make(...) {{{3
  422. function! lh#menu#IVN_make(code, text, binding, i_cmd, v_cmd, n_cmd, ...)
  423. " nore options
  424. let nore_i = (a:0 > 0) ? ((a:1 != 0) ? 'nore' : '') : ''
  425. let nore_v = (a:0 > 1) ? ((a:2 != 0) ? 'nore' : '') : ''
  426. let nore_n = (a:0 > 2) ? ((a:3 != 0) ? 'nore' : '') : ''
  427. "
  428. call lh#menu#make('i'.nore_i,a:code,a:text, a:binding, '<buffer>', a:i_cmd)
  429. call lh#menu#make('v'.nore_v,a:code,a:text, a:binding, '<buffer>', a:v_cmd)
  430. if strlen(a:n_cmd) != 0
  431. call lh#menu#make('n'.nore_n,a:code,a:text, a:binding, '<buffer>', a:n_cmd)
  432. endif
  433. endfunction
  434. "
  435. " Functions }}}1
  436. "------------------------------------------------------------------------
  437. let &cpo=s:cpo_save
  438. "=============================================================================
  439. " vim600: set fdm=marker: