mirror of
https://github.com/leafOfTree/vim-vue-plugin.git
synced 2026-11-04 19:32:53 +08:00
Add tests and GitHub Actions
Replace the Travis tests, which used a separate repo and no longer ran, with tests in this repo. Each case in test/cases is indented as a whole and should be unchanged, with optional syntax, fold and tag checks. The cases are from the previous tests, plus script-setup.vue for <script setup> keywords and a sync check in oneline.vim. test/run.sh runs the cases with Vim in a pseudo terminal, as syntax in Ex mode isn't the same, and Neovim headless.
This commit is contained in:
27 files changed
+974
-19
No files matched your search
@@ -0,0 +1,15 @@
|
|||||||
|
name: test
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install Vim and Neovim
|
||||||
|
run: sudo apt-get update && sudo apt-get install -y vim neovim
|
||||||
|
- name: Run tests
|
||||||
|
run: ./test/run.sh
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
README.html
|
README.html
|
||||||
test.vue
|
test.vue
|
||||||
.DS_STORE
|
.DS_STORE
|
||||||
|
test/deps/
|
||||||
-19
@@ -1,19 +0,0 @@
|
|||||||
dist: focal
|
|
||||||
|
|
||||||
language: minimal
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
- sudo add-apt-repository ppa:neovim-ppa/stable -y
|
|
||||||
- sudo apt-get update
|
|
||||||
- sudo apt-get -y install neovim
|
|
||||||
|
|
||||||
install:
|
|
||||||
- git clone https://github.com/leafOfTree/test ../test --depth=1
|
|
||||||
|
|
||||||
- git clone https://github.com/digitaltoad/vim-pug ../vim-pug --depth=1
|
|
||||||
- git clone https://github.com/kchmck/vim-coffee-script ../vim-coffee-script --depth=1
|
|
||||||
- git clone https://github.com/leafgarland/typescript-vim ../typescript-vim --depth=1
|
|
||||||
- git clone https://github.com/iloginow/vim-stylus.git ../vim-stylus --depth=1
|
|
||||||
|
|
||||||
script:
|
|
||||||
- bash ../test/start.sh vue vim-vue-plugin
|
|
||||||
@@ -208,6 +208,17 @@ For example, the built-in syntax `sass.vim` and `less.vim` in vim8.1 runtime and
|
|||||||
+ endif
|
+ endif
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
Run tests with Vim and Neovim, whichever is installed
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./test/run.sh
|
||||||
|
./test/run.sh basic template # Only these cases
|
||||||
|
```
|
||||||
|
|
||||||
|
Each case is a `.vue` file in [test/cases](test/cases), with an optional `.vim` file for the config and syntax checks. A case fails if indenting the whole file changes it, a check fails, or there is an error message. Tests also run on GitHub Actions for each push and pull request.
|
||||||
|
|
||||||
## Acknowledgments & Refs
|
## Acknowledgments & Refs
|
||||||
|
|
||||||
- [mxw/vim-jsx][1]
|
- [mxw/vim-jsx][1]
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
let g:vim_vue_plugin_config = {
|
||||||
|
\'syntax': {
|
||||||
|
\ 'script': ['javascript'],
|
||||||
|
\ 'template': ['html'],
|
||||||
|
\ 'style': ['css'],
|
||||||
|
\},
|
||||||
|
\}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<template>
|
||||||
|
<p>{{ greeting }}</p>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
greeting: 'Hello'
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
p {
|
||||||
|
font-size: 2em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
" Uses syntax plugins in test/deps
|
||||||
|
let g:vim_vue_plugin_config = {
|
||||||
|
\'syntax': {
|
||||||
|
\ 'template': ['html', 'pug'],
|
||||||
|
\ 'script': ['javascript', 'typescript', 'coffee'],
|
||||||
|
\ 'style': ['css', 'scss', 'sass', 'less', 'stylus'],
|
||||||
|
\ 'docs': 'markdown',
|
||||||
|
\ 'i18n': ['json', 'yaml'],
|
||||||
|
\ 'route': 'json',
|
||||||
|
\},
|
||||||
|
\'attribute': 1,
|
||||||
|
\'keyword': 1,
|
||||||
|
\'foldexpr': 1,
|
||||||
|
\'full_syntax': ['scss', 'less'],
|
||||||
|
\}
|
||||||
@@ -0,0 +1,160 @@
|
|||||||
|
<template lang="pug">
|
||||||
|
div.set
|
||||||
|
span(id="123")
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<template></template>
|
||||||
|
<div></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<p>{{ greeting }}</p>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
greeting: 'Hello'
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue'
|
||||||
|
import { User } from '@/types'
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
name: 'Home' as string,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
user: {} as User
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
fullName(): string { // Computed Property returns a string
|
||||||
|
return `${this.user.firstName} ${this.user.lastName}`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted(): void { // The mounted hook returns nothing
|
||||||
|
...
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="coffee">
|
||||||
|
# Assignment:
|
||||||
|
number = 42
|
||||||
|
opposite = true
|
||||||
|
|
||||||
|
# Conditions:
|
||||||
|
number = -42 if opposite
|
||||||
|
|
||||||
|
# Functions:
|
||||||
|
square = (x) -> x * x
|
||||||
|
|
||||||
|
# Arrays:
|
||||||
|
list = [1, 2, 3, 4, 5]
|
||||||
|
|
||||||
|
`
|
||||||
|
function hello() {}
|
||||||
|
console.log
|
||||||
|
a = 123;
|
||||||
|
`
|
||||||
|
# Objects:
|
||||||
|
math =
|
||||||
|
root: Math.sqrt
|
||||||
|
square: square
|
||||||
|
cube: (x) -> x * square x
|
||||||
|
|
||||||
|
# Splats:
|
||||||
|
race = (winner, runners...) ->
|
||||||
|
print winner, runners
|
||||||
|
|
||||||
|
# Existence:
|
||||||
|
alert "I knew it!" if elvis?
|
||||||
|
|
||||||
|
# Array comprehensions:
|
||||||
|
cubes = (math.cube num for num in list)
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
p {
|
||||||
|
font-size: 2em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
$a: 100px;
|
||||||
|
|
||||||
|
div {
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style lang="sass">
|
||||||
|
$w: 128px;
|
||||||
|
|
||||||
|
.bg
|
||||||
|
$w: 128px;
|
||||||
|
background: white;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
canvas
|
||||||
|
width: 100vw;
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
@list: 10 12;
|
||||||
|
canvas {
|
||||||
|
background: white;
|
||||||
|
width: 100vh;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style lang="stylus">
|
||||||
|
@import 'vendor'
|
||||||
|
|
||||||
|
body
|
||||||
|
font: 12px Helvetica, Arial, sans-serif
|
||||||
|
|
||||||
|
a.button
|
||||||
|
border-radius: 5px
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<route>
|
||||||
|
{
|
||||||
|
"name": "home",
|
||||||
|
"meta": {
|
||||||
|
"requiresAuth": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</route>
|
||||||
|
|
||||||
|
<docs>
|
||||||
|
# This is the documentation for component.
|
||||||
|
</docs>
|
||||||
|
|
||||||
|
<i18n lang="yaml">
|
||||||
|
en:
|
||||||
|
hello: "Hello World!"
|
||||||
|
ja:
|
||||||
|
hello: "こんにちは、世界!"
|
||||||
|
</i18n>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
{
|
||||||
|
"en": {
|
||||||
|
"language": "Language",
|
||||||
|
"hello": "hello, world!"
|
||||||
|
},
|
||||||
|
"ja": {
|
||||||
|
"language": "言語",
|
||||||
|
"hello": "こんにちは、世界!"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</i18n>
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
let g:vim_vue_plugin_config = {
|
||||||
|
\'syntax': {
|
||||||
|
\ 'script': ['javascript'],
|
||||||
|
\ 'template': ['html'],
|
||||||
|
\ 'style': ['css'],
|
||||||
|
\},
|
||||||
|
\'foldexpr': 1,
|
||||||
|
\}
|
||||||
|
|
||||||
|
function! Check()
|
||||||
|
let levels = [1, 2, 1, 1, 1, 2, 3, 4, 4, 3, 3, 1, 1, 1, 1, 1, 2, 2, 1, 1]
|
||||||
|
for lnum in range(1, len(levels))
|
||||||
|
call ExpectFoldlevel(lnum, levels[lnum - 1])
|
||||||
|
endfor
|
||||||
|
endfunction
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<template>
|
||||||
|
<p>{{ greeting }}</p>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
greeting: 'Hello'
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
p {
|
||||||
|
font-size: 2em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
let g:vim_vue_plugin_config = {
|
||||||
|
\'syntax': {
|
||||||
|
\ 'template': ['html'],
|
||||||
|
\ 'script': ['javascript'],
|
||||||
|
\ 'style': ['css', 'sass', 'scss'],
|
||||||
|
\ 'codepen-additional': ['javascript', 'json'],
|
||||||
|
\},
|
||||||
|
\'initial_indent': ['script', 'style', 'codepen-additional'],
|
||||||
|
\}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<template>
|
||||||
|
<p>{{ greeting }}</p>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
greeting: 'Hello'
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
p {
|
||||||
|
font-size: 2em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<codepen-additional>
|
||||||
|
const easings = {
|
||||||
|
linear: '',
|
||||||
|
easeInQuad: '',
|
||||||
|
}
|
||||||
|
</codepen-additional>
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
let g:vim_vue_plugin_config = {
|
||||||
|
\'syntax': {
|
||||||
|
\ 'template': ['html'],
|
||||||
|
\ 'script': ['javascript', 'typescript'],
|
||||||
|
\},
|
||||||
|
\'keyword': 1,
|
||||||
|
\'initial_indent': ['script', 'style', 'codepen-additional'],
|
||||||
|
\}
|
||||||
|
|
||||||
|
function! Check()
|
||||||
|
call ExpectSyntax('name', 'vueObjectKey')
|
||||||
|
call ExpectSyntax('data', 'vueObjectFuncKey')
|
||||||
|
call ExpectSyntax('data', 'vueObjectFuncName')
|
||||||
|
call ExpectSyntax('computed', 'vueObjectKey')
|
||||||
|
call ExpectSyntax('mounted', 'vueObjectFuncName')
|
||||||
|
endfunction
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'Home' as string,
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
greeting: 'Hello'
|
||||||
|
};
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
greeting: 'Hello'
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
fullName() { // Computed Property returns a string
|
||||||
|
return `${this.user.firstName} ${this.user.lastName}`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() { // The mounted hook returns nothing
|
||||||
|
...
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue'
|
||||||
|
import { User } from '@/types'
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
name: 'Home' as string,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
user: {} as User
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
fullName(): string { // Computed Property returns a string
|
||||||
|
return `${this.user.firstName} ${this.user.lastName}`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted(): void { // The mounted hook returns nothing
|
||||||
|
...
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
" One-line blocks like <style>p { color: red; }</style>
|
||||||
|
|
||||||
|
function! Check()
|
||||||
|
call ExpectSyntax('script', 'javascriptScriptBlock')
|
||||||
|
call ExpectSyntax('p {', 'cssStyleBlock')
|
||||||
|
call ExpectSyntax('color', 'cssTextProp')
|
||||||
|
call ExpectSyntax('p {', 'cssStyleBlock')
|
||||||
|
call ExpectSyntax('color', 'cssTextProp')
|
||||||
|
call ExpectSyntax('Hello', 'htmlTemplateBlock')
|
||||||
|
call CheckSync()
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" Syntax from a sync point, as after jumping to a line not displayed yet
|
||||||
|
function! CheckSync()
|
||||||
|
silent edit!
|
||||||
|
call cursor(line('$'), 1)
|
||||||
|
call search('Hello', 'cW')
|
||||||
|
if index(SyntaxList(), 'htmlTemplateBlock') == -1
|
||||||
|
call Fail('after syncing, Hello should be htmlTemplateBlock, but got '
|
||||||
|
\.string(SyntaxList()))
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<script src="./tmp"> </script>
|
||||||
|
|
||||||
|
<style lang="css">
|
||||||
|
p {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
<style>p { color: red; }</style>
|
||||||
|
|
||||||
|
<template><div>Hello</div></template>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
let g:vim_vue_plugin_config = {
|
||||||
|
\'syntax': {
|
||||||
|
\ 'template': ['html'],
|
||||||
|
\ 'script': ['javascript'],
|
||||||
|
\ 'style': [],
|
||||||
|
\},
|
||||||
|
\'attribute': 1,
|
||||||
|
\'keyword': 1,
|
||||||
|
\'foldexpr': 1,
|
||||||
|
\}
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
<template>
|
||||||
|
<div class="editor-page">
|
||||||
|
<div class="container page">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-10 offset-md-1 col-xs-12">
|
||||||
|
<RwvListErrors :errors="errors" />
|
||||||
|
<form @submit.prevent="onPublish(article.slug)">
|
||||||
|
<fieldset :disabled="inProgress">
|
||||||
|
<fieldset class="form-group">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control form-control-lg"
|
||||||
|
v-model="article.title"
|
||||||
|
placeholder="Article Title"
|
||||||
|
/>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset class="form-group">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
v-model="article.description"
|
||||||
|
placeholder="What's this article about?"
|
||||||
|
/>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset class="form-group">
|
||||||
|
<textarea
|
||||||
|
class="form-control"
|
||||||
|
rows="8"
|
||||||
|
v-model="article.body"
|
||||||
|
placeholder="Write your article (in markdown)"
|
||||||
|
>
|
||||||
|
</textarea>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset class="form-group">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
placeholder="Enter tags"
|
||||||
|
v-model="tagInput"
|
||||||
|
@keypress.enter.prevent="addTag(tagInput)"
|
||||||
|
/>
|
||||||
|
<div class="tag-list">
|
||||||
|
<span
|
||||||
|
class="tag-default tag-pill"
|
||||||
|
v-for="(tag, index) of article.tagList"
|
||||||
|
:key="tag + index"
|
||||||
|
>
|
||||||
|
<i class="ion-close-round" @click="removeTag(tag)"> </i>
|
||||||
|
{{ tag }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</fieldset>
|
||||||
|
<button
|
||||||
|
:disabled="inProgress"
|
||||||
|
class="btn btn-lg pull-xs-right btn-primary"
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
Publish Article
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapGetters } from "vuex";
|
||||||
|
import store from "@/store";
|
||||||
|
import RwvListErrors from "@/components/ListErrors";
|
||||||
|
import {
|
||||||
|
ARTICLE_PUBLISH,
|
||||||
|
ARTICLE_EDIT,
|
||||||
|
FETCH_ARTICLE,
|
||||||
|
ARTICLE_EDIT_ADD_TAG,
|
||||||
|
ARTICLE_EDIT_REMOVE_TAG,
|
||||||
|
ARTICLE_RESET_STATE
|
||||||
|
} from "@/store/actions.type";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "RwvArticleEdit",
|
||||||
|
components: { RwvListErrors },
|
||||||
|
props: {
|
||||||
|
previousArticle: {
|
||||||
|
type: Object,
|
||||||
|
required: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async beforeRouteUpdate(to, from, next) {
|
||||||
|
// Reset state if user goes from /editor/:id to /editor
|
||||||
|
// The component is not recreated so we use to hook to reset the state.
|
||||||
|
await store.dispatch(ARTICLE_RESET_STATE);
|
||||||
|
return next();
|
||||||
|
},
|
||||||
|
async beforeRouteEnter(to, from, next) {
|
||||||
|
// SO: https://github.com/vuejs/vue-router/issues/1034
|
||||||
|
// If we arrive directly to this url, we need to fetch the article
|
||||||
|
await store.dispatch(ARTICLE_RESET_STATE);
|
||||||
|
if (to.params.slug !== undefined) {
|
||||||
|
await store.dispatch(
|
||||||
|
FETCH_ARTICLE,
|
||||||
|
to.params.slug,
|
||||||
|
to.params.previousArticle
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return next();
|
||||||
|
},
|
||||||
|
async beforeRouteLeave(to, from, next) {
|
||||||
|
await store.dispatch(ARTICLE_RESET_STATE);
|
||||||
|
next();
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tagInput: null,
|
||||||
|
inProgress: false,
|
||||||
|
errors: {}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters(["article"])
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onPublish(slug) {
|
||||||
|
let action = slug ? ARTICLE_EDIT : ARTICLE_PUBLISH;
|
||||||
|
this.inProgress = true;
|
||||||
|
this.$store
|
||||||
|
.dispatch(action)
|
||||||
|
.then(({ data }) => {
|
||||||
|
this.inProgress = false;
|
||||||
|
this.$router.push({
|
||||||
|
name: "article",
|
||||||
|
params: { slug: data.article.slug }
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(({ response }) => {
|
||||||
|
this.inProgress = false;
|
||||||
|
this.errors = response.data.errors;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
removeTag(tag) {
|
||||||
|
this.$store.dispatch(ARTICLE_EDIT_REMOVE_TAG, tag);
|
||||||
|
},
|
||||||
|
addTag(tag) {
|
||||||
|
this.$store.dispatch(ARTICLE_EDIT_ADD_TAG, tag);
|
||||||
|
this.tagInput = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
let g:vim_vue_plugin_config = {
|
||||||
|
\'syntax': {
|
||||||
|
\ 'template': ['html'],
|
||||||
|
\ 'script': ['javascript', 'typescript'],
|
||||||
|
\},
|
||||||
|
\'keyword': 1,
|
||||||
|
\}
|
||||||
|
|
||||||
|
function! Check()
|
||||||
|
call ExpectSyntax('withDefaults', 'vue3Keyword')
|
||||||
|
call ExpectSyntax('defineProps<', 'vue3Keyword')
|
||||||
|
call ExpectSyntax('defineEmits<', 'vue3Keyword')
|
||||||
|
call ExpectSyntax('defineModel<', 'vue3Keyword')
|
||||||
|
call ExpectSyntax('useTemplateRef', 'vue3Keyword')
|
||||||
|
call ExpectSyntax('ref<number>', 'vue3Keyword')
|
||||||
|
call ExpectNoSyntax('value < 3', 'vue3Keyword')
|
||||||
|
call ExpectSyntax('defineExpose', 'vue3Keyword')
|
||||||
|
endfunction
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<template>
|
||||||
|
<div ref="el">{{ props.foo }}</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
const props = withDefaults(defineProps<{ foo?: string }>(), {
|
||||||
|
foo: 'a',
|
||||||
|
})
|
||||||
|
const emit = defineEmits<{ (e: 'change', id: number): void }>()
|
||||||
|
const model = defineModel<string>()
|
||||||
|
const el = useTemplateRef('el')
|
||||||
|
const count = ref<number>(1)
|
||||||
|
const small = count.value < 3
|
||||||
|
defineExpose({ el })
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
" Nested <template> and <docs> in <template>
|
||||||
|
let g:vim_vue_plugin_config = {
|
||||||
|
\'syntax': {
|
||||||
|
\ 'script': ['javascript'],
|
||||||
|
\ 'template': ['html'],
|
||||||
|
\ 'style': ['css'],
|
||||||
|
\ 'docs': 'markdown',
|
||||||
|
\},
|
||||||
|
\}
|
||||||
|
|
||||||
|
function! Check()
|
||||||
|
while search('/template', 'W')
|
||||||
|
call ExpectTag(line('.'), 'template')
|
||||||
|
endwhile
|
||||||
|
normal! gg
|
||||||
|
call ExpectNoSyntax('/docs', 'markdownDocsBlock')
|
||||||
|
call ExpectSyntax('/docs', 'markdownDocsBlock')
|
||||||
|
endfunction
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<template>
|
||||||
|
<template>
|
||||||
|
<div></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div></div>
|
||||||
|
<template>
|
||||||
|
<div></div>
|
||||||
|
</template>
|
||||||
|
<div></div>
|
||||||
|
<docs></docs>
|
||||||
|
<div></div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<template></template>
|
||||||
|
<div></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<docs>
|
||||||
|
# Hello
|
||||||
|
</docs>
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
let g:vim_vue_plugin_config = {
|
||||||
|
\'syntax': {
|
||||||
|
\ 'template': ['html'],
|
||||||
|
\ 'script': ['javascript', 'typescript'],
|
||||||
|
\ 'style': ['css'],
|
||||||
|
\},
|
||||||
|
\'keyword': 1,
|
||||||
|
\}
|
||||||
|
|
||||||
|
function! Check()
|
||||||
|
call ExpectSyntax('v-bind', 'cssFunctionName')
|
||||||
|
call ExpectSyntax('slotted', 'cssPseudoClassId')
|
||||||
|
call ExpectSyntax('global', 'cssPseudoClassId')
|
||||||
|
call ExpectSyntax('deep', 'cssPseudoClassId')
|
||||||
|
endfunction
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<style scoped>
|
||||||
|
button {
|
||||||
|
color: v-bind(color);
|
||||||
|
}
|
||||||
|
.a :slotted(.b) :global(.c) :deep(.d) {
|
||||||
|
/* ... */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Executable
+47
@@ -0,0 +1,47 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Run all test cases, or the ones given as arguments, with vim and nvim if
|
||||||
|
# they are installed. Vim runs in a pseudo terminal by `script`, as syntax
|
||||||
|
# in Ex mode (-es) isn't the same as in a terminal.
|
||||||
|
# ./test/run.sh
|
||||||
|
# ./test/run.sh basic template
|
||||||
|
cd "$(dirname "$0")/.." || exit 1
|
||||||
|
|
||||||
|
# Syntax plugins used by test/cases/example.vue
|
||||||
|
mkdir -p test/deps
|
||||||
|
for repo in digitaltoad/vim-pug kchmck/vim-coffee-script \
|
||||||
|
leafgarland/typescript-vim iloginow/vim-stylus; do
|
||||||
|
dir=test/deps/${repo#*/}
|
||||||
|
[ -d "$dir" ] || git clone -q --depth 1 "https://github.com/$repo" "$dir"
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ $# -gt 0 ]; then
|
||||||
|
cases=$*
|
||||||
|
else
|
||||||
|
cases=$(ls test/cases/*.vue | sed 's#.*/##; s#\.vue$##')
|
||||||
|
fi
|
||||||
|
|
||||||
|
out=$(mktemp)
|
||||||
|
status=0
|
||||||
|
expected=0
|
||||||
|
for name in $cases; do
|
||||||
|
if command -v vim >/dev/null; then
|
||||||
|
expected=$((expected + 1))
|
||||||
|
VUE_TEST_CASE=$name VUE_TEST_OUT=$out timeout 60 script -qec \
|
||||||
|
'stty rows 40 cols 120; vim -N -u NONE -i NONE -S test/run.vim' \
|
||||||
|
/dev/null >/dev/null || status=1
|
||||||
|
fi
|
||||||
|
if command -v nvim >/dev/null; then
|
||||||
|
expected=$((expected + 1))
|
||||||
|
VUE_TEST_CASE=$name VUE_TEST_OUT=$out timeout 60 \
|
||||||
|
nvim --headless -u NONE -i NONE -S test/run.vim || status=1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
cat "$out"
|
||||||
|
passed=$(grep -c '^ok' "$out")
|
||||||
|
failed=$(grep -c '^FAIL' "$out")
|
||||||
|
missing=$((expected - passed - failed))
|
||||||
|
echo "$passed passed, $failed failed, $missing without result"
|
||||||
|
[ $failed -eq 0 ] && [ $missing -eq 0 ] || status=1
|
||||||
|
rm -f "$out"
|
||||||
|
exit $status
|
||||||
+117
@@ -0,0 +1,117 @@
|
|||||||
|
" Run one test case, see test/run.sh
|
||||||
|
" VUE_TEST_CASE=basic vim -N -u NONE -i NONE -S test/run.vim
|
||||||
|
"
|
||||||
|
" A case is test/cases/<name>.vue, with optional test/cases/<name>.vim to set
|
||||||
|
" g:vim_vue_plugin_config and define Check() for more assertions. It fails if
|
||||||
|
" - indenting the whole file changes it
|
||||||
|
" - Check() calls Fail() or an Expect*() function fails
|
||||||
|
" - there are error messages
|
||||||
|
"
|
||||||
|
" Results are appended to $VUE_TEST_OUT. Exit code is 1 if the case fails.
|
||||||
|
|
||||||
|
set nocompatible
|
||||||
|
let s:root = expand('<sfile>:p:h:h')
|
||||||
|
let s:name = $VUE_TEST_CASE
|
||||||
|
let s:case = s:root.'/test/cases/'.s:name
|
||||||
|
let s:errors = []
|
||||||
|
|
||||||
|
" Plugin first, before Vim's placeholder vue syntax and indent files
|
||||||
|
let &runtimepath = join([s:root, s:root.'/test/runtime']
|
||||||
|
\ + glob(s:root.'/test/deps/*', 0, 1)
|
||||||
|
\ + [&runtimepath], ',')
|
||||||
|
set expandtab shiftwidth=2
|
||||||
|
" Plugin scripts aren't loaded with -u NONE, e.g. vim-stylus sets g:css_units
|
||||||
|
runtime! plugin/**/*.vim
|
||||||
|
|
||||||
|
" Helpers for Check()
|
||||||
|
function! Fail(msg)
|
||||||
|
call add(s:errors, a:msg)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! SyntaxList()
|
||||||
|
return map(synstack(line('.'), col('.')), { _, id -> synIDattr(id, 'name') })
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" Search pattern forward from cursor and check syntax at the match
|
||||||
|
function! ExpectSyntax(pattern, syntax)
|
||||||
|
if !search('\V'.a:pattern, 'W')
|
||||||
|
return Fail('not found: '.a:pattern)
|
||||||
|
endif
|
||||||
|
if index(SyntaxList(), a:syntax) == -1
|
||||||
|
call Fail(printf('%s at line %d should be %s, but got %s',
|
||||||
|
\ a:pattern, line('.'), a:syntax, string(SyntaxList())))
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! ExpectNoSyntax(pattern, syntax)
|
||||||
|
if !search('\V'.a:pattern, 'W')
|
||||||
|
return Fail('not found: '.a:pattern)
|
||||||
|
endif
|
||||||
|
if index(SyntaxList(), a:syntax) != -1
|
||||||
|
call Fail(printf('%s at line %d should not be %s, but got %s',
|
||||||
|
\ a:pattern, line('.'), a:syntax, string(SyntaxList())))
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! ExpectFoldlevel(lnum, level)
|
||||||
|
if foldlevel(a:lnum) != a:level
|
||||||
|
call Fail(printf('foldlevel at line %d should be %d, but got %d',
|
||||||
|
\ a:lnum, a:level, foldlevel(a:lnum)))
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! ExpectTag(lnum, tag)
|
||||||
|
if GetVueTag(a:lnum) !=# a:tag
|
||||||
|
call Fail(printf('tag at line %d should be %s, but got %s',
|
||||||
|
\ a:lnum, string(a:tag), string(GetVueTag(a:lnum))))
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
if filereadable(s:case.'.vim')
|
||||||
|
execute 'source '.fnameescape(s:case.'.vim')
|
||||||
|
endif
|
||||||
|
filetype plugin indent on
|
||||||
|
syntax on
|
||||||
|
|
||||||
|
messages clear
|
||||||
|
execute 'silent edit '.fnameescape(s:case.'.vue')
|
||||||
|
redraw
|
||||||
|
|
||||||
|
" Indent
|
||||||
|
let s:lines = getline(1, '$')
|
||||||
|
silent normal! gg=G
|
||||||
|
let s:indented = getline(1, '$')
|
||||||
|
if s:indented != s:lines
|
||||||
|
let s:lnum = 0
|
||||||
|
while s:lnum < len(s:lines) && get(s:indented, s:lnum) ==# s:lines[s:lnum]
|
||||||
|
let s:lnum += 1
|
||||||
|
endwhile
|
||||||
|
call Fail(printf('indent changed at line %d: expected %s, but got %s',
|
||||||
|
\ s:lnum + 1, string(get(s:lines, s:lnum, '')),
|
||||||
|
\ string(get(s:indented, s:lnum, ''))))
|
||||||
|
silent undo
|
||||||
|
endif
|
||||||
|
|
||||||
|
" Assertions
|
||||||
|
normal! gg
|
||||||
|
if exists('*Check')
|
||||||
|
call Check()
|
||||||
|
endif
|
||||||
|
|
||||||
|
" Errors
|
||||||
|
for s:msg in split(execute('messages'), "\n")
|
||||||
|
if s:msg =~# '^E\d\+:\|^Error detected'
|
||||||
|
call Fail('message: '.s:msg)
|
||||||
|
endif
|
||||||
|
endfor
|
||||||
|
|
||||||
|
let s:editor = has('nvim') ? 'nvim' : 'vim'
|
||||||
|
call writefile(empty(s:errors)
|
||||||
|
\ ? [printf('ok %s %s', s:editor, s:name)]
|
||||||
|
\ : [printf('FAIL %s %s', s:editor, s:name)]
|
||||||
|
\ + map(copy(s:errors), '" ".v:val'),
|
||||||
|
\ $VUE_TEST_OUT, 'a')
|
||||||
|
if !empty(s:errors)
|
||||||
|
cquit
|
||||||
|
endif
|
||||||
|
qall!
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
" Vim indent file
|
||||||
|
" Language: YAML
|
||||||
|
" Maintainer: Nikolai Pavlov <zyx.vim@gmail.com>
|
||||||
|
" Last Update: Lukas Reineke
|
||||||
|
" Last Change: 2020 Jun 07
|
||||||
|
|
||||||
|
" Only load this indent file when no other was loaded.
|
||||||
|
if exists('b:did_indent')
|
||||||
|
finish
|
||||||
|
endif
|
||||||
|
|
||||||
|
let s:save_cpo = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
|
let b:did_indent = 1
|
||||||
|
|
||||||
|
setlocal indentexpr=GetYAMLIndent(v:lnum)
|
||||||
|
setlocal indentkeys=!^F,o,O,0#,0},0],<:>,0-
|
||||||
|
setlocal nosmartindent
|
||||||
|
|
||||||
|
let b:undo_indent = 'setlocal indentexpr< indentkeys< smartindent<'
|
||||||
|
|
||||||
|
" Only define the function once.
|
||||||
|
if exists('*GetYAMLIndent')
|
||||||
|
finish
|
||||||
|
endif
|
||||||
|
|
||||||
|
function s:FindPrevLessIndentedLine(lnum, ...)
|
||||||
|
let prevlnum = prevnonblank(a:lnum-1)
|
||||||
|
let curindent = a:0 ? a:1 : indent(a:lnum)
|
||||||
|
while prevlnum
|
||||||
|
\&& indent(prevlnum) >= curindent
|
||||||
|
\&& getline(prevlnum) !~# '^\s*#'
|
||||||
|
let prevlnum = prevnonblank(prevlnum-1)
|
||||||
|
endwhile
|
||||||
|
return prevlnum
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function s:FindPrevLEIndentedLineMatchingRegex(lnum, regex)
|
||||||
|
let plilnum = s:FindPrevLessIndentedLine(a:lnum, indent(a:lnum)+1)
|
||||||
|
while plilnum && getline(plilnum) !~# a:regex
|
||||||
|
let plilnum = s:FindPrevLessIndentedLine(plilnum)
|
||||||
|
endwhile
|
||||||
|
return plilnum
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
let s:mapkeyregex='\v^\s*\#@!\S@=%(\''%([^'']|\''\'')*\'''.
|
||||||
|
\ '|\"%([^"\\]|\\.)*\"'.
|
||||||
|
\ '|%(%(\:\ )@!.)*)\:%(\ |$)'
|
||||||
|
let s:liststartregex='\v^\s*%(\-%(\ |$))'
|
||||||
|
|
||||||
|
let s:c_ns_anchor_char = '\v%([\n\r\uFEFF \t,[\]{}]@!\p)'
|
||||||
|
let s:c_ns_anchor_name = s:c_ns_anchor_char.'+'
|
||||||
|
let s:c_ns_anchor_property = '\v\&'.s:c_ns_anchor_name
|
||||||
|
|
||||||
|
let s:ns_word_char = '\v[[:alnum:]_\-]'
|
||||||
|
let s:ns_tag_char = '\v%('.s:ns_word_char.'|[#/;?:@&=+$.~*''()])'
|
||||||
|
let s:c_named_tag_handle = '\v\!'.s:ns_word_char.'+\!'
|
||||||
|
let s:c_secondary_tag_handle = '\v\!\!'
|
||||||
|
let s:c_primary_tag_handle = '\v\!'
|
||||||
|
let s:c_tag_handle = '\v%('.s:c_named_tag_handle.
|
||||||
|
\ '|'.s:c_secondary_tag_handle.
|
||||||
|
\ '|'.s:c_primary_tag_handle.')'
|
||||||
|
let s:c_ns_shorthand_tag = '\v'.s:c_tag_handle . s:ns_tag_char.'+'
|
||||||
|
let s:c_non_specific_tag = '\v\!'
|
||||||
|
let s:ns_uri_char = '\v%('.s:ns_word_char.'\v|[#/;?:@&=+$,.!~*''()[\]])'
|
||||||
|
let s:c_verbatim_tag = '\v\!\<'.s:ns_uri_char.'+\>'
|
||||||
|
let s:c_ns_tag_property = '\v'.s:c_verbatim_tag.
|
||||||
|
\ '\v|'.s:c_ns_shorthand_tag.
|
||||||
|
\ '\v|'.s:c_non_specific_tag
|
||||||
|
|
||||||
|
let s:block_scalar_header = '\v[|>]%([+-]?[1-9]|[1-9]?[+-])?'
|
||||||
|
|
||||||
|
function GetYAMLIndent(lnum)
|
||||||
|
if a:lnum == 1 || !prevnonblank(a:lnum-1)
|
||||||
|
return 0
|
||||||
|
endif
|
||||||
|
|
||||||
|
let prevlnum = prevnonblank(a:lnum-1)
|
||||||
|
let previndent = indent(prevlnum)
|
||||||
|
|
||||||
|
let line = getline(a:lnum)
|
||||||
|
if line =~# '^\s*#' && getline(a:lnum-1) =~# '^\s*#'
|
||||||
|
" Comment blocks should have identical indent
|
||||||
|
return previndent
|
||||||
|
elseif line =~# '^\s*[\]}]'
|
||||||
|
" Lines containing only closing braces should have previous indent
|
||||||
|
return indent(s:FindPrevLessIndentedLine(a:lnum))
|
||||||
|
endif
|
||||||
|
|
||||||
|
" Ignore comment lines when calculating indent
|
||||||
|
while getline(prevlnum) =~# '^\s*#'
|
||||||
|
let prevlnum = prevnonblank(prevlnum-1)
|
||||||
|
if !prevlnum
|
||||||
|
return previndent
|
||||||
|
endif
|
||||||
|
endwhile
|
||||||
|
|
||||||
|
let prevline = getline(prevlnum)
|
||||||
|
let previndent = indent(prevlnum)
|
||||||
|
|
||||||
|
" Any examples below assume that shiftwidth=2
|
||||||
|
if prevline =~# '\v[{[:]$|[:-]\ [|>][+\-]?%(\s+\#.*|\s*)$'
|
||||||
|
" Mapping key:
|
||||||
|
" nested mapping: ...
|
||||||
|
"
|
||||||
|
" - {
|
||||||
|
" key: [
|
||||||
|
" list value
|
||||||
|
" ]
|
||||||
|
" }
|
||||||
|
"
|
||||||
|
" - |-
|
||||||
|
" Block scalar without indentation indicator
|
||||||
|
return previndent+shiftwidth()
|
||||||
|
elseif prevline =~# '\v[:-]\ [|>]%(\d+[+\-]?|[+\-]?\d+)%(\#.*|\s*)$'
|
||||||
|
" - |+2
|
||||||
|
" block scalar with indentation indicator
|
||||||
|
"#^^ indent+2, not indent+shiftwidth
|
||||||
|
return previndent + str2nr(matchstr(prevline,
|
||||||
|
\'\v([:-]\ [|>])@<=[+\-]?\d+%([+\-]?%(\s+\#.*|\s*)$)@='))
|
||||||
|
elseif prevline =~# '\v\"%([^"\\]|\\.)*\\$'
|
||||||
|
" "Multiline string \
|
||||||
|
" with escaped end"
|
||||||
|
let qidx = match(prevline, '\v\"%([^"\\]|\\.)*\\')
|
||||||
|
return virtcol([prevlnum, qidx+1])
|
||||||
|
elseif line =~# s:liststartregex
|
||||||
|
" List line should have indent equal to previous list line unless it was
|
||||||
|
" caught by one of the previous rules
|
||||||
|
return indent(s:FindPrevLEIndentedLineMatchingRegex(a:lnum,
|
||||||
|
\ s:liststartregex))
|
||||||
|
elseif line =~# s:mapkeyregex
|
||||||
|
" Same for line containing mapping key
|
||||||
|
let prevmapline = s:FindPrevLEIndentedLineMatchingRegex(a:lnum,
|
||||||
|
\ s:mapkeyregex)
|
||||||
|
if getline(prevmapline) =~# '^\s*- '
|
||||||
|
return indent(prevmapline) + 2
|
||||||
|
else
|
||||||
|
return indent(prevmapline)
|
||||||
|
endif
|
||||||
|
elseif prevline =~# '^\s*- '
|
||||||
|
" - List with
|
||||||
|
" multiline scalar
|
||||||
|
return previndent+2
|
||||||
|
elseif prevline =~# s:mapkeyregex . '\v\s*%(%('.s:c_ns_tag_property.
|
||||||
|
\ '\v|'.s:c_ns_anchor_property.
|
||||||
|
\ '\v|'.s:block_scalar_header.
|
||||||
|
\ '\v)%(\s+|\s*%(\#.*)?$))*'
|
||||||
|
" Mapping with: value
|
||||||
|
" that is multiline scalar
|
||||||
|
return previndent+shiftwidth()
|
||||||
|
endif
|
||||||
|
return previndent
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
let &cpo = s:save_cpo
|
||||||
|
|
||||||
Reference in new issue
Block a user