Skip to content

feat: support JavaScript syntax on template #150

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 23 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
4112c06
feat: support JavaScript syntax on template
vhoyer Aug 15, 2020
ba05250
bug: prevent "' from being a valid region (which is not)
vhoyer Aug 17, 2020
894351b
fix: js syntax highlight within comments shouldn't exist
vhoyer Aug 19, 2020
cc3ad7a
feat: also highlight code insied #[], @[] and :[]
vhoyer Aug 19, 2020
68acbe3
docs: add better comments to syntax file
vhoyer Aug 19, 2020
5eb491c
fix: html attributes with a colon in the middle of it were conflictin…
vhoyer Jan 9, 2021
404afa7
fix: syntax for js values only work for html
vhoyer Jan 21, 2021
921eedc
fix: register js_values_syntax after lang base
vhoyer Feb 20, 2021
ce35421
Change g:vue_pre_processors' default to 'detect_on_enter'
vhoyer Feb 20, 2021
04f2f72
Add 'Before' in vader testing to configure 'all' syntax
vhoyer Feb 27, 2021
80e8226
Change recommendation on README for reloading detect_on_enter
vhoyer Feb 27, 2021
c168b9c
Add tests for new js_value_syntax on template
vhoyer Feb 27, 2021
46a0792
Add a nice comment on 'js_value_syntax'
vhoyer Feb 27, 2021
0a26b48
Eager load html js in template syntax
vhoyer Mar 30, 2021
2b184b4
feat: make script in template be the same as the last <script> tag
vhoyer Oct 11, 2022
31a5092
refactor: remove obsolete if
vhoyer Oct 11, 2022
301a5ef
refactor: change feature name to avoid mentioning javascript specific…
vhoyer Oct 11, 2022
c9fb803
refactor: simplify two statements with one regex or
vhoyer Oct 11, 2022
d15c969
refactor: move last syntax section to keep all syntax code together
vhoyer Oct 11, 2022
4bbfbfb
test: add tests for another template script language
vhoyer Oct 11, 2022
93d1083
test: add manual testing environment
vhoyer Oct 12, 2022
a73d0ad
test: add comments to all tests on syntax_template_scripts
vhoyer Oct 12, 2022
aaeec56
refactor: automatically determine if template_script function has to …
vhoyer Oct 12, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
test: dependencies
vim -u test/vimrc -c 'Vader! test/*.vader'

manual-test: dependencies
vim -u test/vimrc_debug test

test-nvim: dependencies
VADER_OUTPUT_FILE=/dev/stderr nvim -u test/vimrc -c 'Vader! test/*.vader' --headless

manual-test-nvim: dependencies
nvim -u test/vimrc_debug test

dependencies = \
'tpope/vim-scriptease' \
'junegunn/vader.vim' \
'cakebaker/scss-syntax.vim' \
'digitaltoad/vim-pug' \
Expand Down
45 changes: 31 additions & 14 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,34 @@ and properly [configured](https://github.com/vuejs/eslint-plugin-vue#rocket-usag
npm i -g eslint eslint-plugin-vue
```

## Options

### `g:vue_pre_processors`

> default value: `'detect_on_enter'`

This options controls which preprocessors' syntax will be included when you open a new vue file. So when you are using `scss` or `typescript`, the correct syntax highlighting will be applied.

To disable pre-processor languages altogether (only highlight HTML, JavaScript, and CSS):

```vim
let g:vue_pre_processors = []
```

Available pre-processors are: `coffee`, `haml`, `handlebars`, `less`, `pug`, `sass`, `scss`, `slm`, `stylus`, `typescript`.

When `g:vue_pre_processors` is set to `'detect_on_enter'` instead of a list, vim-vue will detect the pre-processors used when a file is opened, and load only their syntax files.

```vim
let g:vue_pre_processors = 'detect_on_enter'
```

This is the default behavior. This also matches how vim natively detects syntaxes, for example, when you create a new file and start typing, you wont see the correct syntax until you save the file under a extension so that vim can detect which syntax to load. The 'detect_on_enter' is similar.

When you want vim-vue to detect a new syntax you just typed, just turn the syntax off (`:syntax off`) and on again (`:syntax on`).

Loading all syntaxes by default is not recommended because doing so slows down vim quite allot due to the multiple syntax highlighting checks that are done. Also, having multiple syntaxes for the `template` tag loaded at the same time, may result in the `js` syntax in the template (like `:value="variable"`) malfunction (see #150 for details).

## Contributing

If your language is not getting highlighted open an issue or a PR with the fix.
Expand Down Expand Up @@ -101,22 +129,11 @@ endfunction
</details>

### _Vim slows down when using this plugin_ How can I fix that?
When checking for pre-processor languages, multiple syntax highlighting checks are done, which can slow down vim. You can trim down which pre-processors to use by setting `g:vue_pre_processors` to a whitelist of languages to support:

```vim
let g:vue_pre_processors = ['pug', 'scss']
```

To disable pre-processor languages altogether (only highlight HTML, JavaScript, and CSS):

```vim
let g:vue_pre_processors = []
```

Available pre-processors are: coffee, haml, handlebars, less, pug, sass, scss, slm, stylus, typescript
> This was more of a problem when the default value of 'g:vue_pre_processors' was to load all pre-processors available, now that this is not the case, this problem shouldn't happen. That said, if you still are having problems, try setting `let g:vue_pre_processors = []`, see if it helps. Read the section on this option above for more information.

When `g:vue_pre_processors` is set to 'detect_on_enter' instead of a list, vim-vue will detect the pre-processors used when a file is opened, and load only their syntax files.
When checking for pre-processor languages, multiple syntax highlighting checks are done, which can slow down vim. You can trim down which pre-processors to use by setting `g:vue_pre_processors` to a whitelist of languages to support:

```vim
let g:vue_pre_processors = 'detect_on_enter'
let g:vue_pre_processors = ['pug', 'scss']
```
65 changes: 60 additions & 5 deletions syntax/vue.vim
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ if exists('g:vue_disable_pre_processors') && g:vue_disable_pre_processors
let g:vue_pre_processors = []
endif

" If not exist, set the default value
if !exists('g:vue_pre_processors')
let g:vue_pre_processors = 'detect_on_enter'
endif

runtime! syntax/html.vim
syntax clear htmlTagName
syntax match htmlTagName contained "\<[a-zA-Z0-9:-]*\>"
Expand Down Expand Up @@ -39,6 +44,49 @@ function! s:should_register(language, start_pattern)
return 1
endfunction

" define the cluster that will be applied inside the template tag where
" javascript is ran by vue, this is defined so it can be redefined with other
" scripting languages such as typescript if the .vue file uses a lang="ts"
syntax cluster TemplateScript contains=@jsAll

syn region vueSurroundingTag contained start=+<\(script\|style\|template\)+ end=+>+ fold contains=htmlTagN,htmlString,htmlArg,htmlValue,htmlTagError,htmlEvent
syn keyword htmlSpecialTagName contained template
syn keyword htmlArg contained scoped ts
syn match htmlArg "[@#v:a-z][-:.0-9_a-z]*\>" contained

" for mustaches quotes (`{{` and `}}`)
syn region vueTemplateScript matchgroup=htmlSpecialChar start=/{{/ keepend end=/}}/ contains=@TemplateScript containedin=ALLBUT,htmlComment

" template_script_in_* region {{{
"""""
" if you want to add script highlighting support for a specific template
" language, you should do it in this region (marked by the {{{fold marks}}})
" named "s:template_script_in_<language>", the "<language>" must match the name
" declared on the "s:languages" array (later in the file)

function! s:template_script_in_html()
" Prevent 0 length vue dynamic attributes (:id="") from overflowing from
" the area described by two quotes ("" or '') this works because syntax
" defined earlier in the file have priority.
syn match htmlString /\(\([@#:]\|v-\)[-:.0-9_a-z\[\]]*=\)\@<=\(""\|''\)/ containedin=ALLBUT,htmlComment

" Actually provide the JavaScript syntax highlighting.

" for double quotes (") and for single quotes (')
" It's necessary to have both because we can't start a region with double
" quotes and it with a single quote, and removing `keepend` would result in
" side effects.
syn region vueTemplateScript start=/\(\s\([@#:]\|v-\)\([-:.0-9_a-z]*\|\[.*\]\)=\)\@<="/ms=e+1 keepend end=/"/me=s-1 contains=@TemplateScript containedin=ALLBUT,htmlComment
syn region vueTemplateScript start=/\(\s\([@#:]\|v-\)\([-:.0-9_a-z]*\|\[.*\]\)=\)\@<='/ms=e+1 keepend end=/'/me=s-1 contains=@TemplateScript containedin=ALLBUT,htmlComment
" This one is for #[thisHere] @[thisHereToo] :[thisHereAlso]
syn region vueTemplateScript matchgroup=htmlArg start=/[@#:]\[/ keepend end=/\]/ contains=@TemplateScript containedin=ALLBUT,htmlComment
endfunction
" }}}

" Eager load template script highlighting for html because it's already being
" loaded as the base for the .vue syntax highlighting.
call s:template_script_in_html()

let s:languages = [
\ {'name': 'less', 'tag': 'style'},
\ {'name': 'pug', 'tag': 'template', 'attr_pattern': s:attr('lang', '\%(pug\|jade\)')},
Expand All @@ -65,14 +113,21 @@ for s:language in s:languages
\ 'end="</' . s:language.tag . '>"me=s-1'
\ 'contains=@' . s:language.name . ',vueSurroundingTag'
\ 'fold'

if (s:language.tag == 'script')
syntax clear @TemplateScript
execute 'syntax cluster TemplateScript contains=@'.s:language.name
endif

" if function exists, call it
if (exists('*s:template_script_in_' . s:language.name))
execute 'call s:template_script_in_' . s:language.name . '()'
endif
endif
endfor

syn region vueSurroundingTag contained start=+<\(script\|style\|template\)+ end=+>+ fold contains=htmlTagN,htmlString,htmlArg,htmlValue,htmlTagError,htmlEvent
syn keyword htmlSpecialTagName contained template
syn keyword htmlArg contained scoped ts
syn match htmlArg "[@v:][-:.0-9_a-z]*\>" contained

syntax sync fromstart

let b:current_syntax = "vue"

" vim: et tw=80 sts=2 fdm=marker
3 changes: 3 additions & 0 deletions test/test_indent.vader
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
Before:
let g:vue_pre_processors = 'all'

#
# HTML
#
Expand Down
4 changes: 3 additions & 1 deletion test/test_syntax.vader
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
Before:
let g:vue_pre_processors="all"
#
# HTML
#
Expand Down Expand Up @@ -27,7 +29,7 @@ Execute (Syntax doesn't stop at the first closing template tag):
#
# JavaScript
#
Given vue:
Given vue (Recognizes javascript syntax with bare script tag):
<script>
//
</script>
Expand Down
123 changes: 123 additions & 0 deletions test/test_syntax_template_scripts.vader
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
Before:
let g:vue_pre_processors="detect_on_enter"
#
# HTML
#
Given vue (HTML directives);
<template>
<input v-model="internalValue" />
</template>

Execute (refresh detect_on_enter):
syntax off
syntax on

Then (expect vue directives to be highlighted as htmlArg and it's value to be highlighted as script):
AssertEqual 'htmlTag', SyntaxAt(2, 10)
AssertEqual 'vueTemplateScript', SyntaxAt(2, 19)

Given vue (HTML slots);
<template>
<custom-button
#default="scopedProps"
#[dynamicSlot[2]]="variable"
/>
</template>

Execute (refresh detect_on_enter):
syntax off
syntax on

Then (expect dynamic slots and slot scopes to be script highlighted):
AssertEqual 'htmlArg', SyntaxAt(3, 5)
AssertEqual 'htmlArg', SyntaxAt(4, 5)
AssertEqual 'vueTemplateScript', SyntaxAt(3, 15)
AssertEqual 'vueTemplateScript', SyntaxAt(4, 7)
AssertEqual 'vueTemplateScript', SyntaxAt(4, 24)

Given vue (HTML events);
<template>
<custom-button
@click='events.goToCheckout(i)'
@[object.hoverEvent(true)]="event"
/>
</template>

Execute (refresh detect_on_enter):
syntax off
syntax on

Then (expect dynamic events and event callbacks to be script highlighted):
AssertEqual 'htmlArg', SyntaxAt(3, 5)
AssertEqual 'htmlArg', SyntaxAt(4, 5)
AssertEqual 'vueTemplateScript', SyntaxAt(3, 13)
AssertEqual 'vueTemplateScript', SyntaxAt(4, 7)
AssertEqual 'vueTemplateScript', SyntaxAt(4, 33)

Given vue (HTML attributes and props);
<template>
<custom-button
:color='customColor'
:[iconProp]="isLoading ? 'loading' : 'bag'"
variant="primary"
tabindex='0'
aria-label="buy our stuff"
/>
</template>

Execute (refresh detect_on_enter):
syntax off
syntax on

Then (expect dynamic bindings and binding values to be script highlighted but static props and args to be htmlString):
# dynamic and static props
AssertEqual 'htmlArg', SyntaxAt(3, 5)
AssertEqual 'htmlArg', SyntaxAt(4, 5)
AssertEqual 'htmlArg', SyntaxAt(5, 5)
AssertEqual 'vueTemplateScript', SyntaxAt(3, 13)
AssertEqual 'vueTemplateScript', SyntaxAt(4, 7)
AssertEqual 'vueTemplateScript', SyntaxAt(4, 18)
# variant tabindex
AssertEqual 'htmlArg', SyntaxAt(5, 5)
AssertEqual 'htmlArg', SyntaxAt(6, 5)
# both types of string
AssertEqual 'htmlString', SyntaxAt(3, 12)
AssertEqual 'htmlString', SyntaxAt(6, 16)
AssertEqual 'htmlString', SyntaxAt(4, 17)
AssertEqual 'htmlString', SyntaxAt(7, 16)

Given vue (HTML mustaches syntax);
<template>
<button>
Buy {{ getProduct() }}!
</button>
</template>

Execute (refresh detect_on_enter):
syntax off
syntax on

Then (expect inside of a mustache to be script highlighted):
AssertEqual 'htmlSpecialChar', SyntaxAt(3, 9)
AssertEqual 'vueTemplateScript', SyntaxAt(3, 11)

Given vue (HTML with typescript);
<template>
<button>
Buy {{ getProduct(item as string) }}!
</button>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({ name: 'Mock' })
</script>

Execute (refresh detect_on_enter):
syntax off
syntax on

Then (Expect typescript highlighting to be present in the template):
AssertEqual 'htmlSpecialChar', SyntaxAt(3, 9)
AssertEqual 'typescriptImport', SyntaxAt(7, 1)
AssertEqual 'vueTemplateScript', SyntaxAt(3, 11)
AssertEqual 'typescriptPredefinedType', SyntaxAt(3, 31)
4 changes: 4 additions & 0 deletions test/vimrc_debug
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
source test/vimrc

" Allow inspection of syntax
map ZI <Plug>ScripteaseSynnames