Changeset 6400 for lang/vim

Show
Ignore:
Timestamp:
02/09/08 02:12:16 (10 months ago)
Author:
kana
Message:

lang/vim/ku:
* Merge the following changes:

r639@colinux (orig r840): kana | 2008-02-03 01:47:33 +0900
vim/dot.vim/autoload/ku.vim:

  • Add some FIXMEs.

r640@colinux (orig r841): kana | 2008-02-03 01:58:02 +0900
vim/dot.vim/autoload/ku.vim:

  • type buffer, file: Add ';' and ':' actions.

r641@colinux (orig r842): kana | 2008-02-03 02:01:52 +0900
vim/dot.vim/autoload/ku.vim:

  • type file:
    • Revise some FIXMEs.

r642@colinux (orig r843): kana | 2008-02-03 02:27:48 +0900
vim/dot.vim/autoload/ku.vim:

  • g:ku_junk_item_pattern:
    • New.
  • s:complete():
    • Use g:ku_junk_item_pattern to sort.

r643@colinux (orig r844): kana | 2008-02-03 02:32:02 +0900
vim/dot.vim/autoload/ku.vim:

  • s:complete():
    • Fix the bug that s:match isn't used for skip matching.

r651@colinux (orig r845): kana | 2008-02-04 02:30:53 +0900
vim/dot.vim/autoload/ku.vim:

  • type file / s:make_glob_pattern():
    • Add comments.
    • Revise the style.
    • Fix the bug that the root directory isn't listed.
    • Fix the bug that wrong pattern is given to glob().

r652@colinux (orig r846): kana | 2008-02-04 02:54:11 +0900
vim/dot.vim/autoload/ku.vim:

  • s:complete():
    • Fix the bug that unmatched items are still listed.

r653@colinux (orig r847): kana | 2008-02-04 04:43:53 +0900
vim/dot.vim/autoload/ku.vim, vim/dot.vim/doc/ku.txt:

  • Modify the definition of type:
    • Separate 'actions' into 'keys' and 'actions'.
    • Update all related parts.
  • s:show_available_actions_message():
    • Sort message by the names of actions.

r665@colinux (orig r848): kana | 2008-02-05 02:59:37 +0900
vim/dot.vim/doc/ku.txt:

  • TYPES: Refine the table of types.

r666@colinux (orig r849): kana | 2008-02-05 03:07:39 +0900
vim/dot.vim/autoload/ku.vim:

  • type buffer, type file: Fix the use of undefined action.

r669@colinux (orig r852): kana | 2008-02-05 03:22:05 +0900
vim/dot.vim/autoload/ku.vim:

  • ku#register_type(): Add the check for all a:args.keys have valid action name.

r673@colinux (orig r853): kana | 2008-02-05 18:26:30 +0900
vim/dot.vim/autoload/ku.vim:

  • s:valid_type_name_p():
    • Revise hte implementation.

r674@colinux (orig r854): kana | 2008-02-05 18:47:22 +0900
vim/dot.vim/autoload/ku.vim:

  • ku#custom_key(): New.

vim/dot.vim/doc/ku.txt:

  • ku#unregister_type(): Fix the missing tag.
  • ku#custom_key(): Add document.

r675@colinux (orig r855): kana | 2008-02-05 18:52:01 +0900
vim/dot.vim/autoload/ku.vim:

  • Remove unnecessary FIXMEs.

r676@colinux (orig r856): kana | 2008-02-05 19:13:44 +0900
vim/dot.vim/autoload/ku.vim:

  • ku#custom_action(): New.

vim/dot.vim/doc/ku.txt

  • ku#custom_action(): Add document.

r677@colinux (orig r857): kana | 2008-02-05 19:58:55 +0900
vim/dot.vim/autoload/ku.vim:

  • Add 'cd' and 'lcd' command.
  • Remove all memos related to type directory. It is integrated into other
    types as some actions.

vim/dot.vim/doc/ku.txt:

  • TYPES:
    • Add remarks for type file.
    • Add remarks for some actions.

r679@colinux (orig r858): kana | 2008-02-05 22:36:21 +0900
vim/dot.vim/autoload/ku.vim:

  • type file: Modify the way to gather items -- especially, search items in
    single directory.

r681@colinux (orig r860): kana | 2008-02-06 04:10:54 +0900
vim/dot.vim/autoload/ku.vim:

  • Add auto-directory-insertion feature.

vim/dot.vim/doc/ku.txt:

  • Add document for auto-directory-insertion.

r693@colinux (orig r861): kana | 2008-02-09 01:18:43 +0900
vim/dot.vim/autoload/ku.vim:

  • s:text_for_auto_completion():
    • Fix the condition on appropriate items.
    • Modify not to select middle-matching items.
Location:
lang/vim/ku/trunk
Files:
2 modified

Legend:

Unmodified
Added
Removed
  • lang/vim/ku/trunk/autoload/ku.vim

    r5827 r6400  
    44" License: MIT license (see <http://www.opensource.org/licenses/mit-license>) 
    55" $Id$  "{{{1 
    6 " FIXME: Translate the following FIXMEs in English. 
    7 " 
    8 " FIXME: ドキュメント更新。 
    9 " 
    10 " FIXME: s:do(): どのitemにもマッチしない入力であってもactionに渡す。 
    11 "   例えば今の状況ではtype fileで新規ファイルを開けないので、 
    12 "   このような処理は必要である。 
    13 " 
    14 "   ただし、これをするならばどのtypeのactionを使うかが問題。 
    15 "   s:preferred_typeが定義されている場合に限定か。 
    16 " 
    17 " FIXME: より「賢い」ソーティング (優先順位: 低) 
    18 "   - パスっぽいものは最後のコンポーネントを重視するのはどうか? 
    19 "   - type buffer: フルパスと相対パスのどちらを使うべきか? 
    20 "   - パスっぽいものは相対パス優先の方がいい? 
    21 "     '/'の値は若い以上、今のままでは自動的に相対パスが軽視されることになる。 
    22 " 
    23 " FIXME: strtrans()の代替作成。<Left>等を正しく変換するように。 
    24 " 
    25 " FIXME: type file - stub. 
    26 " FIXME: type directory - NIY. 
    27 " 
    28 " FIXME: 各種比較でのcase sensitivityの統一・見直し。 
    29 " 
    30 " FIXME: 実装方法の選択 
    31 "   (A) ins-completion-menuを利用(Omni completion) 
    32 "       + Insert modeの基本的な操作体系を利用可能。 
    33 "       - 一部キーをmappingできないため、細かい情報を取れない。 
    34 "         * 特に問題な点は 
    35 "           「どの項目が選択されているか」と 
    36 "           「選択状況が変化したか」を 
    37 "           知ることができないこと。 
    38 "           * このために 
    39 "             「actionを<Tab>で選んで実行」というI/Fや、 
    40 "             「利用可能なactionを適宜表示」ということができない。 
    41 "       - CursorMovedIのフックを利用しての検出のため、カオス過ぎる。 
    42 "         特にs:PROMPTはひどい発明。 
    43 " 
    44 "   (B) getchar()で自力管理 
    45 "       + キー入力を全て把握できるため、ins-completion-menuの問題はない。 
    46 "       - キー入力を自力で管理する以上、 
    47 "         基本的な操作体系も含めて実装しなければならない。 
    48 "       * itemsの一覧表示はins-completion-menu以外の方法が取れる。 
    49 "         例えばtype毎に並べたitemsのうち、 
    50 "         preferred以外はfoldする等。 
    51 " 
    52 "   buffuzzy/zapitの経験から(A)を利用していたが、 
    53 "   itemsの表示はバッファを使ってどうとでもできるので、 
    54 "   (B)も一考の価値はある。 
    55 " 
    56 "   ただ、(A)でもある程度の問題は解決してしまったため、一先ずは(A)で行く。 
    57 "   (B)には(B)で魅力的なメリットがあるので、落ち着いたらそちらも考える。 
     6" FIXME: auto-complete 1 component for each typing '/' (like bluewind). 
     7" FIXME: s:do(): Force action on unmatched pattern. 
     8" FIXME: more smart sorting: 
     9"        - considering last component. 
     10"        - type buffer: full path vs. relative path. 
     11" FIXME: review on case sensitivity. 
     12" FIXME: alternative implementation (getchar()), if necessary. 
    5813" 
    5914" Variables and Constants  "{{{1 
     15" Script-local  "{{{2 
    6016 
    6117let s:FALSE = 0 
    6218let s:TRUE = !s:FALSE 
     19 
     20let s:TYPE_NUMBER = type(0) 
     21let s:TYPE_STRING = type('') 
     22let s:TYPE_FUNCTION = type(function('function')) 
     23let s:TYPE_LIST = type([]) 
     24let s:TYPE_DICTONARY = type({}) 
     25 
    6326 
    6427" Flag which indicates whether the ku window is opened with bang (:Ku!). 
     
    10467let s:winrestcmd = '' 
    10568 
     69" Flag to avoid infinite loop by auto-directory-insertion. 
     70let s:auto_directory_insertion_done_p = s:FALSE 
     71 
     72 
     73 
     74 
     75" Global  "{{{2 
    10676" Flag for debugging. 
    10777if !exists('g:ku_debug_p') 
    10878  let g:ku_debug_p = s:FALSE 
     79endif 
     80 
     81" Patterns for junk items.  These items are listed at the last. 
     82" FIXME: How about g:ku_important_item_pattern? 
     83if !exists('g:ku_junk_item_pattern') 
     84  let g:ku_junk_item_pattern = '\(\~\|\.o\|\.swp\)$' 
    10985endif 
    11086 
     
    143119  2 wincmd _ 
    144120 
     121  " Do some initialization for each type. 
     122  for type_name in keys(s:types) 
     123    call s:types[type_name].initialize() 
     124  endfor 
     125 
    145126  " Start Insert mode. 
    146127  % delete _ 
     
    168149  if !s:valid_type_definition_p(a:args) 
    169150    echohl ErrorMsg 
    170     echomsg 'Invalid type definition:' strtrans(a:args) 
     151    echomsg 'Invalid type definition:' string(a:args) 
    171152    echohl None 
    172153    return s:FALSE 
     
    185166    call remove(s:types, a:name) 
    186167  endif 
     168  return s:TRUE 
     169endfunction 
     170 
     171 
     172 
     173 
     174function! ku#custom_key(type_name, key, new_action_name)  "{{{2 
     175  if !s:valid_type_name_p(a:type_name) 
     176   \ || !has_key(s:types[a:type_name].actions, a:new_action_name) 
     177    return '' 
     178  endif 
     179 
     180  let old_action_name = get(s:types[a:type_name].keys, a:key, '') 
     181  let s:types[(a:type_name)].keys[(a:key)] = a:new_action_name 
     182  return old_action_name 
     183endfunction 
     184 
     185 
     186 
     187 
     188function! ku#custom_action(type_name, action_name, new_action_function)  "{{{2 
     189  if !s:valid_type_name_p(a:type_name) || !s:callable_p(a:new_action_function) 
     190    return s:FALSE 
     191  endif 
     192 
     193  let s:types[a:type_name].actions[a:action_name] = a:new_action_function 
    187194  return s:TRUE 
    188195endfunction 
     
    245252        let new_item['_ku_sort_priority'] 
    246253          \ = [ 
     254          \     (match(new_item.word, g:ku_junk_item_pattern) < 0 ? 0 : 1), 
    247255          \     (g:ku_sort_by_type_first_p ? type_name : 0), 
    248256          \     s:match(new_item.word, s:make_asis_regexp(pattern)), 
     
    255263    endfor 
    256264 
    257     call filter(s:last_items, '0 <= v:val._ku_sort_priority[2]') 
     265    call filter(s:last_items, '0 <= v:val._ku_sort_priority[3]') 
    258266    call sort(s:last_items, function('s:compare_items')) 
    259267    return s:last_items 
     
    289297 
    290298  " Do the specified aciton. 
    291   if type(item) != type('') 
    292     let ActionFunction = (a:choose_p 
    293       \                   ? s:choose_action(item._ku_type.actions) 
    294       \                   : item._ku_type.actions[0].function) 
    295     call ActionFunction(item) 
     299  if type(item) == s:TYPE_DICTONARY 
     300    let ActionFunction 
     301      \ = (a:choose_p 
     302      \    ? s:choose_action_for_item(item) 
     303      \    : item._ku_type.actions[item._ku_type.keys['*default*']]) 
     304    call s:apply(ActionFunction, [item]) 
    296305  endif 
    297306endfunction 
     
    315324function! s:on_InsertEnter()  "{{{2 
    316325  let s:last_col = s:INVALID_COL 
     326  let s:auto_directory_insertion_done_p = s:FALSE 
    317327  return s:on_CursorMovedI() 
    318328endfunction 
     
    331341 
    332342  " The order of these conditions are important. 
    333   if !s:contains_the_prompt_p(getline('.')) 
     343  let line = getline('.') 
     344  if !s:contains_the_prompt_p(line) 
    334345    let keys = repeat("\<Right>", len(s:PROMPT)) 
    335346    call s:complete_the_prompt() 
     
    337348    " The cursor is inside the prompt. 
    338349    let keys = repeat("\<Right>", len(s:PROMPT) - col('.') + 1) 
    339   elseif len(getline('.')) < col('.') && col('.') != s:last_col 
     350  elseif len(line) < col('.') && col('.') != s:last_col 
    340351    " New character is inserted. 
    341     let keys = s:KEYS_TO_START_COMPLETION 
     352      " FIXME: path separator assumption 
     353    if (!s:auto_directory_insertion_done_p) 
     354     \ && line[-1:] == '/' 
     355     \ && len(s:PROMPT) + 2 <= len(line) 
     356      let text = s:text_for_auto_completion(line,s:complete(s:FALSE,line[:-2])) 
     357      if text != '' 
     358        call setline('.', text) 
     359        let keys = "\<End>/" 
     360        let s:auto_directory_insertion_done_p = s:TRUE 
     361      else 
     362        let keys = s:KEYS_TO_START_COMPLETION 
     363        let s:auto_directory_insertion_done_p = s:FALSE 
     364      endif 
     365    else 
     366      let keys = s:KEYS_TO_START_COMPLETION 
     367        let s:auto_directory_insertion_done_p = s:FALSE 
     368    endif 
    342369  else 
    343370    let keys = '' 
     
    365392function! s:nop(...)  "{{{2 
    366393  return 
     394endfunction 
     395 
     396 
     397 
     398 
     399function! s:string(s)  "{{{2 
     400  " like strtrans(), but convert into more human-readable notation on special 
     401  " keys such as <Left>. 
     402  return strtrans(a:s)  " FIXME: NIY. 
    367403endfunction 
    368404 
     
    477513function! s:make_asis_regexp(s)  "{{{2 
    478514  " FIXME: case sensitivity 
    479   return '\c\V' . substitute(a:s, '\', '\\', 'g') 
     515  return '\c\V' . substitute(substitute(a:s, '\s\+', ' ', 'g'), '\', '\\', 'g') 
    480516endfunction 
    481517 
     
    485521function! s:make_skip_regexp(s)  "{{{2 
    486522  " FIXME: case sensitivity 
    487   return substitute(s:make_asis_regexp(a:s), '\s', '\\.\\*', 'g') 
     523  " FIXME: path separator assumption 
     524  let p_asis = s:make_asis_regexp(substitute(a:s, '/', ' / ', 'g')) 
     525  return substitute(p_asis, '\s', '\\.\\*', 'g') 
    488526endfunction 
    489527 
     
    500538function! s:contains_the_prompt_p(s)  "{{{2 
    501539  return len(s:PROMPT) <= len(a:s) && a:s[:len(s:PROMPT) - 1] ==# s:PROMPT 
     540endfunction 
     541 
     542 
     543 
     544 
     545function! s:text_for_auto_completion(line, items)  "{{{2 
     546  " Note that a:line always ends with '/', because this function is always 
     547  " called by typing '/'. 
     548  " FIXME: path separator assumption. 
     549  let line_components = split(a:line[len(s:PROMPT):], '/', s:TRUE) 
     550  for item in a:items 
     551    let item_components = split(item.word, '/', s:TRUE) 
     552 
     553    if len(line_components) <= len(item_components) 
     554      " Discard items which don't match the line from the head of them. 
     555      " Note that line_components[-1] is always '' and line_components[-2] is 
     556      " almost imperfect, so that they aren't used. 
     557      let i = 0 
     558      for i in range(len(line_components) - 2) 
     559        if line_components[i] != item_components[i] 
     560          break 
     561        endif 
     562      endfor 
     563      if line_components[i] != item_components[i] 
     564        continue 
     565      endif 
     566      " OK 
     567    elseif isdirectory(item.word)  " for type file. 
     568      " OK 
     569    else 
     570      continue 
     571    endif 
     572 
     573    return join(item_components[:len(line_components) - 2], '/') 
     574  endfor 
     575  return '' 
    502576endfunction 
    503577 
     
    543617 
    544618 
    545 function! s:choose_action(actions)  "{{{2 
    546   " FIXME: style of the menu message. 
    547   echo 'Available actions are:' 
    548   for action in a:actions 
    549     echo printf('%s :: %s', strtrans(action.key), action.name) 
     619function! s:choose_action_for_item(item)  "{{{2 
     620  call s:show_available_actions_message(a:item) 
     621 
     622  let c = nr2char(getchar()) 
     623  redraw  " clear the menu message lines to avoid hit-enter prompt. 
     624 
     625  if has_key(a:item._ku_type.keys, c) 
     626    let name = a:item._ku_type.keys[c] 
     627    if has_key(a:item._ku_type.actions, name) 
     628      return a:item._ku_type.actions[name] 
     629    endif 
     630  endif 
     631 
     632  echo 'The key' s:string(c) 'is not associated with any action' 
     633     \ '-- nothing happened.' 
     634  return function('s:nop') 
     635endfunction 
     636 
     637 
     638 
     639 
     640function! s:show_available_actions_message(item)  "{{{2 
     641  let items = items(a:item._ku_type.keys) 
     642  call map(items, '[v:val[1], v:val[0]]') 
     643  call sort(items) 
     644  call filter(items, 'v:val[1] !=# "*default*"') 
     645  let keys = map(copy(items), 'v:val[1]') 
     646  let names = map(copy(items), 'v:val[0]') 
     647  let max_key_length = max(map(copy(keys), 'len(s:string(v:val))')) 
     648  let max_name_length = max(map(copy(names), 'len(v:val)')) 
     649  let padding = 3 
     650  let max_cell_length = max_key_length + 3 + max_name_length + padding 
     651  let format = '%*s%*s - %-*s' 
     652 
     653  let max_column = max([1, (&columns + padding - 1) / max_cell_length]) 
     654  let max_column = min([max_column, 4]) 
     655  let n = len(items) 
     656  let max_row = n / max_column + (n % max_column != 0) 
     657 
     658  echo printf('Available actions for %s (type %s) are:', 
     659     \        a:item.word, a:item._ku_type.name) 
     660  for r in range(max_row) 
     661    let i = r 
     662    echo '' 
     663    while i < n 
     664      echon printf(format, 
     665          \        (i == r ? 0 : padding), '', 
     666          \        max_key_length, s:string(items[i][1]), 
     667          \        max_name_length, items[i][0]) 
     668      let i += max_row 
     669    endwhile 
    550670  endfor 
    551671  echo '' 
    552  
    553   let c = nr2char(getchar()) 
    554   redraw  " clear the menu message lines to avoid hit-enter prompt. 
    555  
    556   for action in a:actions 
    557     if c ==# action.key 
    558       return action.function 
     672endfunction 
     673 
     674 
     675 
     676 
     677function! s:valid_type_name_p(name)  "{{{2 
     678  return has_key(s:types, a:name) 
     679endfunction 
     680 
     681 
     682 
     683 
     684function! s:valid_type_definition_p(args)  "{{{2 
     685  if type(a:args) != s:TYPE_DICTONARY 
     686    return s:FALSE 
     687  endif 
     688 
     689  " -- 'name' 
     690  if !s:has_valid_entry(a:args, 'name', s:TYPE_STRING) 
     691    return s:FALSE 
     692  endif 
     693  if !(a:args.name =~# '^[a-z]\+$') | return s:FALSE | endif 
     694 
     695  " -- 'gather' 
     696  if !s:has_valid_entry(a:args, 'gather', s:TYPE_FUNCTION) 
     697    return s:FALSE 
     698  endif 
     699 
     700  " -- 'initialize' 
     701  if has_key(a:args, 'initialize') 
     702    if !(type(a:args.initialize) == s:TYPE_FUNCTION) | return s:FALSE | endif 
     703  else 
     704    let a:args.initialize = function('s:nop') 
     705  endif 
     706 
     707  " -- 'keys' 
     708  if !s:has_valid_entry(a:args, 'keys', s:TYPE_DICTONARY) 
     709    return s:FALSE 
     710  endif 
     711  for k in keys(a:args.keys) 
     712    if !(type(k) == s:TYPE_STRING && type(a:args.keys[k]) == s:TYPE_STRING) 
     713      return s:FALSE 
    559714    endif 
    560715  endfor 
    561  
    562   echo 'The key' strtrans(c) 'is not associated with any action' 
    563      \ '-- nothing happened.' 
    564   return function('s:nop') 
    565 endfunction 
    566  
    567  
    568  
    569  
    570 function! s:valid_type_name_p(name)  "{{{2 
    571   for type_name in keys(s:types) 
    572     if a:name ==# type_name 
    573       return s:TRUE 
     716  if !has_key(a:args.keys, '*default*') 
     717    return s:FALSE 
     718  endif 
     719 
     720  " -- 'actions' 
     721  if !s:has_valid_entry(a:args, 'actions', s:TYPE_DICTONARY) 
     722    return s:FALSE 
     723  endif 
     724  for k in keys(a:args.actions) 
     725    if !(type(k) == s:TYPE_STRING && s:callable_p(a:args.actions[k])) 
     726      return s:FALSE 
    574727    endif 
    575728  endfor 
    576   return s:FALSE 
    577 endfunction 
    578  
    579  
    580  
    581  
    582 function! s:valid_type_definition_p(args)  "{{{2 
    583   let T_NUM = type(0) 
    584   let T_STR = type('') 
    585   let T_FUNC = type(function('function')) 
    586   let T_LIST = type([]) 
    587   let T_DICT = type({}) 
    588  
    589   if type(a:args) != T_DICT 
    590     return s:FALSE 
    591   endif 
    592  
    593   " -- The name of this type. 
    594   if !s:has_valid_entry(a:args, 'name', T_STR) | return s:FALSE | endif 
    595   if !(a:args.name =~# '^[a-z]\+$') | return s:FALSE | endif 
    596  
    597   " -- Available actions for this type of items. 
    598   " This is a list of definitions of actions. 
    599   " It must contain at least 1 definition. 
    600   " The 1st definition is the default action. 
    601   "  
    602   " Each definition is a dictionary with 3 entries: 
    603   " 'key'       The key to choose this action in <Plug>(ku-choose-action). 
    604   " 'name'      The name of the action. 
    605   " 'function'  The function of the action.  It is called with one parameter, 
    606   "             the selected item (as described in :help complete-items). 
    607   if !s:has_valid_entry(a:args, 'actions', T_LIST) | return s:FALSE | endif 
    608   if !(1 <= len(a:args.actions)) | return s:FALSE | endif 
    609   for v in a:args.actions 
    610     if !s:has_valid_entry(v, 'key', T_STR) | return s:FALSE | endif 
    611     if !s:has_valid_entry(v, 'name', T_STR) | return s:FALSE | endif 
    612     if !s:has_valid_entry(v, 'function', T_FUNC) | return s:FALSE | endif 
     729  for n in values(a:args.keys) 
     730    if !has_key(a:args.actions, n) 
     731      return s:FALSE 
     732    endif 
    613733  endfor 
    614734 
    615   " -- Function to gather items which match to the given pattern. 
    616   " It takes 1 argument (user input pattern). 
    617   " It returns a list of items.  Each item is a string. 
    618   if !s:has_valid_entry(a:args, 'gather', T_FUNC) | return s:FALSE | endif 
    619  
    620   " FIXME: other entries. 
     735 
     736  " -- other entries -- 
     737 
    621738  return s:TRUE 
    622739endfunction 
     
    632749 
    633750 
     751function! s:callable_p(obj)  "{{{2 
     752  return (type(a:obj) == s:TYPE_FUNCTION) 
     753       \ || ((type(a:obj) == s:TYPE_DICTONARY) && has_key(a:obj, '__call__')) 
     754endfunction 
     755 
     756 
     757 
     758 
     759function! s:apply(obj, args)  "{{{2 
     760  if type(a:obj) == s:TYPE_FUNCTION 
     761    return call(a:obj, a:args) 
     762  elseif type(a:obj) == s:TYPE_DICTONARY && has_key(a:obj, '__call__') 
     763    return call(a:obj.__call__, a:args, a:obj) 
     764  else 
     765    throw 'This object is not callable:' string(a:obj) 
     766  endif 
     767endfunction 
     768 
     769 
     770 
     771 
     772function! s:pa(f, ...)  "{{{2 
     773  " pa = Partial Apply 
     774  " Returns a callable object g, 
     775  " where g(b, ...) is equivalent to f(a, ..., b, ...). 
     776  let g = {} 
     777  let g.f = a:f 
     778  let g.args = copy(a:000)  " a:000 will be lost after this execution. 
     779  function! g.__call__(...) 
     780    return call(self.f, self.args + a:000) 
     781  endfunction 
     782  return g 
     783endfunction 
     784 
     785 
     786 
     787 
    634788 
    635789 
     
    637791 
    638792" Built-in Types  "{{{1 
     793" common definitions  "{{{2 
     794function! s:_type_any_action_ex(item) 
     795  call feedkeys(':', 'n') 
     796  call feedkeys(ku#bang(), 'n') 
     797  call feedkeys(' ', 'n') 
     798  call feedkeys(a:item.word, 'n') 
     799  call feedkeys("\<C-b>", 'n') 
     800endfunction 
     801 
     802 
     803function! s:_type_any_action_xcd(cd_command, item) 
     804  " FIXME: escape special characters. 
     805  if isdirectory(a:item.word) 
     806    execute a:cd_command a:item.word 
     807  elseif filereadable(a:item.word) 
     808    execute a:cd_command fnamemodify(a:item.word, ':h') 
     809  else 
     810    echo printf('Item %s (type: %s) is not a file or directory.', 
     811       \        a:item.word, a:item._ku_type.name) 
     812  endif 
     813endfunction 
     814 
     815 
     816 
     817 
    639818" buffer  "{{{2 
     819" FIXME: how about unlisted buffers? 
    640820let s:_type_buffer_cached_items = [] 
    641821let s:_type_buffer_last_bufnr = s:INVALID_BUFNR 
    642822function! s:_type_buffer_gather(pattern) 
    643   if s:_type_buffer_last_bufnr != bufnr('$') 
     823  if s:_type_buffer_last_bufnr == bufnr('$') 
     824    call filter(s:_type_buffer_cached_items, 'bufexists(v:val._buffer_number)') 
     825  else 
    644826    let s:_type_buffer_cached_items = [] 
    645827    let format = 'buffer %' . len(bufnr('$')) . 'd' 
     
    664846  execute a:item._buffer_number 'buffer'.ku#bang() 
    665847endfunction 
    666 function! s:_type_buffer_action_split_open(item) 
    667   execute a:item._buffer_number 'sbuffer' 
    668 endfunction 
    669 function! s:_type_buffer_action_vsplit_open(item) 
    670   execute 'vertical' a:item._buffer_number 'sbuffer' 
    671 endfunction 
    672  
    673  
    674 " FIXME: other variants: hjkl HJKL. 
     848function! s:_type_buffer_action_xsplit(modifier, item) 
     849  execute a:modifier a:item._buffer_number 'sbuffer' 
     850endfunction 
     851function! s:_type_buffer_action_xdelete(command, item) 
     852  execute a:item._buffer_number a:command.ku#bang() 
     853endfunction 
     854 
     855 
    675856call ku#register_type({ 
    676857   \   'name': 'buffer', 
    677    \   'actions': [ 
    678    \     {'key': 'o', 
    679    \      'name': 'open', 
    680    \      'function': function('s:_type_buffer_action_open')}, 
    681    \     {'key': 's', 
    682    \      'name': 'split open', 
    683    \      'function': function('s:_type_buffer_action_split_open')}, 
    684    \     {'key': 'v', 
    685    \      'name': 'vsplit open', 
    686    \      'function': function('s:_type_buffer_action_vsplit_open')}, 
    687    \   ], 
    688858   \   'gather': function('s:_type_buffer_gather'), 
     859   \   'keys': { 
     860   \     "*default*": 'open', 
     861   \     "o": 'open', 
     862   \     "\<C-o>": 'open', 
     863   \     "n": 'split', 
     864   \     "\<C-n>": 'split', 
     865   \     "s": 'split', 
     866   \     "\<C-s>": 'split', 
     867   \     "v": 'vsplit', 
     868   \     "\<C-v>": 'vsplit', 
     869   \     "h": 'split-left', 
     870   \     "\<C-h>": 'split-left', 
     871   \     "H": 'split-far-left', 
     872   \     "j": 'split-down', 
     873   \     "\<C-j>": 'split-down', 
     874   \     "J": 'split-bottom', 
     875   \     "k": 'split-up', 
     876   \     "\<C-k>": 'split-up', 
     877   \     "K": 'split-top', 
     878   \     "l": 'split-right', 
     879   \     "\<C-l>": 'split-right', 
     880   \     "L": 'split-far-right', 
     881   \     ";": 'ex', 
     882   \     ":": 'ex', 
     883   \     "/": 'cd', 
     884   \     "?": 'cd-local', 
     885   \     "U": 'unload', 
     886   \     "D": 'delete', 
     887   \     "W": 'wipeout', 
     888   \   }, 
     889   \   'actions': { 
     890   \     'open': 
     891   \       function('s:_type_buffer_action_open'), 
     892   \     'split': 
     893   \       s:pa('s:_type_buffer_action_xsplit', ''), 
     894   \     'vsplit': 
     895   \       s:pa('s:_type_buffer_action_xsplit', 'vertical'), 
     896   \     'split-left': 
     897   \       s:pa('s:_type_buffer_action_xsplit', 'vertical leftabove'), 
     898   \     'split-far-left': 
     899   \       s:pa('s:_type_buffer_action_xsplit', 'vertical topleft'), 
     900   \     'split-down': 
     901   \       s:pa('s:_type_buffer_action_xsplit', 'rightbelow'), 
     902   \     'split-bottom': 
     903   \       s:pa('s:_type_buffer_action_xsplit', 'botright'), 
     904   \     'split-up': 
     905   \       s:pa('s:_type_buffer_action_xsplit', 'leftabove'), 
     906   \     'split-top': 
     907   \       s:pa('s:_type_buffer_action_xsplit', 'topleft'), 
     908   \     'split-right': 
     909   \       s:pa('s:_type_buffer_action_xsplit', 'vertical rightbelow'), 
     910   \     'split-far-right': 
     911   \       s:pa('s:_type_buffer_action_xsplit', 'vertical botright'), 
     912   \     'ex': 
     913   \       function('s:_type_any_action_ex'), 
     914   \     'cd': 
     915   \       s:pa('s:_type_any_action_xcd', 'cd'), 
     916   \     'cd-local': 
     917   \       s:pa('s:_type_any_action_xcd', 'lcd'), 
     918   \     'unload': 
     919   \       s:pa('s:_type_buffer_action_xdelete', 'bunload'), 
     920   \     'delete': 
     921   \       s:pa('s:_type_buffer_action_xdelete', 'bdelete'), 
     922   \     'wipeout': 
     923   \       s:pa('s:_type_buffer_action_xdelete', 'bwipeout'), 
     924   \   }, 
    689925   \ }) 
    690926 
     
    693929