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