Source

aurum / autoload / aurum / tabutils.vim

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
"▶1 
scriptencoding utf-8
execute frawor#Setup('1.0', {'@/options': '0.0',
            \                     '@/os': '0.0',
            \             '@%aurum/edit': '1.0',
            \          '@%aurum/bufvars': '0.0',
            \           '@%aurum/commit': '1.3',})
let s:_messages={
            \'tidnstr': 'Tab identifier must be a non-empty string',
            \'tidreg' : 'Tab identifier “%s” was already registered '.
            \           'by plugin %s',
            \'layinv' : "Invalid layout: it must\n".
            \           " - be a dictionary\n".
            \           " - that has only “top”, “bottom”, “oprefix”, ".
            \              "“unload” keys\n".
            \           " - with “top” key value being a non-empty ".
            \              "list of unique strings\n".
            \           " - and “bottom” key value being a string not present ".
            \              "in “top” list\n".
            \           " - ",
            \'tidukn' : 'Unknown tabid or tabid is not a string',
            \'botncal': 'botfun argument is not callable',
            \'barnlst': 'botargs argument is not a list',
            \  'tabex': 'There already is tab with id “%s”',
            \ 'nsbvar': 'Internal error: sbvar key is present and sbuf '.
            \           'is pointing to unknown buffer. If you can reproduce '.
            \           'this file a bug report',
        \}
let s:f={'cons': {}}
"▶1 options
let s:options={
            \'height':    {'default': 0,
            \               'filter': '(if type "" earg _  range 0 inf)'},
            \'autowrite': {'default': 1,
            \               'filter': 'bool'},
        \}
let s:_options={}
"▶1 getswheight
function s:F.getswheight(tabspec)
    let height=s:_f.getoption(a:tabspec.oprefix . 'height')
    if height<=0
        return winheight(0)/5
    endif
    return height
endfunction
"▶1 getids
function s:F.getids(layout)
    return a:layout.top + [a:layout.bottom]
endfunction
"▶1 f.cons.new :: {f}, tabid, layout → + s:tabs, fdict
let s:tabs={}
function s:f.cons.new(plugdict, fdict, tabid, tabdesc)
    "▶2 Check arguments
    if type(a:tabid)!=type('') || empty(a:tabid)
        call s:_f.throw('tidnstr')
    elseif has_key(s:tabs, a:tabid)
        call s:_f.throw('tidreg', a:tabid, s:tabs[a:tabid])
    elseif          type(a:tabdesc)!=type({})
                \|| sort(keys(a:tabdesc)) !=#
                \       ['bottom', 'oprefix', 'top', 'unload']
                \|| type(a:tabdesc.bottom)!=type('')
                \|| type(a:tabdesc.top)!=type([])
                \|| empty(a:tabdesc.top)
                \|| !empty(filter(copy(a:tabdesc.top),'type(v:val)!='.type('')))
                \|| !empty(filter(a:tabdesc.top[1:],
                \                 'index(a:tabdesc.top[:(v:key)], v:val)!=-1'))
                \|| index(a:tabdesc.top, a:tabdesc.bottom)!=-1
                \|| !exists('*a:tabdesc.unload')
                \|| !(type(a:tabdesc.oprefix) == type(0)
                \  || type(a:tabdesc.oprefix) == type(''))
        call s:_f.throw('layinv')
    endif
    "▶2 tabdesc
    let a:fdict[a:tabid]={
                \'layout': {'bottom': a:tabdesc.bottom,
                \              'top': copy(a:tabdesc.top),},
                \'oprefix': a:tabdesc.oprefix,
                \'unload': a:tabdesc.unload,
                \'ids': s:F.getids(a:tabdesc),
                \'id': a:tabid,
            \}
    let oprefix=a:tabdesc.oprefix
    let s:_options[oprefix.'height']=s:options.height
    let s:_options[oprefix.'autowrite']=s:options.autowrite
    let s:tabs[a:tabid]=a:plugdict.id
endfunction
"▶1 settoplayout
function s:F.settoplayout(layout)
    let w:aurum_winid=a:layout.top[0]
    for id in a:layout.top[1:]
        rightbelow vnew
        setlocal bufhidden=wipe
        let w:aurum_winid=id
    endfor
endfunction
"▶1 getwinnrs
function s:F.getwinnrs(ids)
    let curids={}
    call map(filter(map(range(1, winnr('$')),
                \'[getwinvar(v:val, "aurum_winid"), v:key+1]'),
                \'!empty(v:val[0])'),
                \'extend(curids, {v:val[0]: v:val[1]})')
    return map(copy(a:ids), 'get(curids, v:val, 0)')
endfunction
"▶1 getswnr
function s:F.getswnr(tabspec)
    return s:F.getwinnrs([a:tabspec.layout.bottom])[0]
endfunction
"▶1 getlwnr
function s:F.getlwnr(tabspec)
    return s:F.getwinnrs([a:tabspec.layout.top[0]])[0]
endfunction
"▶1 getlrwnrs
function s:F.getlrwnrs(tabspec)
    return s:F.getwinnrs(a:tabspec.layout.top[0:1])
endfunction
"▶1 unsetautowrite
function s:F.unsetautowrite()
    if exists('t:aurum_tabautowrite')
        if bufexists(t:aurum_tabautowrite)
            execute printf('autocmd! AuRecordAutowrite BufLeave <buffer=%u>',
                        \  t:aurum_tabautowrite)
        endif
        unlet t:aurum_tabautowrite
    endif
endfunction
"▶1 tabunload :: tabspec
function s:F.tabunload(tabspec)
    let swnr=s:F.getswnr(a:tabspec)
    if !swnr
        tabclose
        return -1
    endif
    call s:F.unsetautowrite()
    execute swnr.'wincmd w'
    let bvar=s:_r.bufvars[bufnr('%')]
    return a:tabspec.unload(bvar)
endfunction
"▶1 gettabspec :: fdict[, nothrow] → (tabid, tabspec)
function s:F.gettabspec(fdict, ...)
    let tabid=gettabvar(tabpagenr(), 'aurum_tabid')
    "▶2 Check tabid
    if type(tabid)!=type('') || !has_key(a:fdict, tabid)
        if !a:0 || !a:1
            call s:_f.throw('tidukn')
        else
            return [0, 0]
        endif
    endif
    "▲2
    let tabspec=a:fdict[tabid]
    return [tabid, tabspec]
endfunction
"▶1 f.cons.getwnrs :: {f} → ([topid] + bottomids)
function s:f.cons.getwnrs(plugdict, fdict)
    let [tabid, tabspec]=s:F.gettabspec(a:fdict)
    let ids=tabspec.ids
    let winnrs=s:F.getwinnrs(ids)
    if winnrs[0] is 0 || index(winnrs, 0)==-1
        return winnrs
    else
        execute winnrs[-1].'wincmd w'
        silent only!
        topleft new
        call s:F.settoplayout(tabspec.layout)
        wincmd j
        execute 'resize' s:F.getswheight(tabspec)
        return s:F.getwinnrs(ids)
    endif
endfunction
"▶1 f.cons.create :: {f}, tabid, botfun, botargs → + vim
let s:last_prevtabmark=0
let s:prevtabs={}
function s:f.cons.create(plugdict, fdict, tabid, botfun, botargs)
    "▶2 Check arguments
    if type(a:tabid)!=type('') || !has_key(a:fdict, a:tabid)
        call s:_f.throw('tidukn')
    elseif !exists('*a:botfun')
        call s:_f.throw('botncal')
    elseif type(a:botargs)!=type([])
        call s:_f.throw('barnlst')
    endif
    "▲2
    if !empty(filter(range(1, tabpagenr('$')),
                \    'gettabvar(v:val, "aurum_tabid") is# '.string(a:tabid)))
        call s:_f.throw('tabex', a:tabid)
    endif
    let tabspec=a:fdict[a:tabid]
    if !exists('t:aurum_tabmark')
        let t:aurum_tabmark=s:last_prevtabmark
        let s:last_prevtabmark+=1
    endif
    let tabmark = t:aurum_tabmark
    tabnew
    setlocal bufhidden=wipe
    let t:aurum_tabid=tabspec.id
    call s:F.settoplayout(tabspec.layout)
    let height=s:F.getswheight(tabspec)
    call call(a:botfun, ['silent botright '.height.'split'] + a:botargs,
                \{})
    setlocal bufhidden=wipe
    let w:aurum_winid=tabspec.layout.bottom
    let s:prevtabs[tabspec.id]=tabmark
endfunction
"▶1 find :: tabid → tabnr + tab
function s:F.find(tabid)
    for tabnr in range(1, tabpagenr('$'))
        if gettabvar(tabnr, 'aurum_tabid') is# a:tabid
            execute 'tabnext' tabnr
            return tabnr
        endif
    endfor
    return 0
endfunction
"▶1 close :: tabid, tabspec
function s:F.close(tabid, tabspec)
    unlet t:aurum_tabid
    if tabpagenr('$')>1
        tabclose!
        if has_key(s:prevtabs, a:tabid)
            let tabmark=s:prevtabs[a:tabid]
            let tabnr=index(map(range(1, tabpagenr('$')),
                        \       'gettabvar(v:val, "aurum_tabmark")'), tabmark)+1
            if tabnr
                execute 'tabnext' tabnr
            endif
        endif
    else
        let wlist=range(1, winnr('$'))
        while !empty(wlist)
            for wnr in wlist
                call remove(wlist, 0)
                if index(a:tabspec.ids, getwinvar(wnr, 'aurum_winid'))
                    execute wnr.'wincmd w'
                    if winnr('$')==1
                        unlet w:aurum_winid
                        enew
                    else
                        close!
                    endif
                    let wlist=range(1, winnr('$'))
                    break
                endif
            endfor
        endwhile
    endif
    if has_key(s:prevtabs, a:tabid)
        unlet s:prevtabs[a:tabid]
    endif
    return 1
endfunction
"▶1 f.cons.setautowrite :: {f}
function s:f.cons.setautowrite(plugdict, fdict)
    let [tabid, tabspec]=s:F.gettabspec(a:fdict)
    if s:_f.getoption(tabspec.oprefix . 'autowrite')
        let t:aurum_tabautowrite=bufnr('%')
        augroup AuRecordAutowrite
            autocmd! BufLeave <buffer> nested write
        augroup END
    else
        unlet! t:aurum_tabautowrite
    endif
endfunction
"▶1 f.cons.restore :: {f}, bvar
function s:f.cons.restore(plugdict, fdict, bvar)
    let [tabid, tabspec]=s:F.gettabspec(a:fdict)
    let sbuf=get(a:bvar, 'sbuf', -1)
    "▶2 Check sbuf existence
    if !bufexists(sbuf)
        if has_key(a:bvar, 'sbvar')
            return call(tabspec.unload, [a:bvar.sbvar], {})
        elseif has_key(s:_r.bufvars, sbuf)
            return call(tabspec.unload, [s:_r.bufvars[sbuf]], {})
        else
            call s:_f.throw('nsbvar')
        endif
    endif
    "▲2
    let sbvar=a:bvar.sbvar
    execute 'silent botright sbuffer' sbuf
    execute 'resize' s:F.getswheight(tabspec)
    call winrestview(a:bvar.winview)
    redraw!
    let w:aurum_winid=tabspec.layout.bottom
    setlocal bufhidden=wipe
    return 1
endfunction
"▶1 commitvimdiffcb
function s:F.commitvimdiffcb(file, bvar, hex)
    let bvar=s:_r.bufvars[bufnr('%')]
    let [tabid, tabspec]=s:F.gettabspec(a:bvar.fdict)
    let [lwnr, rwnr]=s:F.getlrwnrs(tabspec)

    execute lwnr.'wincmd w'
    let file=s:_r.os.path.join(a:bvar.repo.path, a:file)
    let existed=bufexists(file)
    execute 'silent edit' fnameescape(file)
    if !existed
        setlocal bufhidden=wipe
    endif
    diffthis

    execute rwnr.'wincmd w'
    let existed=s:_r.run('silent edit', 'file', a:bvar.repo, a:hex, a:file)
    if !existed
        setlocal bufhidden=wipe
    endif
    diffthis

    execute lwnr.'wincmd w'
endfunction
"▶1 commitfindwindow :: {f}
function s:F.commitfindwindow()
    let bvar=s:_r.bufvars[bufnr('%')]
    let [tabid, tabspec]=s:F.gettabspec(bvar.fdict)
    let lwnr=s:F.getlwnr(tabspec)
    execute lwnr.'wincmd w'
    return 1
endfunction
"▶1 f.cons.copen :: {f}, bvar, buf, keys, opts[, status[, files]]
function s:f.cons.copen(plugdict, fdict, bvar, buf, keys, opts, ...)
    if a:0
        let status=a:1
        if a:0>1
            let files=a:2
        else
            let files=[]
        endif
    else
        let status=a:bvar.repo.functions.status(a:bvar.repo)
        let files=[]
    endif
    return s:_r.commit.commit(
                \   a:bvar.repo,
                \   a:bvar.copts,
                \   files,
                \   status,
                \   a:keys,
                \   'silent edit',
                \   extend({
                \       'vimdiffcb'  : s:F.commitvimdiffcb,
                \       'findwindow' : s:F.commitfindwindow,
                \       'bwfunc'     : a:plugdict.g._f.tab.restore,
                \       'sbvar'      : a:bvar,
                \       'sbuf'       : a:buf,
                \       'winview'    : winsaveview(),
                \       'fdict'      : a:fdict,
                \   }, a:opts),
                \)
endfunction
"▶1 f.cons.unload :: {f}
function s:f.cons.unload(plugdict, fdict, tabid, bvar)
    "▶2 Check argument
    if type(a:tabid)!=type('') || !has_key(a:fdict, a:tabid)
        call s:_f.throw('tidukn')
    endif
    "▲2
    let [tabid, tabspec]=s:F.gettabspec(a:fdict, 1)
    if tabid is 0
        unlet tabspec
        if s:F.find(a:tabid)
            let [tabid, tabspec]=s:F.gettabspec(a:fdict, 1)
        endif
        if tabid is 0
            return 1
        endif
    endif
    let swnr=s:F.getswnr(tabspec)
    if !swnr
        let sbuf=0
        let sbvar=get(a:bvar, 'sbvar', a:bvar)
    else
        let sbuf=winbufnr(swnr)
        let sbvar=s:_r.bufvars[sbuf]
    endif
    call s:F.unsetautowrite()
    call tabspec.unload(sbvar)
    if sbuf isnot 0 && bufexists(sbuf)
        unlet sbvar.bwfunc
        execute 'bwipeout!' sbuf
    endif
    if has_key(sbvar, 'savedopts')
        for [o, val] in items(sbvar.savedopts)
            execute 'let &g:'.o.'=val'
        endfor
    endif
    if bufexists(sbvar.bufnr)
        call setbufvar(sbvar.bufnr, '&modified', 0)
    endif
    call s:F.close(tabid, tabspec)
endfunction
"▶1 f.unloadpre :: {f}
function s:f.unloadpre(plugdict, fdict)
    for tabnr in range(1, tabpagenr('$'))
        let tabid=gettabvar(tabnr, 'aurum_tabid')
        if type(tabid)==type('') && has_key(a:fdict, tabid)
            execute 'tabnext' tabnr
            let tabspec=a:fdict[tabid]
            call s:F.tabunload(tabspec)
        endif
    endfor
    call map(keys(a:fdict), 'remove(s:tabs, v:val)')
endfunction
"▶1 Post resource
call s:_f.newfeature('tab', s:f)
"▶1
call frawor#Lockvar(s:, 'tabs,prevtabs,last_prevtabmark,_options,_r')
" vim: ft=vim ts=4 sts=4 et fmr=▶,▲