xff.cz - mapa webu - novinky

Použití editoru Vim k přímému editování obsahu Wiki

Tento web je postavený na RDBMS PostgreSQL. V databázi jsou tabulky pro stránky a soubory. Každá stránka má text psaný v syntaxi Texy!. Obsah stránky je uložený ve dvou sloupcích: content (publikovaný obsah) a scratch (rozepsaný text či poznámky a extra zdroje).

Vše běží mimo mojí pracovní stanici někde na internetu.

Jeden způsob jak obsah webu snadno a rychle editovat, bez nutnosti používat nepohodlné webové rozhraní je využít editor na který jsem zvyklý – Vim – a naučit ho editovat stránky vzdáleně. A protože web je hypertext, editor by měl umět snadno a pohodlně následovat odkazy přímo v Texy kódu a podobně jako v prohlížeči mít historii prohlížení a umět se vrátit zpět, ideálně se zapamatováním původní pozice kurzoru na předchozí stránce.

Nakonec můj web je specifický v tom, že každá stránka má skrytou oblast pro poznámky a tak editor musí být schopen zobrazit současně dvě individuální editační oblasti, jednu pro hlavní obsah a jednu pro poznámky a pěkně je v tandemu aktualizovat při následování odkazů.

Pohyb mezi soubory by měl fungovat pomocí zkratek na který jsem v editoru zvyklý při editaci např. kódu v C. Tedy gf otevře soubor jehož název je pod kurzorem (v mém případě otevře stránku na webu na kterou odkaz pod kurzorem odkazuje), Ctrl+o provede navigaci zpět, atp. Tedy aby iluze, že edituji lokální soubory, byla celkem přesvědčivá.

Vim je mocný editor a ukazuje se, že dosáhnout výše uvedeného není zas takový problém.

Jak na to

Vim je programova­telný pomocí VimScriptu (:help script) a ten se bude hodit k implementaci několika funkcí, které obstarají načítání kódu stránek do editoru z webu a ukládání editované stránky po úpravách zpět do databáze.

Celý kód vypadá takto:

setlocal buftype=nofile
setlocal bufhidden=hide
setlocal noswapfile
setlocal filetype=conf
setlocal buflisted
vnew
setlocal buftype=nofile
setlocal filetype=conf
setlocal bufhidden=hide
setlocal noswapfile
setlocal buflisted

let g:wiki_paths = []
let g:wiki_locs = []

function WikiGetState()
        let s = {'win': winnr()}
        exe bufwinnr(1) . "wincmd w"
        let s.w1 = winsaveview()
        exe bufwinnr(2) . "wincmd w"
        let s.w2 = winsaveview()
        call add(g:wiki_locs, s)
        return s
endfunction

function WikiSetState(s)
        let s = a:s
        exe bufwinnr(1) . "wincmd w"
        call winrestview(s.w1)
        exe bufwinnr(2) . "wincmd w"
        call winrestview(s.w2)
        exe (s.win) . "wincmd w"
endfunction

function WikiAddHist(path)
        call add(g:wiki_paths, a:path)
endfunction

function WikiOpenAdd(path)
        let path = a:path
        if path !~ "^/"
                let path = "/" . path
        endif

        call WikiSave()

        let s = WikiGetState()
        call WikiOpen(path)
        call WikiAddHist(path)
        call add(g:wiki_locs, s)
endfunction

function WikiOpen(path)
        let path = a:path
        if path !~ "^/"
                let path = "/" . path
        endif

        let p = json_decode(system("php conn.php read " . path))
        if type(p) != v:t_dict
                let p = {'content': '', 'scratch': ''}
        endif

        exe bufwinnr(1) . "wincmd w"
        norm! ggVGd
        call append(0, split(p.content, '\r\?\n'))
        exe bufwinnr(2) . "wincmd w"
        norm! ggVGd
        call append(0, split(p.scratch, '\r\?\n'))
        exe bufwinnr(1) . "wincmd w"
        exe "norm! gg"
endfunction

function WikiPrev()
        if len(g:wiki_paths) <= 1
                return
        endif

        call WikiSave()

        call remove(g:wiki_paths, -1)
        let p = get(g:wiki_paths, -1)
        call WikiOpen(p)

        let s = get(g:wiki_locs, -1)
        call remove(g:wiki_locs, -1)
        call WikiSetState(s)
endfunction

function WikiSave()
        if len(g:wiki_paths) < 1
                return
        endif

        let p = get(g:wiki_paths, -1)

        let d = {}
        let wn = winnr()
        exe bufwinnr(1) . "wincmd w"
        let d.content = join(getline(1, '$'), "\n")
        exe bufwinnr(2) . "wincmd w"
        let d.scratch = join(getline(1, '$'), "\n")
        exe wn . "wincmd w"

        call system("php conn.php write " . p, json_encode(d))
endfunction

command -nargs=+ Open  call WikiOpenAdd(<f-args>)
command -nargs=0 Save  call WikiSave()
command -nargs=0 Prev  call WikiPrev()

nnoremap gf :call WikiOpenAdd(expand("<cfile>"))<CR>
nnoremap gb :call WikiPrev()<CR>
nnoremap gs :call WikiSave()<CR>
nnoremap <C-o> :call WikiPrev()<CR>
inoremap <C-o> <Esc>:call WikiPrev()<CR>

call WikiOpenAdd('/')

Kód je možné vložit do nějakého adresáře do souboru .vimrc a povolit set ex v ~/.vimrc. Předpokládá existenci skriptu conn.php, který umí získat data stránky ze vzdáleného zdroje, či je uložit.

Výsledek vypadá takto:

Historie změn

15.7.2018 18:17První sestavení webu