summaryrefslogtreecommitdiffstats
path: root/runtime/ftplugin/astro.vim
blob: 0b0e03447b5d3154ea754b572a6355d72d70dd43 (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
" Vim filetype plugin file
" Language:     Astro
" Maintainer:   Romain Lafourcade <romainlafourcade@gmail.com>
" Last Change:  2024 Apr 21

if exists("b:did_ftplugin")
    finish
endif
let b:did_ftplugin = 1

let s:cpo_save = &cpo
set cpo-=C

function! s:IdentifyScope(start, end) abort
    let pos_start = searchpairpos(a:start, '', a:end, 'bnW')
    let pos_end = searchpairpos(a:start, '', a:end, 'nW')

    return pos_start != [0, 0]
                \ && pos_end != [0, 0]
                \ && pos_start[0] != getpos('.')[1]
endfunction

function! s:AstroComments() abort
    if s:IdentifyScope('^---\n\s*\S', '^---\n\n')
                \ || s:IdentifyScope('^\s*<script', '^\s*<\/script>')
        " ECMAScript comments
        setlocal comments=sO:*\ -,mO:*\ \ ,exO:*/,s1:/*,mb:*,ex:*/,://
        setlocal commentstring=//%s

    elseif s:IdentifyScope('^\s*<style', '^\s*<\/style>')
        " CSS comments
        setlocal comments=s1:/*,mb:*,ex:*/
        setlocal commentstring=/*%s*/

    else
        " HTML comments
        setlocal comments=s:<!--,m:\ \ \ \ ,e:-->
        setlocal commentstring=<!--%s-->
    endif
endfunction

" https://code.visualstudio.com/docs/languages/jsconfig
function! s:CollectPathsFromConfig() abort
    let config_json = findfile('tsconfig.json', '.;')

    if empty(config_json)
        let config_json = findfile('jsconfig.json', '.;')

        if empty(config_json)
            return
        endif
    endif

    let paths_from_config = config_json
                \ ->readfile()
                \ ->filter({ _, val -> val =~ '^\s*[\[\]{}"0-9]' })
                \ ->join()
                \ ->json_decode()
                \ ->get('compilerOptions', {})
                \ ->get('paths', {})

    if !empty(paths_from_config)
        let b:astro_paths = paths_from_config
                    \ ->map({key, val -> [
                    \     key->glob2regpat(),
                    \     val[0]->substitute('\/\*$', '', '')
                    \   ]})
                    \ ->values()
    endif

    let b:undo_ftplugin ..= " | unlet! b:astro_paths"
endfunction

function! s:AstroInclude(filename) abort
    let decorated_filename = a:filename
                \ ->substitute("^", "@", "")

    let found_path = b:
                \ ->get("astro_paths", [])
                \ ->indexof({ key, val -> decorated_filename =~ val[0]})

    if found_path != -1
        let alias = b:astro_paths[found_path][0]
        let path  = b:astro_paths[found_path][1]
                    \ ->substitute('\(\/\)*$', '/', '')

        return decorated_filename
                    \ ->substitute(alias, path, '')
    endif

    return a:filename
endfunction

let b:undo_ftplugin = "setlocal"
            \ .. " formatoptions<"
            \ .. " path<"
            \ .. " suffixesadd<"
            \ .. " matchpairs<"
            \ .. " comments<"
            \ .. " commentstring<"
            \ .. " iskeyword<"
            \ .. " define<"
            \ .. " include<"
            \ .. " includeexpr<"

" Create self-resetting autocommand group
augroup Astro
    autocmd! * <buffer>
augroup END

" Set 'formatoptions' to break comment lines but not other lines,
" and insert the comment leader when hitting <CR> or using "o".
setlocal formatoptions-=t
setlocal formatoptions+=croql

" Remove irrelevant part of 'path'.
setlocal path-=/usr/include

" Seed 'path' with default directories for :find, gf, etc.
setlocal path+=src/**
setlocal path+=public/**

" Help Vim find extension-less filenames
let &l:suffixesadd =
            \ ".astro"
            \ .. ",.js,.jsx,.es,.es6,.cjs,.mjs,.jsm"
            \ .. ",.json"
            \ .. ",.scss,.sass,.css"
            \ .. ",.svelte"
            \ .. ",.ts,.tsx,.d.ts"
            \ .. ",.vue"

" From $VIMRUNTIME/ftplugin/html.vim
setlocal matchpairs+=<:>

" Matchit configuration
if exists("loaded_matchit")
    let b:match_ignorecase = 0

    " From $VIMRUNTIME/ftplugin/javascript.vim
    let b:match_words =
                \ '\<do\>:\<while\>,'
                \ .. '<\@<=\([^ \t>/]\+\)\%(\s\+[^>]*\%([^/]>\|$\)\|>\|$\):<\@<=/\1>,'
                \ .. '<\@<=\%([^ \t>/]\+\)\%(\s\+[^/>]*\|$\):/>'

    " From $VIMRUNTIME/ftplugin/html.vim
    let b:match_words ..=
                \ '<!--:-->,'
                \ .. '<:>,'
                \ .. '<\@<=[ou]l\>[^>]*\%(>\|$\):<\@<=li\>:<\@<=/[ou]l>,'
                \ .. '<\@<=dl\>[^>]*\%(>\|$\):<\@<=d[td]\>:<\@<=/dl>,'
                \ .. '<\@<=\([^/!][^ \t>]*\)[^>]*\%(>\|$\):<\@<=/\1>'

    let b:undo_ftplugin ..= " | unlet! b:match_ignorecase b:match_words"
endif

" Change what constitutes a word, mainly useful for CSS/SASS
setlocal iskeyword+=-
setlocal iskeyword+=$
setlocal iskeyword+=%

" Define paths/aliases for module resolution
call s:CollectPathsFromConfig()

" Find ESM imports
setlocal include=^\\s*\\(import\\\|import\\s\\+[^\/]\\+from\\)\\s\\+['\"]

" Process aliases if file can't be found
setlocal includeexpr=s:AstroInclude(v:fname)

" Set 'define' to a comprehensive value
" From $VIMRUNTIME/ftplugin/javascript.vim and
" $VIMRUNTIME/ftplugin/sass.vim
let &l:define =
            \ '\(^\s*(*async\s\+function\|(*function\)'
            \ .. '\|^\s*\(\*\|static\|async\|get\|set\|\i\+\.\)'
            \ .. '\|^\s*\(\ze\i\+\)\(([^)]*).*{$\|\s*[:=,]\)'


" Set &comments and &commentstring according to current scope
autocmd Astro CursorMoved <buffer> call s:AstroComments()

let &cpo = s:cpo_save
unlet s:cpo_save

" vim: textwidth=78 tabstop=8 shiftwidth=4 softtabstop=4 expandtab