1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
|
" MNV filetype plugin
" Language: MNV
" Maintainer: Doug Kearns <dougkearns@gmail.com>
" Former Maintainer: Bram Moolenaar <Bram@mnv.org>
" Contributors: Riley Bruins <ribru17@gmail.com> ('commentstring')
" @Konfekt
" @tpope (s:Help())
" @lacygoill
" Last Change: 2025 Aug 07
" 2025 Aug 06 by MNV Project (add gf maps #17881)
" 2025 Aug 08 by MNV Project (add MNV script complete function #17871)
" 2025 Aug 12 by MNV Project (improve mnvgoto script #17970))
" 2025 Aug 16 by MNV Project set com depending on MNV9 or legacy script
" 2026 Jan 26 by MNV Project set path to common MNV directories #19219
" 2026 Feb 03 by MNV Project update s:Help to improve detecting functions #19320
" Only do this when not done yet for this buffer
if exists("b:did_ftplugin")
finish
endif
" Don't load another plugin for this buffer
let b:did_ftplugin = 1
let s:cpo_save = &cpo
set cpo&mnv
if !exists('*MNVFtpluginUndo')
func MNVFtpluginUndo()
setl fo< isk< com< tw< commentstring< include< define< keywordprg< omnifunc< path<
sil! delc -buffer MNVKeywordPrg
if exists('b:did_add_maps')
silent! nunmap <buffer> [[
silent! xunmap <buffer> [[
silent! nunmap <buffer> ]]
silent! xunmap <buffer> ]]
silent! nunmap <buffer> []
silent! xunmap <buffer> []
silent! nunmap <buffer> ][
silent! xunmap <buffer> ][
silent! nunmap <buffer> ]"
silent! xunmap <buffer> ]"
silent! nunmap <buffer> ["
silent! xunmap <buffer> ["
silent! nunmap <buffer> gf
silent! nunmap <buffer> <C-W>f
silent! nunmap <buffer> <C-W>gf
endif
unlet! b:match_ignorecase b:match_words b:match_skip b:did_add_maps
endfunc
endif
let b:undo_ftplugin = "call MNVFtpluginUndo()"
" Set 'formatoptions' to break comment lines but not other lines,
" and insert the comment leader when hitting <CR> or using "o".
setlocal fo-=t fo+=croql
" To allow tag lookup via CTRL-] for autoload functions, '#' must be a
" keyword character. E.g., for netrw#Nread().
setlocal isk+=#
" Use :help to lookup the keyword under the cursor with K.
" Distinguish between commands, options and functions.
if !exists("*" .. expand("<SID>") .. "Help")
function s:Help(topic) abort
let topic = a:topic
" keyword is not necessarily under the cursor, see :help K
let line = getline('.')
let i = match(line, '\V' .. escape(topic, '\'), col('.') - len(topic))
let pre = strpart(line, 0, i)
let post = strpart(line, i + len(topic))
" local/global option vars
if topic =~# '[lg]' && pre ==# '&' && post =~# ':\k\+'
let topic = matchstr(post, '\k\+')
endif
if get(g:, 'syntax_on', 0)
let syn = synIDattr(synID(line('.'), col('.'), 1), 'name')
if syn ==# 'mnvFuncName'
return topic .. '()'
elseif syn ==# 'mnvOption' || syn ==# 'mnvOptionVarName'
return "'" .. topic .. "'"
elseif syn ==# 'mnvUserCmdAttrKey'
return ':command-' .. topic
elseif syn ==# 'mnvCommand'
return ':' .. topic
endif
endif
if stridx(post, '(') == 0
return topic .. '()'
elseif pre =~# '^\s*:\=$' || pre =~# '\%(\\\||\)\@<!|\s*:\=$'
return ':' .. topic
elseif pre =~# '\<v:$'
return 'v:' .. topic
elseif pre =~# '<$'
return '<' .. topic .. '>'
elseif pre =~# '\\$'
return '/\' .. topic
elseif topic ==# 'v' && post =~# ':\w\+'
return 'v' .. matchstr(post, ':\w\+')
elseif pre =~# '&\%([lg]:\)\=$'
return "'" .. topic .. "'"
else
return topic
endif
endfunction
endif
command! -buffer -nargs=1 MNVKeywordPrg :exe 'help' s:Help(<q-args>)
setlocal keywordprg=:MNVKeywordPrg
" Comments starts with # in MNV9 script. We have to guess which one to use.
if "\n" .. getline(1, 32)->join("\n") =~# '\n\s*mnv9\%[script]\>'
setlocal commentstring=#\ %s
" Set 'comments' to format dashed lists in comments, for MNV9 script.
setlocal com=sO:#\ -,mO:#\ \ ,eO:##,:#\\\ ,:#
else
setlocal commentstring=\"%s
" Set 'comments' to format dashed lists in comments, for legacy MNV script.
setlocal com=sO:\"\ -,mO:\"\ \ ,eO:\"\",:\"\\\ ,:\"
endif
" set 'include' to recognize import commands
setlocal include=\\v^\\s*import\\s*(autoload)?
" set 'define' to recognize export commands
setlocal define=\\v^\\s*export\\s*(def\|const\|var\|final)
if has("mnv9script")
" set omnifunc completion
setlocal omnifunc=mnvcomplete#Complete
endif
" Format comments to be up to 78 characters long
if &tw == 0
setlocal tw=78
endif
" set 'path' to common MNV directories
setlocal path-=/usr/include
setlocal path+=pack/**,runtime/**,autoload/**,colors/**,compiler/**,ftplugin/**,indent/**,keymap/**,macros/**,plugin/**,syntax/**,after/**
if !exists("no_plugin_maps") && !exists("no_mnv_maps")
let b:did_add_maps = 1
" Move around functions.
nnoremap <silent><buffer> [[ m':call search('^\s*\(fu\%[nction]\\|\(export\s\+\)\?def\)\>', "bW")<CR>
xnoremap <silent><buffer> [[ m':<C-U>exe "normal! gv"<Bar>call search('^\s*\(fu\%[nction]\\|\(export\s\+\)\?def\)\>', "bW")<CR>
nnoremap <silent><buffer> ]] m':call search('^\s*\(fu\%[nction]\\|\(export\s\+\)\?def\)\>', "W")<CR>
xnoremap <silent><buffer> ]] m':<C-U>exe "normal! gv"<Bar>call search('^\s*\(fu\%[nction]\\|\(export\s\+\)\?def\)\>', "W")<CR>
nnoremap <silent><buffer> [] m':call search('^\s*end\(f\%[unction]\\|\(export\s\+\)\?def\)\>', "bW")<CR>
xnoremap <silent><buffer> [] m':<C-U>exe "normal! gv"<Bar>call search('^\s*end\(f\%[unction]\\|\(export\s\+\)\?def\)\>', "bW")<CR>
nnoremap <silent><buffer> ][ m':call search('^\s*end\(f\%[unction]\\|\(export\s\+\)\?def\)\>', "W")<CR>
xnoremap <silent><buffer> ][ m':<C-U>exe "normal! gv"<Bar>call search('^\s*end\(f\%[unction]\\|\(export\s\+\)\?def\)\>', "W")<CR>
" Move around comments
nnoremap <silent><buffer> ]" :call search('\%(^\s*".*\n\)\@<!\%(^\s*"\)', "W")<CR>
xnoremap <silent><buffer> ]" :<C-U>exe "normal! gv"<Bar>call search('\%(^\s*".*\n\)\@<!\%(^\s*"\)', "W")<CR>
nnoremap <silent><buffer> [" :call search('\%(^\s*".*\n\)\%(^\s*"\)\@!', "bW")<CR>
xnoremap <silent><buffer> [" :<C-U>exe "normal! gv"<Bar>call search('\%(^\s*".*\n\)\%(^\s*"\)\@!', "bW")<CR>
" Purpose: Handle :import, :colorscheme and :packadd lines in a smarter way. {{{
"
" `:import` is followed by a filename or filepath. Find it.
"
" `:packadd` is followed by the name of a package, which we might have
" configured in scripts under `~/.mnv/plugin`. Find it.
"
" ---
"
" We can't handle the `:import` lines simply by setting `'includeexpr'`, because
" the option would be ignored if:
"
" - the name of the imported script is the same as the current one
" - `'path'` includes the `.` item
"
" Indeed, in that case, MNV finds the current file, and simply reloads the
" buffer.
" }}}
" We use the `F` variants, instead of the `f` ones, because they're smarter.
" See $MNVRUNTIME/autoload/mnvgoto.mnv
nnoremap <silent><buffer> gf :<C-U>call mnvgoto#Find('gF')<CR>
nnoremap <silent><buffer> <C-W>f :<C-U>call mnvgoto#Find("\<lt>C-W>F")<CR>
nnoremap <silent><buffer> <C-W>gf :<C-U>call mnvgoto#Find("\<lt>C-W>gF")<CR>
endif
" Let the matchit plugin know what items can be matched.
if exists("loaded_matchit")
let b:match_ignorecase = 0
" "func" can also be used as a type:
" var Ref: func
" or to list functions:
" func name
" require a parenthesis following, then there can be an "endfunc".
let b:match_words =
\ '\<\%(fu\%[nction]\|def\)!\=\s\+\S\+\s*(:\%(\%(^\||\)\s*\)\@<=\<retu\%[rn]\>:\%(\%(^\||\)\s*\)\@<=\<\%(endf\%[unction]\|enddef\)\>,' ..
\ '\<\%(wh\%[ile]\|for\)\>:\%(\%(^\||\)\s*\)\@<=\<brea\%[k]\>:\%(\%(^\||\)\s*\)\@<=\<con\%[tinue]\>:\%(\%(^\||\)\s*\)\@<=\<end\%(w\%[hile]\|fo\%[r]\)\>,' ..
\ '\<if\>:\%(\%(^\||\)\s*\)\@<=\<el\%[seif]\>:\%(\%(^\||\)\s*\)\@<=\<en\%[dif]\>,' ..
\ '{:},' ..
\ '\<try\>:\%(\%(^\||\)\s*\)\@<=\<cat\%[ch]\>:\%(\%(^\||\)\s*\)\@<=\<fina\%[lly]\>:\%(\%(^\||\)\s*\)\@<=\<endt\%[ry]\>,' ..
\ '\<aug\%[roup]\s\+\%(END\>\)\@!\S:\<aug\%[roup]\s\+END\>,' ..
\ '\<class\>:\<endclass\>,' ..
\ '\<interface\>:\<endinterface\>,' ..
\ '\<enum\>:\<endenum\>'
" Ignore syntax region commands and settings, any 'en*' would clobber
" if-endif.
" - set spl=de,en
" - au! FileType javascript syntax region foldBraces start=/{/ end=/}/ …
" Also ignore here-doc and dictionary keys (mnvVar).
let b:match_skip = 'synIDattr(synID(line("."), col("."), 1), "name")
\ =~? "comment\\|string\\|mnvSynReg\\|mnvSet\\|mnvLetHereDoc\\|mnvVar"'
endif
let &cpo = s:cpo_save
unlet s:cpo_save
" removed this, because 'cpoptions' is a global option.
" setlocal cpo+=M " makes \%( match \)
"
" mnv: sw=2 et
|