My dotfiles. Period.

ui-functions.vim 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  1. "=============================================================================
  2. " File: plugin/ui-functions.vim {{{1
  3. " Author: Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
  4. " <URL:http://code.google.com/p/lh-vim/>
  5. " License: GPLv3 with exceptions
  6. " <URL:http://code.google.com/p/lh-vim/wiki/License>
  7. " Version: 3.0.0
  8. " Created: 18th nov 2002
  9. " Last Update: $Date: 2012-03-19 19:09:15 +0100 (Mon, 19 Mar 2012) $ (19th Mar 2012)
  10. "------------------------------------------------------------------------
  11. " Description: Functions for the interaction with a User Interface.
  12. " The UI can be graphical or textual.
  13. " At first, this was designed to ease the syntax of
  14. " mu-template's templates.
  15. "
  16. " Option: {{{2
  17. " {[bg]:ui_type}
  18. " = "g\%[ui]",
  19. " = "t\%[ext]" ; the call must not be |:silent|
  20. " = "f\%[te]"
  21. " }}}2
  22. "------------------------------------------------------------------------
  23. " Installation: Drop this into one of your {rtp}/plugin/ directories.
  24. " History: {{{2
  25. " v0.01 Initial Version
  26. " v0.02
  27. " (*) Code "factorisations"
  28. " (*) Help on <F1> enhanced.
  29. " (*) Small changes regarding the parameter accepted
  30. " (*) Function SWITCH
  31. " v0.03
  32. " (*) Small bug fix with INPUT()
  33. " v0.04
  34. " (*) New function: WHICH()
  35. " v0.05
  36. " (*) In vim7e, inputdialog() returns a trailing '\n'. INPUT() strips the
  37. " NL character.
  38. " v0.06
  39. " (*) :s/echoerr/throw/ => vim7 only
  40. " v2.2.0
  41. " (*) menu to switch the ui_type
  42. " v2.2.6
  43. " (*) CONFIRM() and WHICH() accept lists of {choices}
  44. " v3.0.0 GPLv3
  45. "
  46. " TODO: {{{2
  47. " (*) Save the hl-User1..9 before using them
  48. " (*) Possibility other than &statusline:
  49. " echohl User1 |echon "bla"|echohl User2|echon "bli"|echohl None
  50. " (*) Wraps too long choices-line (length > term-width)
  51. " (*) Add to the documentation: "don't use CTRL-C to abort !!"
  52. " (*) Look if I need to support 'wildmode'
  53. " (*) 3rd mode: return string for FTE
  54. " (*) 4th mode: interaction in a scratch buffer
  55. "
  56. " }}}1
  57. "=============================================================================
  58. " Avoid reinclusion {{{1
  59. "
  60. if exists("g:loaded_ui_functions") && !exists('g:force_reload_ui_functions')
  61. finish
  62. endif
  63. let g:loaded_ui_functions = 1
  64. let s:cpo_save=&cpo
  65. set cpo&vim
  66. " }}}1
  67. "------------------------------------------------------------------------
  68. " External functions {{{1
  69. " Function: IF(var, then, else) {{{2
  70. function! IF(var,then, else)
  71. let o = s:Opt_type() " {{{3
  72. if o =~ 'g\%[ui]\|t\%[ext]' " {{{4
  73. return a:var ? a:then : a:else
  74. elseif o =~ 'f\%[te]' " {{{4
  75. return s:if_fte(a:var, a:then, a:else)
  76. else " {{{4
  77. throw "UI-Fns::IF(): Unkonwn user-interface style (".o.")"
  78. endif
  79. " }}}3
  80. endfunction
  81. " Function: SWITCH(var, case, action [, case, action] [default_action]) {{{2
  82. function! SWITCH(var, ...)
  83. let o = s:Opt_type() " {{{3
  84. if o =~ 'g\%[ui]\|t\%[ext]' " {{{4
  85. let explicit_def = ((a:0 % 2) == 1)
  86. let default = explicit_def ? a:{a:0} : ''
  87. let i = a:0 - 1 - explicit_def
  88. while i > 0
  89. if a:var == a:{i}
  90. return a:{i+1}
  91. endif
  92. let i -= 2
  93. endwhile
  94. return default
  95. elseif o =~ 'f\%[te]' " {{{4
  96. return s:if_fte(a:var, a:then, a:else)
  97. else " {{{4
  98. throw "UI-Fns::SWITCH(): Unkonwn user-interface style (".o.")"
  99. endif
  100. " }}}3
  101. endfunction
  102. " Function: CONFIRM(text [, choices [, default [, type]]]) {{{2
  103. function! CONFIRM(text, ...)
  104. " 1- Check parameters {{{3
  105. if a:0 > 4 " {{{4
  106. throw "UI-Fns::CONFIRM(): too many parameters"
  107. return 0
  108. endif
  109. " build the parameters string {{{4
  110. let i = 1
  111. while i <= a:0
  112. if i == 1
  113. if type(a:1) == type([])
  114. let params = string(join(a:1, "\n"))
  115. else
  116. let params = 'a:{1}'
  117. endif
  118. else | let params .= ',a:{'.i.'}'
  119. endif
  120. let i += 1
  121. endwhile
  122. " 2- Choose the correct way to execute according to the option {{{3
  123. let o = s:Opt_type()
  124. if o =~ 'g\%[ui]' " {{{4
  125. exe 'return confirm(a:text,'.params.')'
  126. elseif o =~ 't\%[ext]' " {{{4
  127. if !has('gui_running') && has('dialog_con')
  128. exe 'return confirm(a:text,'.params.')'
  129. else
  130. exe 'return s:confirm_text("none", a:text,'.params.')'
  131. endif
  132. elseif o =~ 'f\%[te]' " {{{4
  133. exe 'return s:confirm_fte(a:text,'.params.')'
  134. else " {{{4
  135. throw "UI-Fns::CONFIRM(): Unkonwn user-interface style (".o.")"
  136. endif
  137. " }}}3
  138. endfunction
  139. " Function: INPUT(prompt [, default ]) {{{2
  140. function! INPUT(prompt, ...)
  141. " 1- Check parameters {{{3
  142. if a:0 > 4 " {{{4
  143. throw "UI-Fns::INPUT(): too many parameters"
  144. return 0
  145. endif
  146. " build the parameters string {{{4
  147. let i = 1 | let params = ''
  148. while i <= a:0
  149. if i == 1 | let params = 'a:{1}'
  150. else | let params .= ',a:{'.i.'}'
  151. endif
  152. let i += 1
  153. endwhile
  154. " 2- Choose the correct way to execute according to the option {{{3
  155. let o = s:Opt_type()
  156. if o =~ 'g\%[ui]' " {{{4
  157. exe 'return matchstr(inputdialog(a:prompt,'.params.'), ".\\{-}\\ze\\n\\=$")'
  158. elseif o =~ 't\%[ext]' " {{{4
  159. exe 'return input(a:prompt,'.params.')'
  160. elseif o =~ 'f\%[te]' " {{{4
  161. exe 'return s:input_fte(a:prompt,'.params.')'
  162. else " {{{4
  163. throw "UI-Fns::INPUT(): Unkonwn user-interface style (".o.")"
  164. endif
  165. " }}}3
  166. endfunction
  167. " Function: COMBO(prompt, choice [, ... ]) {{{2
  168. function! COMBO(prompt, ...)
  169. " 1- Check parameters {{{3
  170. if a:0 > 4 " {{{4
  171. throw "UI-Fns::COMBO(): too many parameters"
  172. return 0
  173. endif
  174. " build the parameters string {{{4
  175. let i = 1
  176. while i <= a:0
  177. if i == 1 | let params = 'a:{1}'
  178. else | let params .= ',a:{'.i.'}'
  179. endif
  180. let i += 1
  181. endwhile
  182. " 2- Choose the correct way to execute according to the option {{{3
  183. let o = s:Opt_type()
  184. if o =~ 'g\%[ui]' " {{{4
  185. exe 'return confirm(a:prompt,'.params.')'
  186. elseif o =~ 't\%[ext]' " {{{4
  187. exe 'return s:confirm_text("combo", a:prompt,'.params.')'
  188. elseif o =~ 'f\%[te]' " {{{4
  189. exe 'return s:combo_fte(a:prompt,'.params.')'
  190. else " {{{4
  191. throw "UI-Fns::COMBO(): Unkonwn user-interface style (".o.")"
  192. endif
  193. " }}}3
  194. endfunction
  195. " Function: WHICH(function, prompt, choice [, ... ]) {{{2
  196. function! WHICH(fn, prompt, ...)
  197. " 1- Check parameters {{{3
  198. " build the parameters string {{{4
  199. let i = 1
  200. while i <= a:0
  201. if i == 1
  202. if type(a:1) == type([])
  203. let choices = a:1
  204. else
  205. let choices = split(a:1, "\n")
  206. endif
  207. let params = 'a:{1}'
  208. else | let params .= ',a:{'.i.'}'
  209. endif
  210. let i += 1
  211. endwhile
  212. " 2- Execute the function {{{3
  213. exe 'let which = '.a:fn.'(a:prompt,'.params.')'
  214. if 0 >= which | return ''
  215. else
  216. return substitute(choices[which-1], '&', '', '')
  217. endif
  218. " }}}3
  219. endfunction
  220. " Function: CHECK(prompt, choice [, ... ]) {{{2
  221. function! CHECK(prompt, ...)
  222. " 1- Check parameters {{{3
  223. if a:0 > 4 " {{{4
  224. throw "UI-Fns::CHECK(): too many parameters"
  225. return 0
  226. endif
  227. " build the parameters string {{{4
  228. let i = 1
  229. while i <= a:0
  230. if i == 1 | let params = 'a:{1}'
  231. else | let params .= ',a:{'.i.'}'
  232. endif
  233. let i += 1
  234. endwhile
  235. " 2- Choose the correct way to execute according to the option {{{3
  236. let o = s:Opt_type()
  237. if o =~ 'g\%[ui]' " {{{4
  238. exe 'return s:confirm_text("check", a:prompt,'.params.')'
  239. elseif o =~ 't\%[ext]' " {{{4
  240. exe 'return s:confirm_text("check", a:prompt,'.params.')'
  241. elseif o =~ 'f\%[te]' " {{{4
  242. exe 'return s:check_fte(a:prompt,'.params.')'
  243. else " {{{4
  244. throw "UI-Fns::CHECK(): Unkonwn user-interface style (".o.")"
  245. endif
  246. " }}}3
  247. endfunction
  248. " }}}1
  249. "------------------------------------------------------------------------
  250. " Options setting {{{1
  251. let s:OptionData = {
  252. \ "variable": "ui_type",
  253. \ "idx_crt_value": 1,
  254. \ "values": ['gui', 'text', 'fte'],
  255. \ "menu": { "priority": '500.2700', "name": '&Plugin.&LH.&UI type'}
  256. \}
  257. call lh#menu#def_toggle_item(s:OptionData)
  258. " }}}1
  259. "------------------------------------------------------------------------
  260. " Internal functions {{{1
  261. function! s:Option(name, default) " {{{2
  262. if exists('b:ui_'.a:name) | return b:ui_{a:name}
  263. elseif exists('g:ui_'.a:name) | return g:ui_{a:name}
  264. else | return a:default
  265. endif
  266. endfunction
  267. function! s:Opt_type() " {{{2
  268. return s:Option('type', 'gui')
  269. endfunction
  270. "
  271. " Function: s:status_line(current, hl [, choices] ) {{{2
  272. " a:current: current item
  273. " a:hl : Generic, Warning, Error
  274. function! s:status_line(current, hl, ...)
  275. " Highlightning {{{3
  276. if a:hl == "Generic" | let hl = '%1*'
  277. elseif a:hl == "Warning" | let hl = '%2*'
  278. elseif a:hl == "Error" | let hl = '%3*'
  279. elseif a:hl == "Info" | let hl = '%4*'
  280. elseif a:hl == "Question" | let hl = '%5*'
  281. else | let hl = '%1*'
  282. endif
  283. " Build the string {{{3
  284. let sl_choices = '' | let i = 1
  285. while i <= a:0
  286. if i == a:current
  287. let sl_choices .= ' '. hl .
  288. \ substitute(a:{i}, '&\(.\)', '%6*\1'.hl, '') . '%* '
  289. else
  290. let sl_choices .= ' ' .
  291. \ substitute(a:{i}, '&\(.\)', '%6*\1%*', '') . ' '
  292. endif
  293. let i += 1
  294. endwhile
  295. " }}}3
  296. return sl_choices
  297. endfunction
  298. " Function: s:confirm_text(box, text [, choices [, default [, type]]]) {{{2
  299. function! s:confirm_text(box, text, ...)
  300. let help = "/<esc>/<s-tab>/<tab>/<left>/<right>/<cr>/<F1>"
  301. " 1- Retrieve the parameters {{{3
  302. let choices = ((a:0>=1) ? a:1 : '&Ok')
  303. let default = ((a:0>=2) ? a:2 : (('check' == a:box) ? 0 : 1))
  304. let type = ((a:0>=3) ? a:3 : 'Generic')
  305. if 'none' == a:box | let prefix = ''
  306. elseif 'combo' == a:box | let prefix = '( )_'
  307. elseif 'check' == a:box | let prefix = '[ ]_'
  308. let help = '/ '.help
  309. else | let prefix = ''
  310. endif
  311. " 2- Retrieve the proposed choices {{{3
  312. " Prepare the hot keys
  313. let i = 0
  314. while i != 26
  315. let hotkey_{nr2char(i+65)} = 0
  316. let i += 1
  317. endwhile
  318. let hotkeys = '' | let help_k = '/'
  319. " Parse the choices
  320. let i = 0
  321. while choices != ""
  322. let i += 1
  323. let item = matchstr(choices, "^.\\{-}\\ze\\(\n\\|$\\)")
  324. let choices = matchstr(choices, "\n\\zs.*$")
  325. " exe 'anoremenu ]'.a:text.'.'.item.' :let s:choice ='.i.'<cr>'
  326. if ('check' == a:box) && (strlen(default)>=i) && (1 == default[i-1])
  327. " let choice_{i} = '[X]' . substitute(item, '&', '', '')
  328. let choice_{i} = '[X]_' . item
  329. else
  330. " let choice_{i} = prefix . substitute(item, '&', '', '')
  331. let choice_{i} = prefix . item
  332. endif
  333. if i == 1
  334. let list_choices = 'choice_{1}'
  335. else
  336. let list_choices .= ',choice_{'.i.'}'
  337. endif
  338. " Update the hotkey.
  339. let key = toupper(matchstr(choice_{i}, '&\zs.\ze'))
  340. let hotkey_{key} = i
  341. let hotkeys .= tolower(key) . toupper(key)
  342. let help_k .= tolower(key)
  343. endwhile
  344. let nb_choices = i
  345. if default > nb_choices | let default = nb_choices | endif
  346. " 3- Run an interactive text menu {{{3
  347. " Note: emenu can not be used through ":exe" {{{4
  348. " let wcm = &wcm
  349. " set wcm=<tab>
  350. " exe ':emenu ]'.a:text.'.'."<tab>"
  351. " let &wcm = wcm
  352. " 3.1- Preparations for the statusline {{{4
  353. " save the statusline
  354. let sl = &l:statusline
  355. " Color schemes for selected item {{{5
  356. :hi User1 term=inverse,bold cterm=inverse,bold ctermfg=Yellow
  357. \ guifg=Black guibg=Yellow
  358. :hi User2 term=inverse,bold cterm=inverse,bold ctermfg=LightRed
  359. \ guifg=Black guibg=LightRed
  360. :hi User3 term=inverse,bold cterm=inverse,bold ctermfg=Red
  361. \ guifg=Black guibg=Red
  362. :hi User4 term=inverse,bold cterm=inverse,bold ctermfg=Cyan
  363. \ guifg=Black guibg=Cyan
  364. :hi User5 term=inverse,bold cterm=inverse,bold ctermfg=LightYellow
  365. \ guifg=Black guibg=LightYellow
  366. :hi User6 term=inverse,bold cterm=inverse,bold ctermfg=LightGray
  367. \ guifg=DarkRed guibg=LightGray
  368. " }}}5
  369. " 3.2- Interactive loop {{{4
  370. let help = "\r-- Keys available (".help_k.help.")"
  371. " item selected at the start
  372. let i = ('check' != a:box) ? default : 1
  373. let direction = 0 | let toggle = 0
  374. while 1
  375. if 'combo' == a:box
  376. let choice_{i} = substitute(choice_{i}, '^( )', '(*)', '')
  377. endif
  378. " Colored statusline
  379. " Note: unfortunately the 'statusline' is a global option, {{{
  380. " not a local one. I the hope that may change, as it does not provokes any
  381. " error, I use '&l:statusline'. }}}
  382. exe 'let &l:statusline=s:status_line(i, type,'. list_choices .')'
  383. if has(':redrawstatus')
  384. redrawstatus!
  385. else
  386. redraw!
  387. endif
  388. " Echo the current selection
  389. echo "\r". a:text.' '.substitute(choice_{i}, '&', '', '')
  390. " Wait the user to hit a key
  391. let key=getchar()
  392. let complType=nr2char(key)
  393. " If the key hit matched awaited keys ...
  394. if -1 != stridx(" \<tab>\<esc>\<enter>".hotkeys,complType) ||
  395. \ (key =~ "\<F1>\\|\<right>\\|\<left>\\|\<s-tab>")
  396. if key == "\<F1>" " Help {{{5
  397. redraw!
  398. echohl StatusLineNC
  399. echo help
  400. echohl None
  401. let key=getchar()
  402. let complType=nr2char(key)
  403. endif
  404. " TODO: support CTRL-D
  405. if complType == "\<enter>" " Validate {{{5
  406. break
  407. elseif complType == " " " check box {{{5
  408. let toggle = 1
  409. elseif complType == "\<esc>" " Abort {{{5
  410. let i = -1 | break
  411. elseif complType == "\<tab>" || key == "\<right>" " Next {{{5
  412. let direction = 1
  413. elseif key =~ "\<left>\\|\<s-tab>" " Previous {{{5
  414. let direction = -1
  415. elseif -1 != stridx(hotkeys, complType ) " Hotkeys {{{5
  416. if '' == complType | continue | endif
  417. let direction = hotkey_{toupper(complType)} - i
  418. let toggle = 1
  419. " else
  420. endif
  421. " }}}5
  422. endif
  423. if direction != 0 " {{{5
  424. if 'combo' == a:box
  425. let choice_{i} = substitute(choice_{i}, '^(\*)', '( )', '')
  426. endif
  427. let i += direction
  428. if i > nb_choices | let i = 1
  429. elseif i == 0 | let i = nb_choices
  430. endif
  431. let direction = 0
  432. endif
  433. if toggle == 1 " {{{5
  434. if 'check' == a:box
  435. let choice_{i} = ((choice_{i}[1] == ' ')? '[X]' : '[ ]')
  436. \ . strpart(choice_{i}, 3)
  437. endif
  438. let toggle = 0
  439. endif
  440. endwhile " }}}4
  441. " 4- Terminate {{{3
  442. " Clear screen
  443. redraw!
  444. " Restore statusline
  445. let &l:statusline=sl
  446. " Return
  447. if (i == -1) || ('check' != a:box)
  448. return i
  449. else
  450. let r = '' | let i = 1
  451. while i <= nb_choices
  452. let r .= ((choice_{i}[1] == 'X') ? '1' : '0')
  453. let i += 1
  454. endwhile
  455. return r
  456. endif
  457. endfunction
  458. " }}}1
  459. "------------------------------------------------------------------------
  460. " Functions that insert fte statements {{{1
  461. " Function: s:if_fte(var, then, else) {{{2
  462. " Function: s:confirm_fte(text, [, choices [, default [, type]]]) {{{2
  463. " Function: s:input_fte(prompt [, default]) {{{2
  464. " Function: s:combo_fte(prompt, choice [, ...]) {{{2
  465. " Function: s:check_fte(prompt, choice [, ...]) {{{2
  466. " }}}1
  467. "------------------------------------------------------------------------
  468. let &cpo=s:cpo_save
  469. "=============================================================================
  470. " vim600: set fdm=marker: