"============================================================================= " File: plugin/ui-functions.vim {{{1 " Author: Luc Hermitte " " License: GPLv3 with exceptions " " Version: 3.0.0 " Created: 18th nov 2002 " Last Update: $Date: 2012-03-19 19:09:15 +0100 (Mon, 19 Mar 2012) $ (19th Mar 2012) "------------------------------------------------------------------------ " Description: Functions for the interaction with a User Interface. " The UI can be graphical or textual. " At first, this was designed to ease the syntax of " mu-template's templates. " " Option: {{{2 " {[bg]:ui_type} " = "g\%[ui]", " = "t\%[ext]" ; the call must not be |:silent| " = "f\%[te]" " }}}2 "------------------------------------------------------------------------ " Installation: Drop this into one of your {rtp}/plugin/ directories. " History: {{{2 " v0.01 Initial Version " v0.02 " (*) Code "factorisations" " (*) Help on enhanced. " (*) Small changes regarding the parameter accepted " (*) Function SWITCH " v0.03 " (*) Small bug fix with INPUT() " v0.04 " (*) New function: WHICH() " v0.05 " (*) In vim7e, inputdialog() returns a trailing '\n'. INPUT() strips the " NL character. " v0.06 " (*) :s/echoerr/throw/ => vim7 only " v2.2.0 " (*) menu to switch the ui_type " v2.2.6 " (*) CONFIRM() and WHICH() accept lists of {choices} " v3.0.0 GPLv3 " " TODO: {{{2 " (*) Save the hl-User1..9 before using them " (*) Possibility other than &statusline: " echohl User1 |echon "bla"|echohl User2|echon "bli"|echohl None " (*) Wraps too long choices-line (length > term-width) " (*) Add to the documentation: "don't use CTRL-C to abort !!" " (*) Look if I need to support 'wildmode' " (*) 3rd mode: return string for FTE " (*) 4th mode: interaction in a scratch buffer " " }}}1 "============================================================================= " Avoid reinclusion {{{1 " if exists("g:loaded_ui_functions") && !exists('g:force_reload_ui_functions') finish endif let g:loaded_ui_functions = 1 let s:cpo_save=&cpo set cpo&vim " }}}1 "------------------------------------------------------------------------ " External functions {{{1 " Function: IF(var, then, else) {{{2 function! IF(var,then, else) let o = s:Opt_type() " {{{3 if o =~ 'g\%[ui]\|t\%[ext]' " {{{4 return a:var ? a:then : a:else elseif o =~ 'f\%[te]' " {{{4 return s:if_fte(a:var, a:then, a:else) else " {{{4 throw "UI-Fns::IF(): Unkonwn user-interface style (".o.")" endif " }}}3 endfunction " Function: SWITCH(var, case, action [, case, action] [default_action]) {{{2 function! SWITCH(var, ...) let o = s:Opt_type() " {{{3 if o =~ 'g\%[ui]\|t\%[ext]' " {{{4 let explicit_def = ((a:0 % 2) == 1) let default = explicit_def ? a:{a:0} : '' let i = a:0 - 1 - explicit_def while i > 0 if a:var == a:{i} return a:{i+1} endif let i -= 2 endwhile return default elseif o =~ 'f\%[te]' " {{{4 return s:if_fte(a:var, a:then, a:else) else " {{{4 throw "UI-Fns::SWITCH(): Unkonwn user-interface style (".o.")" endif " }}}3 endfunction " Function: CONFIRM(text [, choices [, default [, type]]]) {{{2 function! CONFIRM(text, ...) " 1- Check parameters {{{3 if a:0 > 4 " {{{4 throw "UI-Fns::CONFIRM(): too many parameters" return 0 endif " build the parameters string {{{4 let i = 1 while i <= a:0 if i == 1 if type(a:1) == type([]) let params = string(join(a:1, "\n")) else let params = 'a:{1}' endif else | let params .= ',a:{'.i.'}' endif let i += 1 endwhile " 2- Choose the correct way to execute according to the option {{{3 let o = s:Opt_type() if o =~ 'g\%[ui]' " {{{4 exe 'return confirm(a:text,'.params.')' elseif o =~ 't\%[ext]' " {{{4 if !has('gui_running') && has('dialog_con') exe 'return confirm(a:text,'.params.')' else exe 'return s:confirm_text("none", a:text,'.params.')' endif elseif o =~ 'f\%[te]' " {{{4 exe 'return s:confirm_fte(a:text,'.params.')' else " {{{4 throw "UI-Fns::CONFIRM(): Unkonwn user-interface style (".o.")" endif " }}}3 endfunction " Function: INPUT(prompt [, default ]) {{{2 function! INPUT(prompt, ...) " 1- Check parameters {{{3 if a:0 > 4 " {{{4 throw "UI-Fns::INPUT(): too many parameters" return 0 endif " build the parameters string {{{4 let i = 1 | let params = '' while i <= a:0 if i == 1 | let params = 'a:{1}' else | let params .= ',a:{'.i.'}' endif let i += 1 endwhile " 2- Choose the correct way to execute according to the option {{{3 let o = s:Opt_type() if o =~ 'g\%[ui]' " {{{4 exe 'return matchstr(inputdialog(a:prompt,'.params.'), ".\\{-}\\ze\\n\\=$")' elseif o =~ 't\%[ext]' " {{{4 exe 'return input(a:prompt,'.params.')' elseif o =~ 'f\%[te]' " {{{4 exe 'return s:input_fte(a:prompt,'.params.')' else " {{{4 throw "UI-Fns::INPUT(): Unkonwn user-interface style (".o.")" endif " }}}3 endfunction " Function: COMBO(prompt, choice [, ... ]) {{{2 function! COMBO(prompt, ...) " 1- Check parameters {{{3 if a:0 > 4 " {{{4 throw "UI-Fns::COMBO(): too many parameters" return 0 endif " build the parameters string {{{4 let i = 1 while i <= a:0 if i == 1 | let params = 'a:{1}' else | let params .= ',a:{'.i.'}' endif let i += 1 endwhile " 2- Choose the correct way to execute according to the option {{{3 let o = s:Opt_type() if o =~ 'g\%[ui]' " {{{4 exe 'return confirm(a:prompt,'.params.')' elseif o =~ 't\%[ext]' " {{{4 exe 'return s:confirm_text("combo", a:prompt,'.params.')' elseif o =~ 'f\%[te]' " {{{4 exe 'return s:combo_fte(a:prompt,'.params.')' else " {{{4 throw "UI-Fns::COMBO(): Unkonwn user-interface style (".o.")" endif " }}}3 endfunction " Function: WHICH(function, prompt, choice [, ... ]) {{{2 function! WHICH(fn, prompt, ...) " 1- Check parameters {{{3 " build the parameters string {{{4 let i = 1 while i <= a:0 if i == 1 if type(a:1) == type([]) let choices = a:1 else let choices = split(a:1, "\n") endif let params = 'a:{1}' else | let params .= ',a:{'.i.'}' endif let i += 1 endwhile " 2- Execute the function {{{3 exe 'let which = '.a:fn.'(a:prompt,'.params.')' if 0 >= which | return '' else return substitute(choices[which-1], '&', '', '') endif " }}}3 endfunction " Function: CHECK(prompt, choice [, ... ]) {{{2 function! CHECK(prompt, ...) " 1- Check parameters {{{3 if a:0 > 4 " {{{4 throw "UI-Fns::CHECK(): too many parameters" return 0 endif " build the parameters string {{{4 let i = 1 while i <= a:0 if i == 1 | let params = 'a:{1}' else | let params .= ',a:{'.i.'}' endif let i += 1 endwhile " 2- Choose the correct way to execute according to the option {{{3 let o = s:Opt_type() if o =~ 'g\%[ui]' " {{{4 exe 'return s:confirm_text("check", a:prompt,'.params.')' elseif o =~ 't\%[ext]' " {{{4 exe 'return s:confirm_text("check", a:prompt,'.params.')' elseif o =~ 'f\%[te]' " {{{4 exe 'return s:check_fte(a:prompt,'.params.')' else " {{{4 throw "UI-Fns::CHECK(): Unkonwn user-interface style (".o.")" endif " }}}3 endfunction " }}}1 "------------------------------------------------------------------------ " Options setting {{{1 let s:OptionData = { \ "variable": "ui_type", \ "idx_crt_value": 1, \ "values": ['gui', 'text', 'fte'], \ "menu": { "priority": '500.2700', "name": '&Plugin.&LH.&UI type'} \} call lh#menu#def_toggle_item(s:OptionData) " }}}1 "------------------------------------------------------------------------ " Internal functions {{{1 function! s:Option(name, default) " {{{2 if exists('b:ui_'.a:name) | return b:ui_{a:name} elseif exists('g:ui_'.a:name) | return g:ui_{a:name} else | return a:default endif endfunction function! s:Opt_type() " {{{2 return s:Option('type', 'gui') endfunction " " Function: s:status_line(current, hl [, choices] ) {{{2 " a:current: current item " a:hl : Generic, Warning, Error function! s:status_line(current, hl, ...) " Highlightning {{{3 if a:hl == "Generic" | let hl = '%1*' elseif a:hl == "Warning" | let hl = '%2*' elseif a:hl == "Error" | let hl = '%3*' elseif a:hl == "Info" | let hl = '%4*' elseif a:hl == "Question" | let hl = '%5*' else | let hl = '%1*' endif " Build the string {{{3 let sl_choices = '' | let i = 1 while i <= a:0 if i == a:current let sl_choices .= ' '. hl . \ substitute(a:{i}, '&\(.\)', '%6*\1'.hl, '') . '%* ' else let sl_choices .= ' ' . \ substitute(a:{i}, '&\(.\)', '%6*\1%*', '') . ' ' endif let i += 1 endwhile " }}}3 return sl_choices endfunction " Function: s:confirm_text(box, text [, choices [, default [, type]]]) {{{2 function! s:confirm_text(box, text, ...) let help = "///////" " 1- Retrieve the parameters {{{3 let choices = ((a:0>=1) ? a:1 : '&Ok') let default = ((a:0>=2) ? a:2 : (('check' == a:box) ? 0 : 1)) let type = ((a:0>=3) ? a:3 : 'Generic') if 'none' == a:box | let prefix = '' elseif 'combo' == a:box | let prefix = '( )_' elseif 'check' == a:box | let prefix = '[ ]_' let help = '/ '.help else | let prefix = '' endif " 2- Retrieve the proposed choices {{{3 " Prepare the hot keys let i = 0 while i != 26 let hotkey_{nr2char(i+65)} = 0 let i += 1 endwhile let hotkeys = '' | let help_k = '/' " Parse the choices let i = 0 while choices != "" let i += 1 let item = matchstr(choices, "^.\\{-}\\ze\\(\n\\|$\\)") let choices = matchstr(choices, "\n\\zs.*$") " exe 'anoremenu ]'.a:text.'.'.item.' :let s:choice ='.i.'' if ('check' == a:box) && (strlen(default)>=i) && (1 == default[i-1]) " let choice_{i} = '[X]' . substitute(item, '&', '', '') let choice_{i} = '[X]_' . item else " let choice_{i} = prefix . substitute(item, '&', '', '') let choice_{i} = prefix . item endif if i == 1 let list_choices = 'choice_{1}' else let list_choices .= ',choice_{'.i.'}' endif " Update the hotkey. let key = toupper(matchstr(choice_{i}, '&\zs.\ze')) let hotkey_{key} = i let hotkeys .= tolower(key) . toupper(key) let help_k .= tolower(key) endwhile let nb_choices = i if default > nb_choices | let default = nb_choices | endif " 3- Run an interactive text menu {{{3 " Note: emenu can not be used through ":exe" {{{4 " let wcm = &wcm " set wcm= " exe ':emenu ]'.a:text.'.'."" " let &wcm = wcm " 3.1- Preparations for the statusline {{{4 " save the statusline let sl = &l:statusline " Color schemes for selected item {{{5 :hi User1 term=inverse,bold cterm=inverse,bold ctermfg=Yellow \ guifg=Black guibg=Yellow :hi User2 term=inverse,bold cterm=inverse,bold ctermfg=LightRed \ guifg=Black guibg=LightRed :hi User3 term=inverse,bold cterm=inverse,bold ctermfg=Red \ guifg=Black guibg=Red :hi User4 term=inverse,bold cterm=inverse,bold ctermfg=Cyan \ guifg=Black guibg=Cyan :hi User5 term=inverse,bold cterm=inverse,bold ctermfg=LightYellow \ guifg=Black guibg=LightYellow :hi User6 term=inverse,bold cterm=inverse,bold ctermfg=LightGray \ guifg=DarkRed guibg=LightGray " }}}5 " 3.2- Interactive loop {{{4 let help = "\r-- Keys available (".help_k.help.")" " item selected at the start let i = ('check' != a:box) ? default : 1 let direction = 0 | let toggle = 0 while 1 if 'combo' == a:box let choice_{i} = substitute(choice_{i}, '^( )', '(*)', '') endif " Colored statusline " Note: unfortunately the 'statusline' is a global option, {{{ " not a local one. I the hope that may change, as it does not provokes any " error, I use '&l:statusline'. }}} exe 'let &l:statusline=s:status_line(i, type,'. list_choices .')' if has(':redrawstatus') redrawstatus! else redraw! endif " Echo the current selection echo "\r". a:text.' '.substitute(choice_{i}, '&', '', '') " Wait the user to hit a key let key=getchar() let complType=nr2char(key) " If the key hit matched awaited keys ... if -1 != stridx(" \\\".hotkeys,complType) || \ (key =~ "\\\|\\\|\\\|\") if key == "\" " Help {{{5 redraw! echohl StatusLineNC echo help echohl None let key=getchar() let complType=nr2char(key) endif " TODO: support CTRL-D if complType == "\" " Validate {{{5 break elseif complType == " " " check box {{{5 let toggle = 1 elseif complType == "\" " Abort {{{5 let i = -1 | break elseif complType == "\" || key == "\" " Next {{{5 let direction = 1 elseif key =~ "\\\|\" " Previous {{{5 let direction = -1 elseif -1 != stridx(hotkeys, complType ) " Hotkeys {{{5 if '' == complType | continue | endif let direction = hotkey_{toupper(complType)} - i let toggle = 1 " else endif " }}}5 endif if direction != 0 " {{{5 if 'combo' == a:box let choice_{i} = substitute(choice_{i}, '^(\*)', '( )', '') endif let i += direction if i > nb_choices | let i = 1 elseif i == 0 | let i = nb_choices endif let direction = 0 endif if toggle == 1 " {{{5 if 'check' == a:box let choice_{i} = ((choice_{i}[1] == ' ')? '[X]' : '[ ]') \ . strpart(choice_{i}, 3) endif let toggle = 0 endif endwhile " }}}4 " 4- Terminate {{{3 " Clear screen redraw! " Restore statusline let &l:statusline=sl " Return if (i == -1) || ('check' != a:box) return i else let r = '' | let i = 1 while i <= nb_choices let r .= ((choice_{i}[1] == 'X') ? '1' : '0') let i += 1 endwhile return r endif endfunction " }}}1 "------------------------------------------------------------------------ " Functions that insert fte statements {{{1 " Function: s:if_fte(var, then, else) {{{2 " Function: s:confirm_fte(text, [, choices [, default [, type]]]) {{{2 " Function: s:input_fte(prompt [, default]) {{{2 " Function: s:combo_fte(prompt, choice [, ...]) {{{2 " Function: s:check_fte(prompt, choice [, ...]) {{{2 " }}}1 "------------------------------------------------------------------------ let &cpo=s:cpo_save "============================================================================= " vim600: set fdm=marker: