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