6e679d6d4f
--HG-- extra : rebase_source : 17ea9665bc826a9365264122781e1a7f99948b34
481 lines
15 KiB
VimL
481 lines
15 KiB
VimL
"=============================================================================
|
|
" File: plugin/ui-functions.vim {{{1
|
|
" Author: Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
|
|
" <URL:http://code.google.com/p/lh-vim/>
|
|
" URL: http://hermitte.free.fr/vim/ressources/vimfiles/plugin/ui-functions.vim
|
|
"
|
|
" Version: 2.2.1
|
|
" Created: 18th nov 2002
|
|
" Last Update: 28th Nov 2007
|
|
"------------------------------------------------------------------------
|
|
" 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 <F1> 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
|
|
"
|
|
" 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 = 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 | let params = 'a:{1}'
|
|
else | let params = params. ',a:{'.i.'}'
|
|
endif
|
|
let i = 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 = params. ',a:{'.i.'}'
|
|
endif
|
|
let i = 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 = params. ',a:{'.i.'}'
|
|
endif
|
|
let i = 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 | let params = 'a:{1}'
|
|
else | let params = params. ',a:{'.i.'}'
|
|
endif
|
|
let i = i + 1
|
|
endwhile
|
|
" 2- Execute the function {{{3
|
|
exe 'let which = '.a:fn.'(a:prompt,'.params.')'
|
|
if 0 >= which | return ''
|
|
elseif 1 == which
|
|
return substitute(matchstr(a:{1}, '^.\{-}\ze\%(\n\|$\)'), '&', '', 'g')
|
|
else
|
|
return substitute(
|
|
\ matchstr(a:{1}, '^\%(.\{-}\n\)\{'.(which-1).'}\zs.\{-}\ze\%(\n\|$\)')
|
|
\ , '&', '', 'g')
|
|
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 = params. ',a:{'.i.'}'
|
|
endif
|
|
let i = 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 = sl_choices . ' '. hl .
|
|
\ substitute(a:{i}, '&\(.\)', '%6*\1'.hl, '') . '%* '
|
|
else
|
|
let sl_choices = sl_choices . ' ' .
|
|
\ substitute(a:{i}, '&\(.\)', '%6*\1%*', '') . ' '
|
|
endif
|
|
let i = 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 = "/<esc>/<s-tab>/<tab>/<left>/<right>/<cr>/<F1>"
|
|
" 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 = i + 1
|
|
let item = matchstr(choices, "^.\\{-}\\ze\\(\n\\|$\\)")
|
|
let choices = matchstr(choices, "\n\\zs.*$")
|
|
" exe 'anoremenu ]'.a:text.'.'.item.' :let s:choice ='.i.'<cr>'
|
|
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 = list_choices . ',choice_{'.i.'}'
|
|
endif
|
|
" Update the hotkey.
|
|
let key = toupper(matchstr(choice_{i}, '&\zs.\ze'))
|
|
let hotkey_{key} = i
|
|
let hotkeys = hotkeys . tolower(key) . toupper(key)
|
|
let help_k = 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=<tab>
|
|
" exe ':emenu ]'.a:text.'.'."<tab>"
|
|
" 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(" \<tab>\<esc>\<enter>".hotkeys,complType) ||
|
|
\ (key =~ "\<F1>\\|\<right>\\|\<left>\\|\<s-tab>")
|
|
if key == "\<F1>" " Help {{{5
|
|
redraw!
|
|
echohl StatusLineNC
|
|
echo help
|
|
echohl None
|
|
let key=getchar()
|
|
let complType=nr2char(key)
|
|
endif
|
|
" TODO: support CTRL-D
|
|
if complType == "\<enter>" " Validate {{{5
|
|
break
|
|
elseif complType == " " " check box {{{5
|
|
let toggle = 1
|
|
elseif complType == "\<esc>" " Abort {{{5
|
|
let i = -1 | break
|
|
elseif complType == "\<tab>" || key == "\<right>" " Next {{{5
|
|
let direction = 1
|
|
elseif key =~ "\<left>\\|\<s-tab>" " 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 = 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 = r . ((choice_{i}[1] == 'X') ? '1' : '0')
|
|
let i = 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:
|