gemivim

VIM plugin for Gemini browsing
Log (Feed) | Files | Refs (Tags) | README | LICENSE

gemivim.vim (6940B)


      1 let g:tmp_prefix = '/tmp/gemivim/'
      2 
      3 if !has_key(g:, 'gemini_bookmarks_file')
      4     if filereadable(expand('~/.local/share/gmni/bookmarks.gmi'))
      5         let g:gemini_bookmarks_file = $HOME . '/.local/share/gmni/bookmarks.gmi'
      6     else
      7         let g:gemini_bookmarks_file = $HOME . '/.vim/bookmarks.gmi'
      8     endif
      9 endif
     10 
     11 if !isdirectory(g:tmp_prefix) && g:tmp_prefix =~ '.*/$'
     12     call mkdir(g:tmp_prefix)
     13 endif
     14 
     15 function! s:error_msg(msg)
     16     echohl WarningMsg | echom a:msg | echohl None
     17 endfunction
     18 
     19 function! s:info_msg(msg)
     20     echohl Question | echom a:msg | echohl None
     21 endfunction
     22 
     23 function! s:tmp_file_name(url, mime_type)
     24     let l:file = substitute(a:url, 'gemini://', '', 'g')
     25     if match(l:file, '/') == -1
     26         let l:file = l:file . '/'
     27     endif
     28     let l:file = g:tmp_prefix . l:file
     29     if a:mime_type ==# 'text/gemini' && l:file =~# '/$'
     30         return l:file . 'index.gmi'
     31     else
     32         return l:file
     33     endif
     34 endfunction
     35 
     36 function! s:mkdir_for_file(file)
     37     call mkdir(fnamemodify(a:file, ':p:h'), 'p')
     38 endfunction
     39 
     40 function! s:url_from_file(file)
     41     "let l:url = substitute(a:file, '^/tmp/', 'gemini://', 'g')
     42     let l:url = substitute(a:file, '^' . g:tmp_prefix, 'gemini://', 'g')
     43     if l:url =~# '.*/index\.gmi$'
     44         return substitute(l:url, 'index\.gmi$', '', 'g')
     45     else
     46         return l:url
     47     endif
     48 endfunction
     49 
     50 function! s:system_split_output(command)
     51     let l:stdout = g:tmp_prefix . 'stdout'
     52     let l:stderr = g:tmp_prefix . 'stderr'
     53     call system(a:command . ' 1>' . l:stdout . ' 2>' . l:stderr)
     54     return [v:shell_error, readfile(l:stdout), readfile(l:stderr)]
     55 endfunction
     56 
     57 function s:ssl_error(output)
     58     for line in a:output
     59         let l:groups = matchlist(line, '.*SSL error \(\d\+\)$')
     60         if len(l:groups) > 1
     61             return str2nr(l:groups[1])
     62         endif
     63     endfor
     64     return 0
     65 endfunction
     66 
     67 function! gemivim#Get(url, ...)
     68     let l:trust_choice = get(a:, 1, '')
     69 
     70     if l:trust_choice ==# 'always'
     71         let l:j_opt = '-j always'
     72     elseif l:trust_choice ==# 'once'
     73         let l:j_opt = '-j once'
     74     else
     75         let l:j_opt = ''
     76     endif
     77 
     78     if !executable('gmni')
     79         call s:error_msg('gmni is not installed. Visit https://sr.ht/~sircmpwn/gmni/')
     80         return
     81     endif
     82 
     83     let [l:exit_code, l:stdout, l:stderr] = s:system_split_output('gmni -I ' . l:j_opt . ' "' . a:url .'"')
     84     if l:exit_code != 0
     85         if s:ssl_error(l:stderr) == 62
     86             let l:choice = input(l:stderr[0] ."\n". l:stderr[1] . "\nIf you knew the fingerprint to expect in advance, verify that this matches.\n
     87                         \ Otherwise, it should be safe to trust this certificate.
     88                         \ [t]rust always, [o]nce, [a]bort: ")
     89             if l:choice == "t"
     90                 return gemivim#Get(a:url, 'always')
     91             elseif l:choice == "o"
     92                 return gemivim#Get(a:url, 'once')
     93             else
     94                 call s:info_msg('Aborted')
     95                 return
     96             endif
     97         else
     98             call s:error_msg(join(l:stderr, "\n"))
     99             return
    100         endif
    101     endif
    102 
    103     let l:header = split(l:stdout[0])
    104     if l:header[0] =~# '1\d'
    105         let l:prompt = join(header[1:], ' ') . ': '
    106         let l:input = input(l:prompt)
    107         call gemivim#Get(a:url . '?' . l:input, l:trust_choice)
    108     elseif l:header[0] =~# '2\d'
    109         let l:tmp_file = s:tmp_file_name(a:url, split(l:header[1], ";")[0])
    110         call s:mkdir_for_file(l:tmp_file)
    111         let [l:exit_code, l:stdout, l:stderr] = s:system_split_output('gmni ' . l:j_opt . ' "' . a:url . '"')
    112         if l:exit_code == 0
    113             call writefile(l:stdout, l:tmp_file)
    114             execute 'view ' . l:tmp_file
    115         else
    116             call s:error_msg(join(l:stderr, "\n"))
    117         endif
    118     elseif l:header[0] =~# '3\d'
    119         call gemivim#Get(l:header[1], l:trust_choice)
    120     elseif l:header[0] =~# '[456]\d'
    121         call s:error_msg(a:url . ': ' . join(l:stdout, "\n"))
    122     else
    123         call s:error_msg(a:url . ': ' . join(l:stdout + l:stderr, "\n"))
    124     endif
    125 endfunction
    126 
    127 function! s:current_url()
    128     return s:url_from_file(bufname())
    129 endfunction
    130 
    131 function! s:base_url(url)
    132     let l:url = 'gemini://' . split(substitute(a:url, 'gemini://', '', 'g'), '/')[0]
    133     return substitute(l:url, '.gmi$', '', 'g')
    134 endfunction
    135 
    136 function! s:base_path(url)
    137     return substitute(a:url, '[^/]*$', '', '')
    138 endfunction
    139 
    140 function! s:relative_url(from, relative)
    141     if a:relative =~# '^/.*'
    142         return s:base_url(a:from) . a:relative
    143     else
    144         return s:base_path(a:from) . substitute(a:relative, '^./', '', '')
    145     endif
    146 endfunction
    147 
    148 function! gemivim#Open()
    149     let l:url = trim(input("Open gemini URL: "))
    150     if l:url !~? '^gemini://.*'
    151         let l:url = 'gemini://' . l:url
    152     endif
    153 
    154     if len(l:url) > 0
    155         call gemivim#Get(l:url)
    156     else
    157         call s:info_msg('Nevermind')
    158     endif
    159 endfunction
    160 
    161 function! gemivim#GX()
    162     let url = expand('<cWORD>')
    163     if url =~? 'gemini://.*'
    164         call gemivim#Get(url)
    165     elseif url =~? 'http://.*' || url =~? 'https://.*' || url =~? 'gopher://.*'
    166         call s:error_msg('Not a gemini url')
    167     else
    168         call gemivim#Get(s:relative_url(s:current_url(), url))
    169     endif
    170 endfunction
    171 
    172 function! s:add_url_to_bookmarks(url)
    173     if a:url =~? 'gemini://.*'
    174         for link in readfile(g:gemini_bookmarks_file)
    175             if link =~? '=> ' . a:url . '$' || link =~? '=> ' . a:url . '\_s.*'
    176                 call s:info_msg('Already in bookmarks')
    177                 return 
    178             endif
    179         endfor
    180         call writefile(['=> '. a:url], g:gemini_bookmarks_file, 'a')
    181         call s:info_msg('Added to bookmarks: ' . a:url)
    182     else
    183         call s:error_msg('Not a gemini url: ' . a:url)
    184     endif
    185 endfunction
    186 
    187 function! s:remove_url_from_bookmarks(url)
    188     if a:url =~? 'gemini://.*'
    189         for link in readfile(g:gemini_bookmarks_file)
    190             if link =~? '=> ' . a:url . '$' || link =~? '=> ' . a:url . '\_s.*'
    191                 continue
    192             endif
    193             call writefile([ link ], g:gemini_bookmarks_file. '.temp', 'a')
    194         endfor
    195         call system('mv '. g:gemini_bookmarks_file.'.temp '. g:gemini_bookmarks_file)
    196         call s:info_msg('Removed bookmark: ' . a:url)
    197     else
    198         call s:error_msg('Not a gemini url: ' . a:url)
    199     endif
    200 endfunction
    201 
    202 function! gemivim#OpenBookmarks()
    203     execute 'edit ' . g:gemini_bookmarks_file
    204 endfunction
    205 
    206 function! gemivim#CurrentUrlToBookmarks()
    207     call s:add_url_to_bookmarks(s:current_url())
    208 endfunction!
    209 
    210 function! gemivim#CursorUrlToBookmarks()
    211     call s:add_url_to_bookmarks(expand('<cWORD>'))
    212 endfunction
    213 
    214 function! gemivim#CursorUrlDelBookmark()
    215     call s:remove_url_from_bookmarks(expand('<cWORD>'))
    216 endfunction!
    217 
    218 function! gemivim#CurrentUrlDelBookmark()
    219     call s:remove_url_from_bookmarks(s:current_url())
    220 endfunction!
    221 
    222 function! gemivim#Refresh()
    223     call gemivim#Get(s:current_url())
    224 endfunction
    225