blob: 0b0e03447b5d3154ea754b572a6355d72d70dd43 [file] [log] [blame]
Philip Hd3ff1292024-04-21 15:44:10 +02001" Vim filetype plugin file
2" Language: Astro
3" Maintainer: Romain Lafourcade <romainlafourcade@gmail.com>
4" Last Change: 2024 Apr 21
5
6if exists("b:did_ftplugin")
7 finish
8endif
9let b:did_ftplugin = 1
10
11let s:cpo_save = &cpo
12set cpo-=C
13
14function! s:IdentifyScope(start, end) abort
15 let pos_start = searchpairpos(a:start, '', a:end, 'bnW')
16 let pos_end = searchpairpos(a:start, '', a:end, 'nW')
17
18 return pos_start != [0, 0]
19 \ && pos_end != [0, 0]
20 \ && pos_start[0] != getpos('.')[1]
21endfunction
22
23function! s:AstroComments() abort
24 if s:IdentifyScope('^---\n\s*\S', '^---\n\n')
25 \ || s:IdentifyScope('^\s*<script', '^\s*<\/script>')
26 " ECMAScript comments
27 setlocal comments=sO:*\ -,mO:*\ \ ,exO:*/,s1:/*,mb:*,ex:*/,://
28 setlocal commentstring=//%s
29
30 elseif s:IdentifyScope('^\s*<style', '^\s*<\/style>')
31 " CSS comments
32 setlocal comments=s1:/*,mb:*,ex:*/
33 setlocal commentstring=/*%s*/
34
35 else
36 " HTML comments
37 setlocal comments=s:<!--,m:\ \ \ \ ,e:-->
38 setlocal commentstring=<!--%s-->
39 endif
40endfunction
41
42" https://code.visualstudio.com/docs/languages/jsconfig
43function! s:CollectPathsFromConfig() abort
44 let config_json = findfile('tsconfig.json', '.;')
45
46 if empty(config_json)
47 let config_json = findfile('jsconfig.json', '.;')
48
49 if empty(config_json)
50 return
51 endif
52 endif
53
54 let paths_from_config = config_json
55 \ ->readfile()
56 \ ->filter({ _, val -> val =~ '^\s*[\[\]{}"0-9]' })
57 \ ->join()
58 \ ->json_decode()
59 \ ->get('compilerOptions', {})
60 \ ->get('paths', {})
61
62 if !empty(paths_from_config)
63 let b:astro_paths = paths_from_config
64 \ ->map({key, val -> [
65 \ key->glob2regpat(),
66 \ val[0]->substitute('\/\*$', '', '')
67 \ ]})
68 \ ->values()
69 endif
70
71 let b:undo_ftplugin ..= " | unlet! b:astro_paths"
72endfunction
73
74function! s:AstroInclude(filename) abort
75 let decorated_filename = a:filename
76 \ ->substitute("^", "@", "")
77
78 let found_path = b:
79 \ ->get("astro_paths", [])
80 \ ->indexof({ key, val -> decorated_filename =~ val[0]})
81
82 if found_path != -1
83 let alias = b:astro_paths[found_path][0]
84 let path = b:astro_paths[found_path][1]
85 \ ->substitute('\(\/\)*$', '/', '')
86
87 return decorated_filename
88 \ ->substitute(alias, path, '')
89 endif
90
91 return a:filename
92endfunction
93
94let b:undo_ftplugin = "setlocal"
95 \ .. " formatoptions<"
96 \ .. " path<"
97 \ .. " suffixesadd<"
98 \ .. " matchpairs<"
99 \ .. " comments<"
100 \ .. " commentstring<"
101 \ .. " iskeyword<"
102 \ .. " define<"
103 \ .. " include<"
104 \ .. " includeexpr<"
105
106" Create self-resetting autocommand group
107augroup Astro
108 autocmd! * <buffer>
109augroup END
110
111" Set 'formatoptions' to break comment lines but not other lines,
112" and insert the comment leader when hitting <CR> or using "o".
113setlocal formatoptions-=t
114setlocal formatoptions+=croql
115
116" Remove irrelevant part of 'path'.
117setlocal path-=/usr/include
118
119" Seed 'path' with default directories for :find, gf, etc.
120setlocal path+=src/**
121setlocal path+=public/**
122
123" Help Vim find extension-less filenames
124let &l:suffixesadd =
125 \ ".astro"
126 \ .. ",.js,.jsx,.es,.es6,.cjs,.mjs,.jsm"
127 \ .. ",.json"
128 \ .. ",.scss,.sass,.css"
129 \ .. ",.svelte"
130 \ .. ",.ts,.tsx,.d.ts"
131 \ .. ",.vue"
132
133" From $VIMRUNTIME/ftplugin/html.vim
134setlocal matchpairs+=<:>
135
136" Matchit configuration
137if exists("loaded_matchit")
138 let b:match_ignorecase = 0
139
140 " From $VIMRUNTIME/ftplugin/javascript.vim
141 let b:match_words =
142 \ '\<do\>:\<while\>,'
143 \ .. '<\@<=\([^ \t>/]\+\)\%(\s\+[^>]*\%([^/]>\|$\)\|>\|$\):<\@<=/\1>,'
144 \ .. '<\@<=\%([^ \t>/]\+\)\%(\s\+[^/>]*\|$\):/>'
145
146 " From $VIMRUNTIME/ftplugin/html.vim
147 let b:match_words ..=
148 \ '<!--:-->,'
149 \ .. '<:>,'
150 \ .. '<\@<=[ou]l\>[^>]*\%(>\|$\):<\@<=li\>:<\@<=/[ou]l>,'
151 \ .. '<\@<=dl\>[^>]*\%(>\|$\):<\@<=d[td]\>:<\@<=/dl>,'
152 \ .. '<\@<=\([^/!][^ \t>]*\)[^>]*\%(>\|$\):<\@<=/\1>'
153
154 let b:undo_ftplugin ..= " | unlet! b:match_ignorecase b:match_words"
155endif
156
157" Change what constitutes a word, mainly useful for CSS/SASS
158setlocal iskeyword+=-
159setlocal iskeyword+=$
160setlocal iskeyword+=%
161
162" Define paths/aliases for module resolution
163call s:CollectPathsFromConfig()
164
165" Find ESM imports
166setlocal include=^\\s*\\(import\\\|import\\s\\+[^\/]\\+from\\)\\s\\+['\"]
167
168" Process aliases if file can't be found
169setlocal includeexpr=s:AstroInclude(v:fname)
170
171" Set 'define' to a comprehensive value
172" From $VIMRUNTIME/ftplugin/javascript.vim and
173" $VIMRUNTIME/ftplugin/sass.vim
174let &l:define =
175 \ '\(^\s*(*async\s\+function\|(*function\)'
176 \ .. '\|^\s*\(\*\|static\|async\|get\|set\|\i\+\.\)'
177 \ .. '\|^\s*\(\ze\i\+\)\(([^)]*).*{$\|\s*[:=,]\)'
178
179
180" Set &comments and &commentstring according to current scope
181autocmd Astro CursorMoved <buffer> call s:AstroComments()
182
183let &cpo = s:cpo_save
184unlet s:cpo_save
185
186" vim: textwidth=78 tabstop=8 shiftwidth=4 softtabstop=4 expandtab