tutor.vim (6912B)
1 " vim: fdm=marker et ts=4 sw=4 2 3 " Setup: {{{1 4 function! tutor#SetupVim() 5 if !exists('g:did_load_ftplugin') || g:did_load_ftplugin != 1 6 filetype plugin on 7 endif 8 if has('syntax') 9 if !exists('g:syntax_on') || g:syntax_on == 0 10 syntax on 11 endif 12 endif 13 endfunction 14 15 " Loads metadata file, if available 16 function! tutor#LoadMetadata() 17 let b:tutor_metadata = json_decode(join(readfile(expand('%').'.json'), "\n")) 18 endfunction 19 20 " Mappings: {{{1 21 22 function! tutor#SetNormalMappings() 23 nnoremap <silent> <buffer> <CR> :call tutor#FollowLink(0)<cr> 24 nnoremap <silent> <buffer> <2-LeftMouse> :call tutor#MouseDoubleClick()<cr> 25 nnoremap <buffer> >> :call tutor#InjectCommand()<cr> 26 endfunction 27 28 function! tutor#MouseDoubleClick() 29 if foldclosed(line('.')) > -1 30 normal! zo 31 else 32 if match(getline('.'), '^#\{1,} ') > -1 && foldlevel(line('.')) > 0 33 silent normal! zc 34 else 35 call tutor#FollowLink(0) 36 endif 37 endif 38 endfunction 39 40 function! tutor#InjectCommand() 41 let l:cmd = substitute(getline('.'), '^\s*', '', '') 42 exe l:cmd 43 redraw | echohl WarningMsg | echon "tutor: ran" | echohl None | echon " " | echohl Statement | echon l:cmd 44 endfunction 45 46 function! tutor#FollowLink(force) 47 let l:stack_s = join(map(synstack(line('.'), col('.')), 'synIDattr(v:val, "name")'), '') 48 if l:stack_s =~# 'tutorLink' 49 let l:link_start = searchpairpos('\[', '', ')', 'nbcW') 50 let l:link_end = searchpairpos('\[', '', ')', 'ncW') 51 if l:link_start[0] == l:link_end[0] 52 let l:linkData = getline(l:link_start[0])[l:link_start[1]-1:l:link_end[1]-1] 53 else 54 return 55 endif 56 let l:target = matchstr(l:linkData, '(\@<=.*)\@=') 57 if a:force != 1 && match(l:target, '\*.\+\*') > -1 58 call cursor(l:link_start[0], l:link_end[1]) 59 call search(l:target, '') 60 normal! ^ 61 elseif a:force != 1 && match(l:target, '^@tutor:') > -1 62 let l:tutor = matchstr(l:target, '@tutor:\zs.*') 63 exe "Tutor ".l:tutor 64 else 65 exe "help ".l:target 66 endif 67 endif 68 endfunction 69 70 " Folding And Info: {{{1 71 72 function! tutor#TutorFolds() 73 if getline(v:lnum) =~# '^#\{1,6}' 74 return ">". len(matchstr(getline(v:lnum), '^#\{1,6}')) 75 else 76 return "=" 77 endif 78 endfunction 79 80 " Tutor Cmd: {{{1 81 82 function! s:Locale() 83 " Make sure l:lang exists before returning. 84 let l:lang = 'en_US' 85 if exists('v:lang') && v:lang =~ '\a\a' 86 let l:lang = v:lang 87 elseif $LC_ALL =~ '\a\a' 88 let l:lang = $LC_ALL 89 elseif $LC_MESSAGES =~ '\a\a' || $LC_MESSAGES ==# "C" 90 " LC_MESSAGES=C can be used to explicitly ask for English messages while 91 " keeping LANG non-English; don't set l:lang then. 92 if $LC_MESSAGES =~ '\a\a' 93 let l:lang = $LC_MESSAGES 94 endif 95 elseif $LANG =~ '\a\a' 96 let l:lang = $LANG 97 endif 98 return split(l:lang, '_') 99 endfunction 100 101 function! s:GlobPath(lp, pat) 102 if version >= 704 && has('patch279') 103 return globpath(a:lp, a:pat, 1, 1) 104 else 105 return split(globpath(a:lp, a:pat, 1), '\n') 106 endif 107 endfunction 108 109 function! s:Sort(a, b) 110 let mod_a = fnamemodify(a:a, ':t') 111 let mod_b = fnamemodify(a:b, ':t') 112 if mod_a == mod_b 113 let retval = 0 114 elseif mod_a > mod_b 115 if match(mod_a, '^vim-') > -1 && match(mod_b, '^vim-') == -1 116 let retval = -1 117 else 118 let retval = 1 119 endif 120 else 121 if match(mod_b, '^vim-') > -1 && match(mod_a, '^vim-') == -1 122 let retval = 1 123 else 124 let retval = -1 125 endif 126 endif 127 return retval 128 endfunction 129 130 " returns a list of all tutor files matching the given name 131 function! tutor#GlobTutorials(name, locale) 132 let locale = a:locale 133 " pack/*/start/* are not reported in &rtp 134 let rtp = nvim_list_runtime_paths() 135 \ ->map({_, v -> escape(v:lua.vim.fs.normalize(v), ',')}) 136 \ ->join(',') 137 " search for tutorials: 138 " 1. non-localized 139 let l:tutors = s:GlobPath(rtp, 'tutor/'.a:name.'.tutor') 140 " 2. localized for current locale 141 let l:locale_tutors = s:GlobPath(rtp, 'tutor/'.locale.'/'.a:name.'.tutor') 142 " 3. fallback to 'en' 143 if len(l:locale_tutors) == 0 144 let l:locale_tutors = s:GlobPath(rtp, 'tutor/en/'.a:name.'.tutor') 145 endif 146 call extend(l:tutors, l:locale_tutors) 147 return uniq(sort(l:tutors, 's:Sort'), 's:Sort') 148 endfunction 149 150 function! tutor#TutorCmd(tutor_name) 151 if match(a:tutor_name, '[[:space:]]') > 0 152 echom "Only one argument accepted (check spaces)" 153 return 154 endif 155 156 if a:tutor_name == '' 157 let l:tutor_name = 'vim-01-beginner.tutor' 158 else 159 let l:tutor_name = a:tutor_name 160 endif 161 162 if match(l:tutor_name, '\.tutor$') > 0 163 let l:tutor_name = fnamemodify(l:tutor_name, ':r') 164 endif 165 166 let l:tutors = tutor#GlobTutorials(l:tutor_name, s:Locale()[0]) 167 168 if len(l:tutors) == 0 169 echom "No tutorial with that name found" 170 return 171 endif 172 173 if len(l:tutors) == 1 174 let l:to_open = l:tutors[0] 175 else 176 let l:idx = 0 177 let l:candidates = ['Several tutorials with that name found. Select one:'] 178 for candidate in map(copy(l:tutors), 179 \'fnamemodify(v:val, ":h:h:t")."/".s:Locale()[0]."/".fnamemodify(v:val, ":t")') 180 let l:idx += 1 181 call add(l:candidates, l:idx.'. '.candidate) 182 endfor 183 let l:tutor_to_open = inputlist(l:candidates) 184 let l:to_open = l:tutors[l:tutor_to_open-1] 185 endif 186 187 call tutor#SetupVim() 188 exe "drop ".fnameescape(l:to_open) 189 call tutor#EnableInteractive(v:true) 190 call tutor#ApplyTransform() 191 endfunction 192 193 function! tutor#TutorCmdComplete(lead,line,pos) 194 let l:tutors = tutor#GlobTutorials('*', s:Locale()[0]) 195 let l:names = uniq(sort(map(l:tutors, 'fnamemodify(v:val, ":t:r")'), 's:Sort')) 196 return join(l:names, "\n") 197 endfunction 198 199 " Enables/disables interactive mode. 200 function! tutor#EnableInteractive(enable) 201 let enable = a:enable 202 if enable 203 setlocal buftype=nowrite 204 setlocal concealcursor+=inv 205 setlocal conceallevel=2 206 lua require('nvim.tutor').apply_marks() 207 augroup tutor_interactive 208 autocmd! TextChanged,TextChangedI <buffer> lua require('nvim.tutor').apply_marks_on_changed() 209 augroup END 210 else 211 setlocal buftype< 212 setlocal concealcursor< 213 setlocal conceallevel< 214 if exists('#tutor_interactive') 215 autocmd! tutor_interactive * <buffer> 216 endif 217 endif 218 endfunction 219 220 function! tutor#ApplyTransform() 221 if has('win32') 222 sil! %s/{unix:(\(.\{-}\)),win:(\(.\{-}\))}/\2/g 223 else 224 sil! %s/{unix:(\(.\{-}\)),win:(\(.\{-}\))}/\1/g 225 endif 226 normal! gg0 227 endfunction