blob: 3265fdde369a0425e28b55fc1af23853d93327f5 [file] [log] [blame]
Yegappan Lakshmanana54816b2024-11-03 10:49:23 +01001" vim: fdm=marker et ts=4 sw=4
2
3" Setup: {{{1
4function! 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
13endfunction
14
15" Loads metadata file, if available
16function! tutor#LoadMetadata()
17 let b:tutor_metadata = json_decode(join(readfile(expand('%').'.json'), "\n"))
18endfunction
19
20" Mappings: {{{1
21
22function! 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>
26endfunction
27
28function! tutor#MouseDoubleClick()
29 if foldclosed(line('.')) > -1
30 normal! zo
31 else
32 if match(getline('.'), '^#\{1,} ') > -1
33 silent normal! zc
34 else
35 call tutor#FollowLink(0)
36 endif
37 endif
38endfunction
39
40function! 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
44endfunction
45
46function! 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
68endfunction
69
70" Folding And Info: {{{1
71
72function! tutor#TutorFolds()
73 if getline(v:lnum) =~# '^#\{1,6}'
74 return ">". len(matchstr(getline(v:lnum), '^#\{1,6}'))
75 else
76 return "="
77 endif
78endfunction
79
80" Marks: {{{1
81
82function! tutor#ApplyMarks()
83 hi! link tutorExpect Special
84 if exists('b:tutor_metadata') && has_key(b:tutor_metadata, 'expect')
85 let b:tutor_sign_id = 1
86 for expct in keys(b:tutor_metadata['expect'])
87 let lnum = eval(expct)
88 call matchaddpos('tutorExpect', [lnum])
89 call tutor#CheckLine(lnum)
90 endfor
91 endif
92endfunction
93
94function! tutor#ApplyMarksOnChanged()
95 if exists('b:tutor_metadata') && has_key(b:tutor_metadata, 'expect')
96 let lnum = line('.')
97 if index(keys(b:tutor_metadata['expect']), string(lnum)) > -1
98 call tutor#CheckLine(lnum)
99 endif
100 endif
101endfunction
102
103function! tutor#CheckLine(line)
104 if exists('b:tutor_metadata') && has_key(b:tutor_metadata, 'expect')
105 let bufn = bufnr('%')
106 let ctext = getline(a:line)
107 if b:tutor_metadata['expect'][string(a:line)] == -1 || ctext ==# b:tutor_metadata['expect'][string(a:line)]
108 exe "sign place ".b:tutor_sign_id." line=".a:line." name=tutorok buffer=".bufn
109 else
110 exe "sign place ".b:tutor_sign_id." line=".a:line." name=tutorbad buffer=".bufn
111 endif
112 let b:tutor_sign_id+=1
113 endif
114endfunction
115
116" Tutor Cmd: {{{1
117
118function! s:Locale()
119 if exists('v:lang') && v:lang =~ '\a\a'
120 let l:lang = v:lang
121 elseif $LC_ALL =~ '\a\a'
122 let l:lang = $LC_ALL
123 elseif $LANG =~ '\a\a'
124 let l:lang = $LANG
125 else
126 let l:lang = 'en_US'
127 endif
128 return split(l:lang, '_')
129endfunction
130
131function! s:GlobPath(lp, pat)
132 if version >= 704 && has('patch279')
133 return globpath(a:lp, a:pat, 1, 1)
134 else
135 return split(globpath(a:lp, a:pat, 1), '\n')
136 endif
137endfunction
138
139function! s:Sort(a, b)
140 let mod_a = fnamemodify(a:a, ':t')
141 let mod_b = fnamemodify(a:b, ':t')
142 if mod_a == mod_b
143 let retval = 0
144 elseif mod_a > mod_b
145 if match(mod_a, '^vim-') > -1 && match(mod_b, '^vim-') == -1
146 let retval = -1
147 else
148 let retval = 1
149 endif
150 else
151 if match(mod_b, '^vim-') > -1 && match(mod_a, '^vim-') == -1
152 let retval = 1
153 else
154 let retval = -1
155 endif
156 endif
157 return retval
158endfunction
159
160function! s:GlobTutorials(name)
161 " search for tutorials:
162 " 1. non-localized
163 let l:tutors = s:GlobPath(&rtp, 'tutor/'.a:name.'.tutor')
164 " 2. localized for current locale
165 let l:locale_tutors = s:GlobPath(&rtp, 'tutor/'.s:Locale()[0].'/'.a:name.'.tutor')
166 " 3. fallback to 'en'
167 if len(l:locale_tutors) == 0
168 let l:locale_tutors = s:GlobPath(&rtp, 'tutor/en/'.a:name.'.tutor')
169 endif
170 call extend(l:tutors, l:locale_tutors)
171 return uniq(sort(l:tutors, 's:Sort'), 's:Sort')
172endfunction
173
174function! tutor#TutorCmd(tutor_name)
175 if match(a:tutor_name, '[[:space:]]') > 0
176 echom "Only one argument accepted (check spaces)"
177 return
178 endif
179
180 if a:tutor_name == ''
181 let l:tutor_name = 'vim-01-beginner.tutor'
182 else
183 let l:tutor_name = a:tutor_name
184 endif
185
186 if match(l:tutor_name, '\.tutor$') > 0
187 let l:tutor_name = fnamemodify(l:tutor_name, ':r')
188 endif
189
190 let l:tutors = s:GlobTutorials(l:tutor_name)
191
192 if len(l:tutors) == 0
193 echom "No tutorial with that name found"
194 return
195 endif
196
197 if len(l:tutors) == 1
198 let l:to_open = l:tutors[0]
199 else
200 let l:idx = 0
201 let l:candidates = ['Several tutorials with that name found. Select one:']
202 for candidate in map(copy(l:tutors),
203 \'fnamemodify(v:val, ":h:h:t")."/".s:Locale()[0]."/".fnamemodify(v:val, ":t")')
204 let l:idx += 1
205 call add(l:candidates, l:idx.'. '.candidate)
206 endfor
207 let l:tutor_to_open = inputlist(l:candidates)
208 let l:to_open = l:tutors[l:tutor_to_open-1]
209 endif
210
211 call tutor#SetupVim()
212 exe "edit ".l:to_open
213endfunction
214
215function! tutor#TutorCmdComplete(lead,line,pos)
216 let l:tutors = s:GlobTutorials('*')
217 let l:names = uniq(sort(map(l:tutors, 'fnamemodify(v:val, ":t:r")'), 's:Sort'))
218 return join(l:names, "\n")
219endfunction