summaryrefslogtreecommitdiff
path: root/mnv/runtime/ftplugin/mnv.mnv
blob: 9daf7a20ab0ad7895c11c94cb7a3712427e55887 (plain)
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