Commits

ZyX_I  committed 8e4b302

Mappings: initial implementation

  • Participants
  • Parent commits 4da57bf

Comments (0)

Files changed (2)

File plugin/gtregen.vim

 if !exists('s:_pluginloaded')
     execute frawor#Setup('0.0', {'@/fwc': '0.0',
                 \          '@/functions': '0.0',
-                \              '@/table': '0.0',}, 0)
+                \           '@/mappings': '0.0',
+                \              '@/table': '0.0',
+                \     '@gtregen/bufvars': '0.0',}, 0)
     finish
 elseif s:_pluginloaded
     finish
 endif
 let s:_messages={
             \'treeex': 'Tree %s already exsists',
+            \'invrun': 'Failed to run mapping',
         \}
 let s:tree={}
 let s:trees={}
+"▶1 Default mappings
+let s:defmappings={}
+let s:defmappings._select={'lhs': 's', 'selection': 'ignore', 'passnodes': 0}
+function s:defmappings._select.function(bvar, lnr)
+    let a:bvar.selected[a:lnr]=1
+    " TODO syntax
+    return ''
+endfunction
+let s:defmappings._unselect={'lhs': 'S', 'selection': 'ignore', 'passnodes': 0}
+function s:defmappings._unselect.function(bvar, lnr)
+    if has_key(a:bvar.selected, a:lnr)
+        unlet a:bvar.selected[a:lnr]
+        " TODO syntax
+    endif
+    return ''
+endfunction
+"▶1 runmap :: tree, mdescr
+function s:F.runmap(tree, mdescr, mode)
+    let bvar=s:_r.bufvars[bufnr('%')]
+    if a:mode is# 'x'
+        let lstart=line("'<")
+        let lend=line("'>")
+        if lstart>lend
+            let [lstart, lend]=[lend, lstart]
+        endif
+        if lstart<bvar.cstart
+            let lstart=bvar.cstart
+        elseif lstart>bvar.cend
+            " TODO Footer action
+            return ''
+        endif
+        if lend<bvar.cstart
+            " TODO Header action
+            return ''
+        elseif lend>bvar.cend
+            let lend=bvar.cend
+        endif
+        let selection=range(lstart-bvar.cstart, lend-bvar.cstart)
+    elseif a:mode is# 'n'
+        if a:mdescr.selection isnot# 'ignore' && !empty(bvar.selected)
+            let selection=map(keys(bvar.selected), '+v:val')
+            " TODO empty selection after mapping
+        else
+            let line=line('.')
+            if line<bvar.cstart
+                " TODO Header action
+                return ''
+            elseif line>bvar.cend
+                " TODO Footer action
+                return ''
+            endif
+            let selection=[line-bvar.cstart]
+        endif
+    endif
+    if a:mdescr.treerun isnot# 'all'
+        " TODO leaf/tree selecting
+    endif
+    if a:mdescr.passnodes
+        call map(selection, 'bvar.nodes[v:val]')
+    endif
+    if a:mdescr.multrun is# 'map' || (a:mdescr.multrun is# 'no' &&
+                \                     len(selection)==1)
+        return join(map(selection, 'a:mdescr.function(bvar, v:val)'), '')
+    elseif a:mdescr.multrun is# 'list'
+        return a:mdescr.function(bvar, selection)
+    else
+        call s:_f.throw('invrun')
+    endif
+    return ''
+endfunction
 "▶1 tree.add :: {f}, tid, Func, options → + s:trees, :au
 let s:tree.cons={'add': {}, 'del': {}}
 function s:tree.cons.add.function(plugdict, fdict, tid, Func, options)
     endif
     let tree.defsortby=get(a:options, 'defsortby', 'name')
     let tree.options=get(a:options, 'options', 0)
+    "▶2 Generate mappings
+    let tree.mgname='GTG'.a:tid
+    let tree.mappings={}
+    let mgdescr={'func': s:F.runmap, 'mode': 'nx', 'buffer': 1, 'dontmap': 1}
+    let mappings={}
+    for [mapname, mdescr] in items(extend(copy(s:defmappings),
+                \                         get(a:options, 'mappings', {})))
+        let tree.mappings[mapname]={'function': mdescr.function,
+                    \                'multrun': get(mdescr, 'multrun',   'map'),
+                    \                'treerun': get(mdescr, 'treerun',   'no' ),
+                    \              'passnodes': get(mdescr, 'passnodes',   1  ),
+                    \              'selection': get(mdescr, 'selection', 'use')}
+        " TODO operator mappings
+        " TODO non-expr mappings
+        let mappings[mapname]={'lhs': get(mdescr, 'lhs', 0),
+                    \          'rhs': [tree, tree.mappings[mapname], '%mode'],}
+        if get(mdescr, 'multrun', 0) is# 'no'
+            let mappings[mapname].mode='n'
+        endif
+    endfor
+    call s:_f.mapgroup.add(tree.mgname, mappings, mgdescr)
+    "▲2
     augroup Gtregen
         execute 'autocmd BufReadCmd '.tree.pattern.' '.
                     \'call s:F.setup('.string(a:tid).')'
             \       ' sortby    either is =(0), type ""'.
             \       '}'
 let s:tree.cons.add['@FWC']=
-            \['_ _ (match @\v^[^:/\\\x0a]+$@ #treeex(.) not key trees) '.
+            \['_ _ (match @\v^[a-zA-Z0-9]+$@ #treeex(.) not key trees) '.
             \     'isfunc '.
             \     'dict {columns '.
             \               '(dict {?match /\v^\w+$/  (|isfunc)}) '.
             \           'pattern     type "" '.
             \           'options     either ((|isfunc), '.
             \                                s:optcheck.') '.
+            \           'mappings    dict {'.
+            \                         '?match /\v^\a\w*$/ '.
+            \                               '(haskey function '.
+            \                                'dict {'.
+            \                                   'function (|isfunc)'.
+            \                                   'multrun  in [map list no]'.
+            \                                   'treerun  in [leafs trees all '.
+            \                                                'no]'.
+            \                                   'lhs      type ""'.
+            \                                '})'.
+            \                       '} '.
             \          '}',
             \ 'filter']
 let s:tree.cons.add=s:_f.wrapfunc(s:tree.cons.add)
     " TODO workaround for older vims not supporting sort(..., dict)
     return sort(a:nodes, s:F.sorter, d)
 endfunction
-"▶1 gencolumns :: tree, tdata, options, level → [Number:[String]]
+"▶1 gencolumns :: tree, tdata, options, level → [node:Number:[String]]
 function s:F.gencolumns(tree, tdata, options, level)
     let columns=copy(a:options.columns)
-    let r=[[a:level]+map(copy(columns),
-                \        '((has_key(a:tdata.columns, v:val))?'.
-                \               '(a:tdata.columns[v:val]):'.
-                \               '(call(a:tree.columns[v:val], [a:tdata],{})))')]
+    let r=[[a:tdata, a:level]+
+                \map(copy(columns),
+                \    '((has_key(a:tdata.columns, v:val))?'.
+                \           '(a:tdata.columns[v:val]):'.
+                \           '(call(a:tree.columns[v:val], [a:tdata],{})))')]
     if has_key(a:tdata, 'nodes')
         if type(a:tdata.nodes)==2
             let a:tdata.nodes_generator=a:tdata.nodes
 function s:F.align(text, alignment, textwidth, width)
     let add=a:width-a:textwidth
     let r=a:text
-    " TODO tabs
     if add
         if a:alignment is# 'left'
             let r.=repeat(' ', add)
 "▶1 firstchars :: columns → [String]
 " {down}|({up}<<1)|({right}<<2)|({left}<<3)
 function s:F.firstchars(columns, chars)
+    " TODO add additional character before every node
     let collevels=map(copy(a:columns), 'v:val[0]')
     let r=[]
     let i=0
                 \         'contained'
     execute 'syntax cluster '.a:clustername.' add='.synname
 endfunction
-"▶1 setup :: tid → + buffer
-let s:uchars  = split(' ╷╵│╶┌└├╴┐┘┤─┬┴┼', '\v.@=')
-let s:nuchars = split(' |||-+`+-+++-+++', '\v.@=')
-function s:F.setup(tid)
-    let tree=s:trees[a:tid]
-    let path=expand('<amatch>')[(tree.startlen):]
-    " tdata :: {path : [String],
-    "          nodes : {name : tdata},
-    "        columns : {colname : col}
-    "          [nodes_sorted : Either [tdata] Fref,]
-    "          [nodes_generator : Fref,]}
-    let tdata=tree.get(path)
-    if !has_key(tdata, 'path')
-        let tdata.path=[path]
-    endif
-    call s:F.proctdata(tree, tdata)
-    "▶2 Get options
-    if type(tree.options)==2
-        let options=deepcopy(tree.options(path))
-    elseif type(tree.options)==type({})
-        let options=deepcopy(tree.options)
-    else
-        let options={}
-    endif
-    if has_key(options, 'treechars')
-        let options.treechars=split(options.treechars, '\v.@=')
-    else
-        let options.treechars=((&encoding is# 'utf-8')?(s:uchars):
-                    \                                  (s:nuchars))
-    endif
-    if !has_key(options, 'sortby')
-        let options.sortby=tree.defsortby
-    endif
-    if !has_key(options, 'columns')
-        let options.columns=tree.coldefault
-    endif
-    if !has_key(options, 'colopts')
-        let options.colopts={}
-    endif
-    for col in filter(copy(options.columns), '!has_key(options.colopts, v:val)')
-        let options.colopts[col]={}
-    endfor
-    "▲2
+"▶1 setbuf :: bvar → + bvar, buffer
+function s:F.setbuf(bvar)
+    let options=a:bvar.options
+    let tree=a:bvar.tree
+    let tdata=a:bvar.tdata
     let columns=s:F.gencolumns(tree, tdata, options, 1)
+    let a:bvar.nodes=map(copy(columns), 'remove(v:val, 0)')
     "▶2 Get column titles
     let hastitles=0
     let titles=map(copy(options.columns),
                     \                'hwidths[v:key], colwidths[v:key])'))
     endif
     "▶2 Get other lines
+    " TODO Non-tree style
     let firstchars=s:F.firstchars(columns, options.treechars)
     let lines=map(copy(columns),
                 \ 'firstchars[v:key].'.
                 \                               '\"align\", \"left\"), '.
                 \                               'widths[v:key+1][".v:key."], '.
                 \                               'colwidths[v:key+1])"), "")')
-    "▶2 Populate buffer variable
-    let bvar={}
-    let bvar.hastitles=hastitles
-    let bvar.cstart=1
-    let bvar.cend=len(lines)
-    let bvar.colwidths=colwidths
-    let bvar.colstarts=colstarts
-    let bvar.options=options
+    "▶2 Populate buffer variable with regions borders
+    let a:bvar.hastitles=hastitles
+    let a:bvar.cstart=1
+    let a:bvar.cend=len(lines)
+    let a:bvar.colwidths=colwidths
+    let a:bvar.colstarts=colstarts
     "▶2 Header, footer and titles
     if hastitles
         call insert(lines, hline)
-        let bvar.cstart+=1
-        let bvar.cend+=1
+        let a:bvar.cstart+=1
+        let a:bvar.cend+=1
     endif
     if has_key(options, 'header')
         let header=split(options.header, "\n", 1)
         call extend(lines, header, 0)
-        let bvar.hlen=len(header)
-        let bvar.cstart+=bvar.hlen
-        let bvar.cend+=bvar.hlen
+        let a:bvar.hlen=len(header)
+        let a:bvar.cstart+=a:bvar.hlen
+        let a:bvar.cend+=a:bvar.hlen
     endif
     if has_key(options, 'footer')
         let footer=split(options.footer, "\n", 1)
         call extend(lines, footer)
-        let bvar.flen=len(footer)
+        let a:bvar.flen=len(footer)
     endif
     "▶2 Create syntax
-    if has_key(bvar, 'hlen')
+    if has_key(a:bvar, 'hlen')
         execute 'syntax region TreeHeader start=/\v%1l^/ '.
-                    \                    'end=/\v%'.bvar.hlen.'l$/'
+                    \                    'end=/\v%'.a:bvar.hlen.'l$/'
     endif
-    if has_key(bvar, 'flen')
-        execute 'syntax region TreeFooter start=/\v%'.(bvar.cend+1).'l^/ '.
-                    \                    'end=/\v%'.(bvar.cend+bvar.flen).'l$/'
+    if has_key(a:bvar, 'flen')
+        let fend=(a:bvar.cend+a:bvar.flen)
+        execute 'syntax region TreeFooter start=/\v%'.(a:bvar.cend+1).'l^/ '.
+                    \                    'end=/\v%'.fend.'l$/'
     endif
-    if bvar.hastitles
-        execute 'syntax match TreeTitles /\v%'.(bvar.cstart-1).'l^.*/ '.
+    if a:bvar.hastitles
+        execute 'syntax match TreeTitles /\v%'.(a:bvar.cstart-1).'l^.*/ '.
                     \                   'contains=@TreeTitle'
     endif
-    execute 'syntax match TreeData /\v%>'.(bvar.cstart-1).'l'.
-                \                    '%<'.(bvar.cend+1).'l^.*/ '.
+    execute 'syntax match TreeData /\v%>'.(a:bvar.cstart-1).'l'.
+                \                    '%<'.(a:bvar.cend+1).'l^.*/ '.
                 \                 'contains=@TreeColumn'
     let i=0
-    for column in bvar.options.columns
-        let islast=(i==len(bvar.options.columns)-1)
-        if bvar.hastitles
-            call s:F.addsyncol('TreeTitle', column, bvar.colstarts[i],
-                        \      bvar.colwidths[i], islast)
+    for column in a:bvar.options.columns
+        let islast=(i==len(a:bvar.options.columns)-1)
+        if a:bvar.hastitles
+            call s:F.addsyncol('TreeTitle', column, a:bvar.colstarts[i],
+                        \      a:bvar.colwidths[i], islast)
         endif
-        call s:F.addsyncol('TreeColumn', column, bvar.colstarts[i],
-                    \      bvar.colwidths[i], islast)
+        call s:F.addsyncol('TreeColumn', column, a:bvar.colstarts[i],
+                    \      a:bvar.colwidths[i], islast)
         let i+=1
     endfor
     "▲2
     setlocal modifiable noreadonly
     call setline('.', lines)
     setlocal nomodified nomodifiable readonly
+    let a:bvar.selected={}
+    return a:bvar
+endfunction
+"▶1 setup :: tid → + buffer
+let s:uchars  = split(' ╷╵│╶┌└├╴┐┘┤─┬┴┼', '\v.@=')
+let s:nuchars = split(' |||-+`+-+++-+++', '\v.@=')
+function s:F.setup(tid)
+    let tree=s:trees[a:tid]
+    let path=expand('<amatch>')[(tree.startlen):]
+    let buf=+expand('<abuf>')
+    " tdata :: {path : [String],
+    "          nodes : {name : tdata},
+    "        columns : {colname : col}
+    "          [nodes_sorted : Either [tdata] Fref,]
+    "          [nodes_generator : Fref,]}
+    let tdata=tree.get(path)
+    if !has_key(tdata, 'path')
+        let tdata.path=[path]
+    endif
+    call s:F.proctdata(tree, tdata)
+    "▶2 Get options
+    if type(tree.options)==2
+        let options=deepcopy(tree.options(path))
+    elseif type(tree.options)==type({})
+        let options=deepcopy(tree.options)
+    else
+        let options={}
+    endif
+    if has_key(options, 'treechars')
+        let options.treechars=split(options.treechars, '\v.@=')
+    else
+        let options.treechars=((&encoding is# 'utf-8')?(s:uchars):
+                    \                                  (s:nuchars))
+    endif
+    if !has_key(options, 'sortby')
+        let options.sortby=tree.defsortby
+    endif
+    if !has_key(options, 'columns')
+        let options.columns=tree.coldefault
+    endif
+    if !has_key(options, 'colopts')
+        let options.colopts={}
+    endif
+    for col in filter(copy(options.columns), '!has_key(options.colopts, v:val)')
+        let options.colopts[col]={}
+    endfor
+    "▲2
+    let bvar=s:F.setbuf({'tree': tree, 'tdata': tdata, 'options': options})
+    let s:_r.bufvars[buf]=bvar
+    let tree.buffers+=[buf]
     if has_key(tree, 'write')
         augroup Gtregen
-            execute 'autocmd BufWriteCmd <buffer> '.
+            " FIXME Run this commmand only for {tid}:// targets
+            execute 'autocmd! BufWriteCmd <buffer> '.
                         \':call s:trees['.string(tree.id).'].write()'
         augroup END
         setlocal buftype=acwrite
     else
         setlocal buftype=nofile
     endif
-    " TODO Add mappings
+    augroup Gtregen
+        autocmd! InsertEnter <buffer> :call feedkeys("\<C-\>\<C-n>")
+    augroup END
+    call s:_f.mapgroup.map(tree.mgname, buf)
 endfunction
 "▶1
-call frawor#Lockvar(s:, 'trees,_pluginloaded')
+call frawor#Lockvar(s:, '_pluginloaded,_r,trees')
 " vim: ft=vim ts=4 sts=4 et fmr=▶,▲

File plugin/gtregen/bufvars.vim

+"▶1 
+scriptencoding utf-8
+if !exists('s:_pluginloaded')
+    execute frawor#Setup('0.0', {'@/resources': '0.0',}, 0)
+    finish
+elseif s:_pluginloaded
+    finish
+endif
+let s:bufvars={}
+"▶1 bufwipeout
+function s:F.bufwipeout()
+    let buf=+expand('<abuf>')
+    if has_key(s:bufvars, buf)
+        call filter(s:bufvars[buf].tree.buffers, 'v:val=='.buf)
+        unlet s:bufvars[buf]
+    endif
+endfunction
+augroup GtregenBufVars
+    autocmd BufWipeOut * :call s:F.bufwipeout()
+augroup END
+let s:_augroups+=['GtregenBufVars']
+"▶1
+call s:_f.postresource('bufvars', s:bufvars, 1)
+"▶1
+call frawor#Lockvar(s:, '_pluginloaded,bufvars')
+" vim: ft=vim ts=4 sts=4 et fmr=▶,▲