diff --git a/.github/workflows/auto-fix.yml b/.github/workflows/auto-fix.yml index ebdaa68ec9..9041ed7a6f 100644 --- a/.github/workflows/auto-fix.yml +++ b/.github/workflows/auto-fix.yml @@ -3,7 +3,13 @@ name: auto-fix on: push: branches: - - 'master' + - master + pull_request: + branches: + - master + +permissions: + contents: write jobs: auto-fix: @@ -22,7 +28,11 @@ jobs: # lint - name: Auto-fix - run: pnpm run lint:fix + run: npm run lint:fix + + # format + - name: Format + run: npm run format # commit - name: Commit diff --git a/.github/workflows/extension-build.yml b/.github/workflows/extension-build.yml deleted file mode 100644 index 993380f68d..0000000000 --- a/.github/workflows/extension-build.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: extension - -on: - push: - branches: - - 'master' - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - uses: pnpm/action-setup@v4 - - - uses: actions/setup-node@v4 - with: - node-version: 20 - cache: pnpm - - - name: Install dependencies - run: pnpm install - - - name: Build - run: pnpm --filter ./extensions/vscode run pack - - - name: Upload Artifact - uses: actions/upload-artifact@v4 - with: - name: Extension - path: ./extensions/vscode/volar-*.vsix diff --git a/.github/workflows/extension-release.yml b/.github/workflows/extension-release.yml index 6102269aa7..9451ac65bd 100644 --- a/.github/workflows/extension-release.yml +++ b/.github/workflows/extension-release.yml @@ -1,10 +1,7 @@ -name: extension +name: extension-release on: workflow_dispatch: - push: - tags: - - '*' jobs: release: @@ -19,13 +16,34 @@ jobs: node-version: 20 cache: pnpm - - run: pnpm install -g ovsx - - run: pnpm install --frozen-lockfile - - run: pnpm run build:minify && pnpm ovsx publish + - name: Install global tools + run: pnpm install -g @vscode/vsce ovsx + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build + run: pnpm run build && pnpm --filter volar run build:prod + + - name: Publish to Open VSX + id: publish_ovsx + run: | + VERSION=$(jq -r .version package.json) + echo "version=$VERSION" >> $GITHUB_OUTPUT + if [[ "$VERSION" == *-* ]]; then + echo "Detected prerelease version: $VERSION" + pnpm ovsx publish --pre-release + else + echo "Detected stable release version: $VERSION" + pnpm ovsx publish + fi working-directory: extensions/vscode env: OVSX_PAT: ${{ secrets.OVSX_PAT }} - # - run: pnpm ovsx publish - # working-directory: extensions/vscode-typescript-plugin - # env: - # OVSX_PAT: ${{ secrets.OVSX_PAT }} + + - name: Publish to VSCode Marketplace + if: ${{ !contains(steps.publish_ovsx.outputs.version, '-') }} + working-directory: extensions/vscode + env: + VSCE_TOKEN: ${{ secrets.VSCE_TOKEN }} + run: vsce publish diff --git a/.github/workflows/pkg.pr.new.yml b/.github/workflows/pkg.pr.new.yml index 8ab6a806bb..033250d0fd 100644 --- a/.github/workflows/pkg.pr.new.yml +++ b/.github/workflows/pkg.pr.new.yml @@ -1,4 +1,5 @@ name: publish-any-commit + on: [push, pull_request] jobs: diff --git a/.gitignore b/.gitignore index 7b42626d66..afde7b6d78 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,7 @@ -out dist node_modules *.tsbuildinfo *.vsix -.vscode-test-web -extensions/*/meta.json -extensions/*/stats.html -extensions/vscode/src/generated-meta.ts packages/*/*.d.ts packages/*/*.js diff --git a/.oxlintrc.json b/.oxlintrc.json new file mode 100644 index 0000000000..569c5e6e74 --- /dev/null +++ b/.oxlintrc.json @@ -0,0 +1,11 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "ignorePatterns": [ + "**/*.vue" + ], + "rules": { + "curly": "warn", + "no-unused-expressions": "warn", + "require-await": "warn" + } +} diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000000..1b0a8caae7 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,6 @@ +{ + "recommendations": [ + "dprint.dprint", + "johnsoncodehk.vscode-tsslint" + ] +} diff --git a/.vscode/launch.json b/.vscode/launch.json index 1631ace705..5cb40c82c6 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,52 +1,52 @@ -// A launch configuration that compiles the extension and then opens it inside a new window -{ - "version": "0.2.0", - "configurations": [ - { - "name": "Launch Client", - "type": "extensionHost", - "request": "launch", - "autoAttachChildProcesses": true, - "runtimeExecutable": "${execPath}", - "args": [ - "--extensionDevelopmentPath=${workspaceRoot}/extensions/vscode", - "--folder-uri=${workspaceRoot}/test-workspace", - ], - "outFiles": [ - "${workspaceRoot}/**/*.js" - ], - "preLaunchTask": { - "type": "npm", - "script": "watch" - } - }, - { - "name": "Launch Web Client", - "type": "pwa-extensionHost", - "debugWebWorkerHost": true, - "request": "launch", - "args": [ - "--disable-extensions", - "--extensionDevelopmentPath=${workspaceFolder}/extensions/vscode", - "--extensionDevelopmentKind=web" - ], - "outFiles": [ - "${workspaceRoot}/**/*.js" - ], - "preLaunchTask": { - "type": "npm", - "script": "watch" - } - }, - { - "name": "Attach to Language Server", - "type": "node", - "request": "attach", - "port": 6009, - "restart": true, - "outFiles": [ - "${workspaceRoot}/**/*.js" - ] - } - ], -} \ No newline at end of file +// A launch configuration that compiles the extension and then opens it inside a new window +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Client", + "type": "extensionHost", + "request": "launch", + "autoAttachChildProcesses": true, + "runtimeExecutable": "${execPath}", + "args": [ + "--extensionDevelopmentPath=${workspaceRoot}/extensions/vscode", + "--folder-uri=${workspaceRoot}/test-workspace" + ], + "outFiles": [ + "${workspaceRoot}/**/*.js" + ], + "preLaunchTask": { + "type": "npm", + "script": "watch" + } + }, + { + "name": "Launch Web Client", + "type": "pwa-extensionHost", + "debugWebWorkerHost": true, + "request": "launch", + "args": [ + "--disable-extensions", + "--extensionDevelopmentPath=${workspaceFolder}/extensions/vscode", + "--extensionDevelopmentKind=web" + ], + "outFiles": [ + "${workspaceRoot}/**/*.js" + ], + "preLaunchTask": { + "type": "npm", + "script": "watch" + } + }, + { + "name": "Attach to Language Server", + "type": "node", + "request": "attach", + "port": 6009, + "restart": true, + "outFiles": [ + "${workspaceRoot}/**/*.js" + ] + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 0efd9ce70d..28d945c5b2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,24 +1,23 @@ { - "typescript.format.semicolons": "insert", - "editor.insertSpaces": false, - "editor.detectIndentation": false, - "editor.codeActionsOnSave": { - "source.organizeImports": "always" + "dprint.path": "node_modules/dprint/dprint", + "[javascript]": { + "editor.defaultFormatter": "dprint.dprint" }, - "json.format.keepLines": true, - "typescript.tsdk": "node_modules/typescript/lib", "[typescript]": { - "editor.defaultFormatter": "vscode.typescript-language-features" - }, - "[javascript]": { - "editor.defaultFormatter": "vscode.typescript-language-features" + "editor.defaultFormatter": "dprint.dprint" }, "[json]": { - "editor.defaultFormatter": "vscode.json-language-features" + "editor.defaultFormatter": "dprint.dprint" }, "[jsonc]": { - "editor.defaultFormatter": "vscode.json-language-features" + "editor.defaultFormatter": "dprint.dprint" }, + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.organizeImports": "always" + }, + "editor.detectIndentation": false, + "editor.insertSpaces": false, "files.exclude": { "packages/*/*.d.ts": true, "packages/*/*.js": true, @@ -26,5 +25,8 @@ "packages/*/lib/**/*.d.ts": true, "packages/*/lib/**/*.js": true, "packages/*/lib/**/*.map": true - } -} \ No newline at end of file + }, + "json.format.keepLines": true, + "typescript.format.semicolons": "insert", + "typescript.tsdk": "node_modules/typescript/lib" +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 070d88eb16..b20df75820 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,33 +1,40 @@ -{ - "version": "2.0.0", - "tasks": [ - { - "type": "npm", - "script": "compile", - "group": "build", - "presentation": { - "panel": "dedicated", - "reveal": "never" - }, - "problemMatcher": [ - "$tsc" - ] - }, - { - "type": "npm", - "script": "watch", - "isBackground": true, - "group": { - "kind": "build", - "isDefault": true - }, - "presentation": { - "panel": "dedicated", - "reveal": "never" - }, - "problemMatcher": [ - "$tsc-watch" - ] - } - ] -} \ No newline at end of file +{ + "version": "2.0.0", + "tasks": [ + { + "type": "npm", + "script": "compile", + "group": "build", + "presentation": { + "panel": "dedicated", + "reveal": "never" + }, + "problemMatcher": [ + "$tsc" + ] + }, + { + "type": "npm", + "script": "watch", + "isBackground": true, + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "panel": "dedicated", + "reveal": "never" + }, + "problemMatcher": { + "pattern": { + "regexp": "__________" + }, + "background": { + "activeOnStart": true, + "beginsPattern": " ", + "endsPattern": "Waiting for changes..." + } + } + } + ] +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a2749815f..f34c1ca467 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,98 @@ # Changelog -> [Join the Insiders Program](https://github.com/vuejs/language-tools/wiki/Get-Insiders-Edition) for more exclusive features and updates. +## 3.0.0 (2025-06-25) + +### Features + +- feat(typescript-plugin): skip declaration files in goto components definition (#5221) - Thanks to @KazariEX! +- feat(language-core): introduce `strictVModel` option (#5229) - Thanks to @KazariEX! +- feat(vscode, language-server, typescript-plugin): communicate with tsserver based on request forwarding (#5252, #5395, #5443) +- feat(language-core): support navigation of events with `v-on` syntax (#5275) - Thanks to @KazariEX! +- feat(language-core): type support of slot children (#5137) - Thanks to @KazariEX! +- feat(language-service): autocomplete for props with union type +- feat(language-service): document links for template refs (#5385) - Thanks to @alex-snezhko! +- feat(language-core): resolve external stylesheets (#5136) - Thanks to @KazariEX! +- feat(language-core): add `strictCssModules` option (#5164) - Thanks to @KazariEX! +- feat(component-type-helpers): add `ComponentAttrs` type for attribute extraction +- feat(vscode): add support for `typescript.sortImports`, `typescript.removeUnusedImports` commands (#5444) +- feat(vscode): i18n support of configurations and commands with `zh-CN`, `zh-TW`, `ru` and `ja` (#5330, #5340, #5404) - Thanks to @KazariEX, @PurplePlanen and @zyoshoka! + +### Bug Fixes + +- fix(language-core): generate condition guards for model events (#5225) - Thanks to @KazariEX! +- fix(language-core): prevent global types generation in declaration files (#5239) - Thanks to @KazariEX! +- fix(language-core): prevent eager inference of slot props from generics (#5247) - Thanks to @KazariEX! +- fix(typescript-plugin): prevent highlighting native element tags with same name as components (#5253) - Thanks to @KazariEX! +- fix(language-service): do not provide required props inlay hints for intrinsic elements (#5258) - Thanks to @KazariEX! +- fix(vscode): handle `typescript-language-features` module loading race condition (#5260) +- fix(component-meta): update event type representation to include array notation +- fix(language-core): correct error mapping when prop exp is arrow function (#5262) - Thanks to @KazariEX! +- fix(language-service): add document highlights support (#5263) - Thanks to @KazariEX! +- fix(language-core): correct type inference of multiple template refs with same name (#5271) - Thanks to @KazariEX! +- fix(language-core): skip AST parsing when the expression is an identifier (#5268) - Thanks to @KazariEX! +- fix(language-core): do not drop leading comments of `defineModels` (#5273) - Thanks to @KazariEX! +- fix(language-core): improve fault tolerance for unsupported script languages +- fix(language-core): avoid invalid auto import edit position when setup global types fails +- fix(language-core): transform slot parameter list into equivalent binding pattern (#5245) - Thanks to @KazariEX! +- fix(language-core): correct codegen when src path does not match the generated length - Thanks to @KazariEX! +- fix(language-service): exclude `data-` attribute completion from sfc level nodes - Thanks to @KazariEX! +- fix(language-core): remove semantic highlight of v-bind shorthand (#5321) - Thanks to @KazariEX! +- fix(vscode): inline html comment pattern in Vue syntax definition (#5327) - Thanks to @zyoshoka! +- fix(language-core): avoid unrelated virtual code recomputes on style and template change - Thanks to @KazariEX! +- fix(component-meta): attach namespace prefix correctly on generated types (#5326) - Thanks to @KazariEX! +- fix(language-core): drop `undefined` from optional prop type with default in template (#5339) - Thanks to @Dylancyclone! +- fix: depend on exact volar version (#5345) - Thanks to @tomblachut! +- fix(language-core): ignore frontmatter block in markdown files (#5362) - Thanks to @brc-dd! +- fix(component-meta): only exclude vnode events from props (#5369) - Thanks to @KazariEX! +- fix(language-core): skip css references for position within virtual code with `navigation: true` (#5378) - Thanks to @KazariEX! +- fix(language-core): hoist export declarations from generic script block (#5398) - Thanks to @KazariEX! +- fix(vscode): correct syntax highlight for directives starting with `v-for` (#5399) - Thanks to @KazariEX! +- fix(language-core): correct support for flatten plugins (#5392) - Thanks to @zhiyuanzmj! +- fix(language-core): remove `semantic` code feature on first argument of `useCssModule` and `useTemplateRef` - Thanks to @KazariEX! +- fix(typescript-plugin): filter completion items of macros and global variables in template and styles (#5425) - Thanks to @KazariEX! +- fix(language-core): do not generate redundant function scopes to affect type narrowing (#5430) - Thanks to @KazariEX! +- fix(component-meta): add new file name in `updateFile` (#5438) - Thanks to @Akryum! +- fix(language-core): `Prettify` breaks generics inferencing (#5424) - Thanks to @so1ve! +- fix(language-core): use `var` instead of `let` to declare `attrsVar` that may be hoisted - Thanks to @KazariEX! + +### Performance + +- perf(language-core): cache and reuse inline ts asts during full updates (#5435) - Thanks to @KazariEX! + +### Other Changes + +- refactor(vscode, language-server): remove hybrid mode configuration (#5248) +- refactor(vscode): remove write virtual files command +- chore(vscode): correct `directory` path in package.json (#5283) - Thanks to @zyoshoka! +- chore(vscode): use rolldown for bundling (#5337) - Thanks to @KazariEX! +- refactor(vscode): remove doctor - Thanks to @KazariEX! +- docs: update instructions for neovim lsp configuration (#5361) - Thanks to @kshksdrt! +- refactor(vscode): remove Vite problem matcher (#5375) +- chore(docs): update vue language package name (#5376) - Thanks to @marktlinn! +- chore(ci): set pre-release status when publishing to Open VSX (#5377) - Thanks to @lukashass! +- docs: fallback workaround of `vue_language_server_path` in nvim setup example (#5391) - Thanks to @menuRivera! +- test(component-meta): simplify code with snapshots (#5403) - Thanks to @KazariEX! +- docs(nvim): move neovim lspconfig docs to wiki page (#5408) - Thanks to @RayGuo-ergou! +- refactor(language-server): drop `typescript.tsdk` initialization option (#5409) +- refactor(language-service): drop name casing convertion and its language status item (#5411) - Thanks to @KazariEX! +- refactor(language-core): drop `defineProp` support (#5415) - Thanks to @KazariEX! +- chore(vscode): change display name to "Vue (Official)" +- refactor: cleanup dependencies relationship (#5421) +- refactor(component-meta): use type-helpers as a peer dependency +- refactor(vscode): cleanup extension client (#5422) +- refactor(language-server): move in server code from insiders edition (#5423) +- chore: introduce oxlint for faster linting (#5416) - Thanks to @KazariEX! +- refactor(vscode): remove split editor feature (#5446) +- refactor(vscode): rename configuration keys from `complete` to `suggest` for clarity + +## 2.2.10 official (2025-04-22) + +## Bug Fixes + +- fix(language-core): generate condition guards for model events (#5225) - Thanks to @KazariEX! +- fix(language-core): prevent global types generation in declaration files (#5239) - Thanks to @KazariEX! +- fix(language-core): prevent eager inference of slot props from generics (#5247) - Thanks to @KazariEX! +- fix(typescript-plugin): prevent highlighting native element tags with same name as components (#5253) - Thanks to @KazariEX! ## 2.2.8 official, 2.2.9 insiders (2025-03-02) diff --git a/README.md b/README.md index d7749c2175..e1be3ff9e2 100644 --- a/README.md +++ b/README.md @@ -1,131 +1,46 @@ # Vue Language Tools -> ⚡ High-performance Vue language tooling based-on [Volar.js](https://volarjs.dev/) +

+ Version + Downloads + License +

+ +> ⚡ Vue language toolset with native TypeScript performance based-on [Volar.js](https://volarjs.dev/) 💬 **#language-tools** on our [Discord Server](https://discord.gg/vue) ## Packages -- [Vue Language Features](https://github.com/vuejs/language-tools/tree/master/extensions/vscode) \ +- [Vue (Official)](https://github.com/vuejs/language-tools/tree/master/extensions/vscode) \ *Vue, Vitepress, petite-vue language support extension for VSCode* - [vue-tsc](https://github.com/vuejs/language-tools/tree/master/packages/tsc) \ *Type-check and dts build command line tool* - [vue-component-meta](https://github.com/vuejs/language-tools/tree/master/packages/component-meta) \ *Component props, events, slots types information extract tool* -- [vite-plugin-vue-component-preview](https://github.com/johnsoncodehk/vite-plugin-vue-component-preview) \ -*Vite plugin for support Vue component preview view with `Vue Language Features`* - [`@vue/language-server`](/packages/language-server/) \ *The language server itself*. - [`@vue/typescript-plugin`](/packages/typescript-plugin/) \ -*Typescript plugin for the language server*. +*TypeScript LanguageService Plugin for Vue*. ## Community Integration -[yaegassy/coc-volar](https://github.com/yaegassy/coc-volar) ⚡ 🤝 🅿️ \ +[yaegassy/coc-volar](https://github.com/yaegassy/coc-volar) \ *Vue language client for coc.nvim* -[neovim/nvim-lspconfig](https://github.com/neovim/nvim-lspconfig) ⚡ 🤝 \ -*Vue language server configuration for Neovim* - -
- How to configure vue language server with neovim and lsp? - -### Hybrid mode configuration (Requires `@vue/language-server` version `^2.0.0`) - -Note: The "Take Over" mode has been discontinued. Instead, a new "Hybrid" mode has been introduced. In this mode, the Vue Language Server exclusively manages the CSS/HTML sections. As a result, you must run `@vue/language-server` in conjunction with a TypeScript server that employs `@vue/typescript-plugin`. Below is a streamlined configuration for Neovim's LSP, updated to accommodate the language server following the upgrade to version `2.0.0`. - -> For nvim-lspconfig versions below [v1.0.0](https://newreleases.io/project/github/neovim/nvim-lspconfig/release/v1.0.0) use tsserver instead of ts_ls, e.g. `lspconfig.ts_ls.setup` - -```lua --- If you are using mason.nvim, you can get the ts_plugin_path like this --- local mason_registry = require('mason-registry') --- local vue_language_server_path = mason_registry.get_package('vue-language-server'):get_install_path() .. '/node_modules/@vue/language-server' - -local vue_language_server_path = '/path/to/@vue/language-server' - -local lspconfig = require('lspconfig') - -lspconfig.ts_ls.setup { - init_options = { - plugins = { - { - name = '@vue/typescript-plugin', - location = vue_language_server_path, - languages = { 'vue' }, - }, - }, - }, - filetypes = { 'typescript', 'javascript', 'javascriptreact', 'typescriptreact', 'vue' }, -} - --- No need to set `hybridMode` to `true` as it's the default value -lspconfig.volar.setup {} -``` - -### Non-Hybrid mode(similar to takeover mode) configuration (Requires `@vue/language-server` version `^2.0.7`) - -Note: If `hybridMode` is set to `false` `Volar` will run embedded `ts_ls` therefore there is no need to run it separately. - -For more information see [#4119](https://github.com/vuejs/language-tools/pull/4119) +[neovim/nvim-lspconfig](https://github.com/neovim/nvim-lspconfig) \ +*Vue language server configuration for Neovim*, check documentation [here](https://github.com/vuejs/language-tools/wiki/Neovim) to set it up. -*Make sure you have typescript installed globally or pass the location to volar* - -Use volar for all `.{vue,js,ts,tsx,jsx}` files. -```lua -local lspconfig = require('lspconfig') - --- lspconfig.ts_ls.setup {} -lspconfig.volar.setup { - filetypes = { 'typescript', 'javascript', 'javascriptreact', 'typescriptreact', 'vue' }, - init_options = { - vue = { - hybridMode = false, - }, - }, -} -``` - -Use `volar` for only `.vue` files and `ts_ls` for `.ts` and `.js` files. -```lua -local lspconfig = require('lspconfig') - -lspconfig.ts_ls.setup { - init_options = { - plugins = { - { - name = '@vue/typescript-plugin', - location = '/path/to/@vue/language-server', - languages = { 'vue' }, - }, - }, - }, -} - -lspconfig.volar.setup { - init_options = { - vue = { - hybridMode = false, - }, - }, -} -``` - -### nvim-cmp integration - -Check out this [discussion](https://github.com/vuejs/language-tools/discussions/4495) - -
- -[mattn/vim-lsp-settings](https://github.com/mattn/vim-lsp-settings) ⚡ \ +[mattn/vim-lsp-settings](https://github.com/mattn/vim-lsp-settings) \ *Vue language server auto configuration for vim-lsp* -[sublimelsp/LSP-volar](https://github.com/sublimelsp/LSP-volar) 🤝 \ +[sublimelsp/LSP-volar](https://github.com/sublimelsp/LSP-volar) \ *Vue language client for Sublime* [kabiaa/atom-ide-volar](https://github.com/kabiaa/atom-ide-volar) \ *Vue language client for Atom* -[emacs-lsp/lsp-mode](https://github.com/emacs-lsp/lsp-mode) ([jadestrong/lsp-volar](https://github.com/jadestrong/lsp-volar)) ⚡ 🤝 \ +[emacs-lsp/lsp-mode](https://github.com/emacs-lsp/lsp-mode) ([jadestrong/lsp-volar](https://github.com/jadestrong/lsp-volar)) \ *Vue language client for Emacs* [tommasongr/nova-vue](https://github.com/tommasongr/nova-vue) \ @@ -143,10 +58,6 @@ Check out this [discussion](https://github.com/vuejs/language-tools/discussions/ [Eclipse WildWebDeveloper](https://github.com/eclipse-wildwebdeveloper/wildwebdeveloper) \ *Vue language server configuration for Eclipse* -\* ⚡ support [multiple servers](https://github.com/vuejs/language-tools/discussions/393#discussioncomment-1213736) \ -\* 🤝 support [take over mode](https://github.com/vuejs/language-tools/discussions/471) \ -\* 🅿️ support [extra preview features](https://twitter.com/johnsoncodehk/status/1507024137901916161) - @@ -175,82 +86,9 @@ Additional info for contributing to open source projects can be found here: http To develop with upstream Volar.js modules, you can setup workspace with https://github.com/volarjs/workspace. ---- +## ❤️ Thanks to Our Sponsors - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Special Sponsor -
-
- - - -

Next Generation Tooling

-
- Platinum Sponsors -
- - - -

An approachable, performant and versatile framework for building web user interfaces.

-
- - - - -

Astro powers the world's fastest websites, client-side web apps, dynamic API endpoints, and everything in-between.

-
- - - - -

Essential tools for software developers and teams.

-
- - - -

Stay in the flow with instant dev experiences.
No more hours stashing/pulling/installing locally

-

— just click, and start coding.

-
- Silver Sponsors -
- - - - - -
- -

- Become a sponsor -

+This project is made possible thanks to our generous sponsors:

diff --git a/dprint.json b/dprint.json new file mode 100644 index 0000000000..c49deea9a1 --- /dev/null +++ b/dprint.json @@ -0,0 +1,18 @@ +{ + "useTabs": true, + "typescript": { + "quoteStyle": "preferSingle", + "arrowFunction.useParentheses": "preferNone" + }, + "json": { + }, + "excludes": [ + "**/node_modules", + "**/*-lock.json", + "packages/language-service/data" + ], + "plugins": [ + "https://plugins.dprint.dev/typescript-0.95.7.wasm", + "https://plugins.dprint.dev/json-0.20.0.wasm" + ] +} diff --git a/extensions/vscode/.vscodeignore b/extensions/vscode/.vscodeignore index df03891801..ba74b028d7 100644 --- a/extensions/vscode/.vscodeignore +++ b/extensions/vscode/.vscodeignore @@ -1,7 +1,3 @@ -out -scripts -src +**/*.ts tests -tsconfig.* -meta.json -stats.html +tsconfig.json diff --git a/extensions/vscode/README.md b/extensions/vscode/README.md index c26404c94e..00546dc1f9 100644 --- a/extensions/vscode/README.md +++ b/extensions/vscode/README.md @@ -7,236 +7,10 @@ - [petite](https://github.com/JessicaSachs/petite) - [volar-starter](https://github.com/johnsoncodehk/volar-starter) (For bug report and experiment features testing) -## Insiders Program 🚀 -This project is community-driven. If you would like to support this project, consider joining the [Insiders Program](https://github.com/vuejs/language-tools/wiki/Get-Insiders-Edition) to improve the sustainability of this project and unlock more features. +## ❤️ Thanks to Our Sponsors - - -## Sponsors - - +This project is made possible thanks to our generous sponsors:

@@ -244,10 +18,6 @@ Finally you need to make VS Code recognize your new extension and automatically

-

- Become a sponsor -

- ## Credits - [vscode-extension-samples](https://github.com/microsoft/vscode-extension-samples) shows all the knowledge required to develop the extension. diff --git a/extensions/vscode/client.js b/extensions/vscode/client.js deleted file mode 100644 index 297ee3763a..0000000000 --- a/extensions/vscode/client.js +++ /dev/null @@ -1,5 +0,0 @@ -try { - module.exports = require('./out/nodeClientMain'); -} catch { - module.exports = require('./dist/client'); -} diff --git a/extensions/vscode/images/icon.png b/extensions/vscode/icon.png similarity index 100% rename from extensions/vscode/images/icon.png rename to extensions/vscode/icon.png diff --git a/extensions/vscode/images/split-editors.png b/extensions/vscode/images/split-editors.png deleted file mode 100644 index c8e9678caa..0000000000 Binary files a/extensions/vscode/images/split-editors.png and /dev/null differ diff --git a/extensions/vscode/index.ts b/extensions/vscode/index.ts new file mode 100644 index 0000000000..a42ecadc28 --- /dev/null +++ b/extensions/vscode/index.ts @@ -0,0 +1,196 @@ +import { activateAutoInsertion, activateDocumentDropEdit, createLabsInfo, middleware } from '@volar/vscode'; +import * as lsp from '@volar/vscode/node'; +import * as fs from 'node:fs'; +import * as path from 'node:path'; +import { + defineExtension, + executeCommand, + extensionContext, + nextTick, + onDeactivate, + useActiveTextEditor, + useCommand, + useOutputChannel, + useVisibleTextEditors, + watch, +} from 'reactive-vscode'; +import * as vscode from 'vscode'; +import { config } from './lib/config'; + +let client: lsp.BaseLanguageClient | undefined; + +class _LanguageClient extends lsp.LanguageClient { + fillInitializeParams(params: lsp.InitializeParams) { + // fix https://github.com/vuejs/language-tools/issues/1959 + params.locale = vscode.env.language; + } +} + +export const { activate, deactivate } = defineExtension(async () => { + await vscode.extensions.getExtension('vscode.typescript-language-features')?.activate(); + + const context = extensionContext.value!; + const volarLabs = createLabsInfo(); + const activeTextEditor = useActiveTextEditor(); + const visibleTextEditors = useVisibleTextEditors(); + const { stop } = watch(activeTextEditor, () => { + if ( + !visibleTextEditors.value.some( + editor => config.server.includeLanguages.includes(editor.document.languageId), + ) + ) { + return; + } + + nextTick(() => stop()); + + watch(() => config.server.includeLanguages, async () => { + const reload = await vscode.window.showInformationMessage( + 'Please restart extension host to apply the new language settings.', + 'Restart Extension Host', + ); + if (reload) { + executeCommand('workbench.action.restartExtensionHost'); + } + }); + + // Setup typescript.js in production mode + if (fs.existsSync(path.join(__dirname, 'language-server.js'))) { + fs.writeFileSync( + path.join(__dirname, 'typescript.js'), + `module.exports = require("${ + vscode.env.appRoot.replace(/\\/g, '/') + }/extensions/node_modules/typescript/lib/typescript.js");`, + ); + } + + volarLabs.addLanguageClient(client = launch(context)); + + const selectors = config.server.includeLanguages; + + activateAutoInsertion(selectors, client); + activateDocumentDropEdit(selectors, client); + }, { immediate: true }); + + useCommand('vue.action.restartServer', async () => { + await executeCommand('typescript.restartTsServer'); + await client?.stop(); + client?.outputChannel.clear(); + await client?.start(); + }); + + onDeactivate(async () => { + await client?.stop(); + }); + + return volarLabs.extensionExports; +}); + +function launch(context: vscode.ExtensionContext) { + const serverModule = vscode.Uri.joinPath(context.extensionUri, 'dist', 'language-server.js'); + const client = new _LanguageClient( + 'vue', + 'Vue', + { + run: { + module: serverModule.fsPath, + transport: lsp.TransportKind.ipc, + options: {}, + }, + debug: { + module: serverModule.fsPath, + transport: lsp.TransportKind.ipc, + options: { execArgv: ['--nolazy', '--inspect=' + 6009] }, + }, + }, + { + middleware: { + ...middleware, + async resolveCodeAction(item, token, next) { + if (item.kind?.value === 'refactor.move.newFile.dumb' && config.codeActions.askNewComponentName) { + const inputName = await vscode.window.showInputBox({ value: (item as any).data.original.data.newName }); + if (!inputName) { + return item; // cancel + } + (item as any).data.original.data.newName = inputName; + } + return await (middleware.resolveCodeAction?.(item, token, next) ?? next(item, token)); + }, + }, + documentSelector: config.server.includeLanguages, + markdown: { + isTrusted: true, + supportHtml: true, + }, + outputChannel: useOutputChannel('Vue Language Server'), + }, + ); + + client.onNotification('tsserver/request', async ([seq, command, args]) => { + const res = await vscode.commands.executeCommand<{ body: unknown } | undefined>( + 'typescript.tsserverRequest', + command, + args, + { isAsync: true, lowPriority: true }, + ); + client.sendNotification('tsserver/response', [seq, res?.body]); + }); + client.start(); + + return client; +} + +try { + const fs = require('node:fs'); + const tsExtension = vscode.extensions.getExtension('vscode.typescript-language-features')!; + const readFileSync = fs.readFileSync; + const extensionJsPath = require.resolve('./dist/extension.js', { + paths: [tsExtension.extensionPath], + }); + + // @ts-expect-error + fs.readFileSync = (...args) => { + if (args[0] === extensionJsPath) { + let text = readFileSync(...args) as string; + + // patch readPlugins + text = text.replace( + 'languages:Array.isArray(e.languages)', + [ + 'languages:', + `e.name==='vue-typescript-plugin-pack'?[${ + config.server.includeLanguages + .map(lang => `'${lang}'`) + .join(',') + }]`, + ':Array.isArray(e.languages)', + ].join(''), + ); + + // patch jsTsLanguageModes + text = text.replace( + 't.jsTsLanguageModes=[t.javascript,t.javascriptreact,t.typescript,t.typescriptreact]', + s => s + '.concat("vue")', + ); + // patch isSupportedLanguageMode + text = text.replace( + '.languages.match([t.typescript,t.typescriptreact,t.javascript,t.javascriptreact]', + s => s + '.concat("vue")', + ); + + return text; + } + return readFileSync(...args); + }; + + const loadedModule = require.cache[extensionJsPath]; + if (loadedModule) { + delete require.cache[extensionJsPath]; + const patchedModule = require(extensionJsPath); + Object.assign(loadedModule.exports, patchedModule); + } + + if (tsExtension.isActive) { + vscode.commands.executeCommand('workbench.action.restartExtensionHost'); + } +} catch {} diff --git a/extensions/vscode/languages/markdown-language-configuration.json b/extensions/vscode/languages/markdown-language-configuration.json index ccd6f4cb4c..c9a1e2f79a 100644 --- a/extensions/vscode/languages/markdown-language-configuration.json +++ b/extensions/vscode/languages/markdown-language-configuration.json @@ -51,6 +51,6 @@ [ "{{", "}}" - ], + ] ] -} \ No newline at end of file +} diff --git a/extensions/vscode/languages/sfc-template-language-configuration.json b/extensions/vscode/languages/sfc-template-language-configuration.json index 70fc485003..376d2d0715 100644 --- a/extensions/vscode/languages/sfc-template-language-configuration.json +++ b/extensions/vscode/languages/sfc-template-language-configuration.json @@ -3,6 +3,6 @@ [ "{{", "}}" - ], + ] ] -} \ No newline at end of file +} diff --git a/extensions/vscode/languages/vue-language-configuration.json b/extensions/vscode/languages/vue-language-configuration.json index c0a0494dbd..0863ee36b5 100644 --- a/extensions/vscode/languages/vue-language-configuration.json +++ b/extensions/vscode/languages/vue-language-configuration.json @@ -101,7 +101,7 @@ [ "`", "`" - ], + ] ], "colorizedBracketPairs": [], "folding": { @@ -142,4 +142,4 @@ "increaseIndentPattern": "<(?!\\?|(?:area|base|br|col|frame|hr|html|img|input|keygen|link|menuitem|meta|param|source|track|wbr|script|style)\\b|[^>]*\\/>)([-_\\.A-Za-z0-9]+)(?=\\s|>)\\b[^>]*>(?!\\s*\\()(?!.*<\\/\\1>)|)|\\{[^}\"']*$", "decreaseIndentPattern": "^\\s*(<\\/(?!html)[-_\\.A-Za-z0-9]+\\b[^>]*>|-->|\\})" } -} \ No newline at end of file +} diff --git a/extensions/vscode/src/config.ts b/extensions/vscode/lib/config.ts similarity index 58% rename from extensions/vscode/src/config.ts rename to extensions/vscode/lib/config.ts index 11ac8ef56e..4447e6b910 100644 --- a/extensions/vscode/src/config.ts +++ b/extensions/vscode/lib/config.ts @@ -1,7 +1,7 @@ import { defineConfigObject } from 'reactive-vscode'; -import { NestedScopedConfigs, scopedConfigs } from './generated-meta'; +import { type NestedScopedConfigs, scopedConfigs } from './generated-meta'; export const config = defineConfigObject( scopedConfigs.scope, - scopedConfigs.defaults + scopedConfigs.defaults, ); diff --git a/extensions/vscode/lib/generated-meta.ts b/extensions/vscode/lib/generated-meta.ts new file mode 100644 index 0000000000..1c0af10ec0 --- /dev/null +++ b/extensions/vscode/lib/generated-meta.ts @@ -0,0 +1,432 @@ +// This file is generated by `vscode-ext-gen`. Do not modify manually. +// @see https://github.com/antfu/vscode-ext-gen + +// Meta info +export const publisher = 'Vue'; +export const name = 'volar'; +export const version = '3.0.0-beta.4'; +export const displayName = 'Vue (Official)'; +export const description = 'Language Support for Vue'; +export const extensionId = `${publisher}.${name}`; + +/** + * Type union of all commands + */ +export type CommandKey = 'vue.action.restartServer'; + +/** + * Commands map registed by `Vue.volar` + */ +export const commands = { + /** + * %command.action.restartServer% + * @value `vue.action.restartServer` + */ + actionRestartServer: 'vue.action.restartServer', +} satisfies Record; + +/** + * Type union of all configs + */ +export type ConfigKey = + | 'vue.trace.server' + | 'vue.server.includeLanguages' + | 'vue.codeActions.askNewComponentName' + | 'vue.suggest.componentNameCasing' + | 'vue.suggest.propNameCasing' + | 'vue.suggest.defineAssignment' + | 'vue.autoInsert.dotValue' + | 'vue.autoInsert.bracketSpacing' + | 'vue.inlayHints.destructuredProps' + | 'vue.inlayHints.missingProps' + | 'vue.inlayHints.inlineHandlerLeading' + | 'vue.inlayHints.optionsWrapper' + | 'vue.inlayHints.vBindShorthand' + | 'vue.format.template.initialIndent' + | 'vue.format.script.initialIndent' + | 'vue.format.style.initialIndent' + | 'vue.format.wrapAttributes'; + +export interface ConfigKeyTypeMap { + 'vue.trace.server': 'off' | 'messages' | 'verbose'; + 'vue.server.includeLanguages': string[]; + 'vue.codeActions.askNewComponentName': boolean; + 'vue.suggest.componentNameCasing': 'preferKebabCase' | 'preferPascalCase' | 'alwaysKebabCase' | 'alwaysPascalCase'; + 'vue.suggest.propNameCasing': 'preferKebabCase' | 'preferCamelCase' | 'alwaysKebabCase' | 'alwaysCamelCase'; + 'vue.suggest.defineAssignment': boolean; + 'vue.autoInsert.dotValue': boolean; + 'vue.autoInsert.bracketSpacing': boolean; + 'vue.inlayHints.destructuredProps': boolean; + 'vue.inlayHints.missingProps': boolean; + 'vue.inlayHints.inlineHandlerLeading': boolean; + 'vue.inlayHints.optionsWrapper': boolean; + 'vue.inlayHints.vBindShorthand': boolean; + 'vue.format.template.initialIndent': boolean; + 'vue.format.script.initialIndent': boolean; + 'vue.format.style.initialIndent': boolean; + 'vue.format.wrapAttributes': + | 'auto' + | 'force' + | 'force-aligned' + | 'force-expand-multiline' + | 'aligned-multiple' + | 'preserve' + | 'preserve-aligned'; +} + +export interface ConfigShorthandMap { + traceServer: 'vue.trace.server'; + serverIncludeLanguages: 'vue.server.includeLanguages'; + codeActionsAskNewComponentName: 'vue.codeActions.askNewComponentName'; + suggestComponentNameCasing: 'vue.suggest.componentNameCasing'; + suggestPropNameCasing: 'vue.suggest.propNameCasing'; + suggestDefineAssignment: 'vue.suggest.defineAssignment'; + autoInsertDotValue: 'vue.autoInsert.dotValue'; + autoInsertBracketSpacing: 'vue.autoInsert.bracketSpacing'; + inlayHintsDestructuredProps: 'vue.inlayHints.destructuredProps'; + inlayHintsMissingProps: 'vue.inlayHints.missingProps'; + inlayHintsInlineHandlerLeading: 'vue.inlayHints.inlineHandlerLeading'; + inlayHintsOptionsWrapper: 'vue.inlayHints.optionsWrapper'; + inlayHintsVBindShorthand: 'vue.inlayHints.vBindShorthand'; + formatTemplateInitialIndent: 'vue.format.template.initialIndent'; + formatScriptInitialIndent: 'vue.format.script.initialIndent'; + formatStyleInitialIndent: 'vue.format.style.initialIndent'; + formatWrapAttributes: 'vue.format.wrapAttributes'; +} + +export interface ConfigShorthandTypeMap { + traceServer: 'off' | 'messages' | 'verbose'; + serverIncludeLanguages: string[]; + codeActionsAskNewComponentName: boolean; + suggestComponentNameCasing: 'preferKebabCase' | 'preferPascalCase' | 'alwaysKebabCase' | 'alwaysPascalCase'; + suggestPropNameCasing: 'preferKebabCase' | 'preferCamelCase' | 'alwaysKebabCase' | 'alwaysCamelCase'; + suggestDefineAssignment: boolean; + autoInsertDotValue: boolean; + autoInsertBracketSpacing: boolean; + inlayHintsDestructuredProps: boolean; + inlayHintsMissingProps: boolean; + inlayHintsInlineHandlerLeading: boolean; + inlayHintsOptionsWrapper: boolean; + inlayHintsVBindShorthand: boolean; + formatTemplateInitialIndent: boolean; + formatScriptInitialIndent: boolean; + formatStyleInitialIndent: boolean; + formatWrapAttributes: + | 'auto' + | 'force' + | 'force-aligned' + | 'force-expand-multiline' + | 'aligned-multiple' + | 'preserve' + | 'preserve-aligned'; +} + +export interface ConfigItem { + key: T; + default: ConfigKeyTypeMap[T]; +} + +/** + * Configs map registered by `Vue.volar` + */ +export const configs = { + /** + * @key `vue.trace.server` + * @default `"off"` + * @type `string` + */ + traceServer: { + key: 'vue.trace.server', + default: 'off', + } as ConfigItem<'vue.trace.server'>, + /** + * @key `vue.server.includeLanguages` + * @default `["vue"]` + * @type `array` + */ + serverIncludeLanguages: { + key: 'vue.server.includeLanguages', + default: ['vue'], + } as ConfigItem<'vue.server.includeLanguages'>, + /** + * @key `vue.codeActions.askNewComponentName` + * @default `true` + * @type `boolean` + */ + codeActionsAskNewComponentName: { + key: 'vue.codeActions.askNewComponentName', + default: true, + } as ConfigItem<'vue.codeActions.askNewComponentName'>, + /** + * @key `vue.suggest.componentNameCasing` + * @default `"preferPascalCase"` + * @type `string` + */ + suggestComponentNameCasing: { + key: 'vue.suggest.componentNameCasing', + default: 'preferPascalCase', + } as ConfigItem<'vue.suggest.componentNameCasing'>, + /** + * @key `vue.suggest.propNameCasing` + * @default `"preferKebabCase"` + * @type `string` + */ + suggestPropNameCasing: { + key: 'vue.suggest.propNameCasing', + default: 'preferKebabCase', + } as ConfigItem<'vue.suggest.propNameCasing'>, + /** + * @key `vue.suggest.defineAssignment` + * @default `true` + * @type `boolean` + */ + suggestDefineAssignment: { + key: 'vue.suggest.defineAssignment', + default: true, + } as ConfigItem<'vue.suggest.defineAssignment'>, + /** + * @key `vue.autoInsert.dotValue` + * @default `false` + * @type `boolean` + */ + autoInsertDotValue: { + key: 'vue.autoInsert.dotValue', + default: false, + } as ConfigItem<'vue.autoInsert.dotValue'>, + /** + * @key `vue.autoInsert.bracketSpacing` + * @default `true` + * @type `boolean` + */ + autoInsertBracketSpacing: { + key: 'vue.autoInsert.bracketSpacing', + default: true, + } as ConfigItem<'vue.autoInsert.bracketSpacing'>, + /** + * @key `vue.inlayHints.destructuredProps` + * @default `false` + * @type `boolean` + */ + inlayHintsDestructuredProps: { + key: 'vue.inlayHints.destructuredProps', + default: false, + } as ConfigItem<'vue.inlayHints.destructuredProps'>, + /** + * @key `vue.inlayHints.missingProps` + * @default `false` + * @type `boolean` + */ + inlayHintsMissingProps: { + key: 'vue.inlayHints.missingProps', + default: false, + } as ConfigItem<'vue.inlayHints.missingProps'>, + /** + * @key `vue.inlayHints.inlineHandlerLeading` + * @default `false` + * @type `boolean` + */ + inlayHintsInlineHandlerLeading: { + key: 'vue.inlayHints.inlineHandlerLeading', + default: false, + } as ConfigItem<'vue.inlayHints.inlineHandlerLeading'>, + /** + * @key `vue.inlayHints.optionsWrapper` + * @default `false` + * @type `boolean` + */ + inlayHintsOptionsWrapper: { + key: 'vue.inlayHints.optionsWrapper', + default: false, + } as ConfigItem<'vue.inlayHints.optionsWrapper'>, + /** + * @key `vue.inlayHints.vBindShorthand` + * @default `false` + * @type `boolean` + */ + inlayHintsVBindShorthand: { + key: 'vue.inlayHints.vBindShorthand', + default: false, + } as ConfigItem<'vue.inlayHints.vBindShorthand'>, + /** + * @key `vue.format.template.initialIndent` + * @default `true` + * @type `boolean` + */ + formatTemplateInitialIndent: { + key: 'vue.format.template.initialIndent', + default: true, + } as ConfigItem<'vue.format.template.initialIndent'>, + /** + * @key `vue.format.script.initialIndent` + * @default `false` + * @type `boolean` + */ + formatScriptInitialIndent: { + key: 'vue.format.script.initialIndent', + default: false, + } as ConfigItem<'vue.format.script.initialIndent'>, + /** + * @key `vue.format.style.initialIndent` + * @default `false` + * @type `boolean` + */ + formatStyleInitialIndent: { + key: 'vue.format.style.initialIndent', + default: false, + } as ConfigItem<'vue.format.style.initialIndent'>, + /** + * @key `vue.format.wrapAttributes` + * @default `"auto"` + * @type `string` + */ + formatWrapAttributes: { + key: 'vue.format.wrapAttributes', + default: 'auto', + } as ConfigItem<'vue.format.wrapAttributes'>, +}; + +export interface ScopedConfigKeyTypeMap { + 'trace.server': 'off' | 'messages' | 'verbose'; + 'server.includeLanguages': string[]; + 'codeActions.askNewComponentName': boolean; + 'suggest.componentNameCasing': 'preferKebabCase' | 'preferPascalCase' | 'alwaysKebabCase' | 'alwaysPascalCase'; + 'suggest.propNameCasing': 'preferKebabCase' | 'preferCamelCase' | 'alwaysKebabCase' | 'alwaysCamelCase'; + 'suggest.defineAssignment': boolean; + 'autoInsert.dotValue': boolean; + 'autoInsert.bracketSpacing': boolean; + 'inlayHints.destructuredProps': boolean; + 'inlayHints.missingProps': boolean; + 'inlayHints.inlineHandlerLeading': boolean; + 'inlayHints.optionsWrapper': boolean; + 'inlayHints.vBindShorthand': boolean; + 'format.template.initialIndent': boolean; + 'format.script.initialIndent': boolean; + 'format.style.initialIndent': boolean; + 'format.wrapAttributes': + | 'auto' + | 'force' + | 'force-aligned' + | 'force-expand-multiline' + | 'aligned-multiple' + | 'preserve' + | 'preserve-aligned'; +} + +export const scopedConfigs = { + scope: 'vue', + defaults: { + 'trace.server': 'off', + 'server.includeLanguages': ['vue'], + 'codeActions.askNewComponentName': true, + 'suggest.componentNameCasing': 'preferPascalCase', + 'suggest.propNameCasing': 'preferKebabCase', + 'suggest.defineAssignment': true, + 'autoInsert.dotValue': false, + 'autoInsert.bracketSpacing': true, + 'inlayHints.destructuredProps': false, + 'inlayHints.missingProps': false, + 'inlayHints.inlineHandlerLeading': false, + 'inlayHints.optionsWrapper': false, + 'inlayHints.vBindShorthand': false, + 'format.template.initialIndent': true, + 'format.script.initialIndent': false, + 'format.style.initialIndent': false, + 'format.wrapAttributes': 'auto', + } satisfies ScopedConfigKeyTypeMap, +}; + +export interface NestedConfigs { + 'vue': { + 'trace': { + 'server': 'off' | 'messages' | 'verbose'; + }; + 'server': { + 'includeLanguages': string[]; + }; + 'codeActions': { + 'askNewComponentName': boolean; + }; + 'suggest': { + 'componentNameCasing': 'preferKebabCase' | 'preferPascalCase' | 'alwaysKebabCase' | 'alwaysPascalCase'; + 'propNameCasing': 'preferKebabCase' | 'preferCamelCase' | 'alwaysKebabCase' | 'alwaysCamelCase'; + 'defineAssignment': boolean; + }; + 'autoInsert': { + 'dotValue': boolean; + 'bracketSpacing': boolean; + }; + 'inlayHints': { + 'destructuredProps': boolean; + 'missingProps': boolean; + 'inlineHandlerLeading': boolean; + 'optionsWrapper': boolean; + 'vBindShorthand': boolean; + }; + 'format': { + 'template': { + 'initialIndent': boolean; + }; + 'script': { + 'initialIndent': boolean; + }; + 'style': { + 'initialIndent': boolean; + }; + 'wrapAttributes': + | 'auto' + | 'force' + | 'force-aligned' + | 'force-expand-multiline' + | 'aligned-multiple' + | 'preserve' + | 'preserve-aligned'; + }; + }; +} + +export interface NestedScopedConfigs { + 'trace': { + 'server': 'off' | 'messages' | 'verbose'; + }; + 'server': { + 'includeLanguages': string[]; + }; + 'codeActions': { + 'askNewComponentName': boolean; + }; + 'suggest': { + 'componentNameCasing': 'preferKebabCase' | 'preferPascalCase' | 'alwaysKebabCase' | 'alwaysPascalCase'; + 'propNameCasing': 'preferKebabCase' | 'preferCamelCase' | 'alwaysKebabCase' | 'alwaysCamelCase'; + 'defineAssignment': boolean; + }; + 'autoInsert': { + 'dotValue': boolean; + 'bracketSpacing': boolean; + }; + 'inlayHints': { + 'destructuredProps': boolean; + 'missingProps': boolean; + 'inlineHandlerLeading': boolean; + 'optionsWrapper': boolean; + 'vBindShorthand': boolean; + }; + 'format': { + 'template': { + 'initialIndent': boolean; + }; + 'script': { + 'initialIndent': boolean; + }; + 'style': { + 'initialIndent': boolean; + }; + 'wrapAttributes': + | 'auto' + | 'force' + | 'force-aligned' + | 'force-expand-multiline' + | 'aligned-multiple' + | 'preserve' + | 'preserve-aligned'; + }; +} diff --git a/extensions/vscode/package.json b/extensions/vscode/package.json index d7359d7a65..027823a8b5 100644 --- a/extensions/vscode/package.json +++ b/extensions/vscode/package.json @@ -1,11 +1,11 @@ { "private": true, "name": "volar", - "version": "2.2.8", + "version": "3.0.0", "repository": { "type": "git", "url": "https://github.com/vuejs/language-tools.git", - "directory": "packages/vscode" + "directory": "extensions/vscode" }, "categories": [ "Programming Languages" @@ -13,8 +13,8 @@ "sponsor": { "url": "https://github.com/sponsors/johnsoncodehk" }, - "icon": "images/icon.png", - "displayName": "Vue - Official", + "icon": "icon.png", + "displayName": "Vue (Official)", "description": "Language Support for Vue", "author": "johnsoncodehk", "publisher": "Vue", @@ -26,7 +26,7 @@ "onLanguage:markdown", "onLanguage:html" ], - "main": "./client.js", + "main": "./dist/extension.js", "browser": "./web.js", "capabilities": { "virtualWorkspaces": { @@ -37,28 +37,15 @@ "contributes": { "jsonValidation": [ { - "fileMatch": "tsconfig.json", - "url": "./dist/schemas/vue-tsconfig.schema.json" - }, - { - "fileMatch": "tsconfig-*.json", - "url": "./dist/schemas/vue-tsconfig.schema.json" - }, - { - "fileMatch": "tsconfig.*.json", - "url": "./dist/schemas/vue-tsconfig.schema.json" - }, - { - "fileMatch": "jsconfig.json", - "url": "./dist/schemas/vue-tsconfig.schema.json" - }, - { - "fileMatch": "jsconfig-*.json", - "url": "./dist/schemas/vue-tsconfig.schema.json" - }, - { - "fileMatch": "jsconfig.*.json", - "url": "./dist/schemas/vue-tsconfig.schema.json" + "fileMatch": [ + "tsconfig.json", + "tsconfig.*.json", + "tsconfig-*.json", + "jsconfig.json", + "jsconfig.*.json", + "jsconfig-*.json" + ], + "url": "./schemas/vue-tsconfig.schema.json" } ], "languages": [ @@ -252,35 +239,7 @@ "verbose" ], "default": "off", - "description": "Traces the communication between VS Code and the language server." - }, - "vue.server.hybridMode": { - "type": [ - "boolean", - "string" - ], - "default": "auto", - "enum": [ - "auto", - "typeScriptPluginOnly", - true, - false - ], - "enumDescriptions": [ - "Automatically detect and enable TypeScript Plugin/Hybrid Mode in a safe environment.", - "Only enable Vue TypeScript Plugin but disable Hybrid Mode.", - "Enable TypeScript Plugin/Hybrid Mode.", - "Disable TypeScript Plugin/Hybrid Mode." - ], - "description": "Vue language server only handles CSS and HTML language support, and tsserver takes over TS language support via TS plugin." - }, - "vue.server.compatibleExtensions": { - "type": "array", - "items": { - "type": "string" - }, - "default": [], - "description": "Set compatible extensions to skip automatic detection of Hybrid Mode." + "markdownDescription": "%configuration.trace.server%" }, "vue.server.includeLanguages": { "type": "array", @@ -289,152 +248,102 @@ }, "default": [ "vue" - ] - }, - "vue.server.maxOldSpaceSize": { - "type": [ - "number", - "null" ], - "default": null, - "description": "Set --max-old-space-size option on server process. If you have problem on frequently \"Request textDocument/** failed.\" error, try setting higher memory(MB) on it." - }, - "vue.doctor.status": { - "type": "boolean", - "default": true, - "description": "Show known problems in status bar." - }, - "vue.splitEditors.icon": { - "type": "boolean", - "default": false, - "description": "Show split editor icon in title area of editor." - }, - "vue.splitEditors.layout.left": { - "type": "array", - "items": { - "type": "string" - }, - "default": [ - "script", - "scriptSetup", - "styles" - ] - }, - "vue.splitEditors.layout.right": { - "type": "array", - "items": { - "type": "string" - }, - "default": [ - "template", - "customBlocks" - ] - }, - "vue.updateImportsOnFileMove.enabled": { - "type": "boolean", - "default": true, - "description": "Enabled update imports on file move." - }, - "vue.codeActions.enabled": { - "type": "boolean", - "default": true, - "description": "Enabled code actions." + "markdownDescription": "%configuration.server.includeLanguages%" }, "vue.codeActions.askNewComponentName": { "type": "boolean", "default": true, - "description": "Ask for new component name when extract component." + "markdownDescription": "%configuration.codeActions.askNewComponentName%" }, - "vue.codeLens.enabled": { - "type": "boolean", - "default": true, - "description": "Enabled code lens." - }, - "vue.complete.casing.tags": { + "vue.suggest.componentNameCasing": { "type": "string", "enum": [ - "autoKebab", - "autoPascal", - "kebab", - "pascal" + "preferKebabCase", + "preferPascalCase", + "alwaysKebabCase", + "alwaysPascalCase" ], "enumDescriptions": [ - "Auto Detect from Content (Fallback to if detect failed)", - "Auto Detect from Content (Fallback to if detect failed)", - "", - "" + "Prefer kebab-case (lowercase with hyphens, e.g. my-component)", + "Prefer PascalCase (UpperCamelCase, e.g. MyComponent)", + "Always kebab-case (enforce kebab-case, e.g. my-component)", + "Always PascalCase (enforce PascalCase, e.g. MyComponent)" ], - "default": "autoPascal", - "description": "Preferred tag name case." + "default": "preferPascalCase", + "markdownDescription": "%configuration.suggest.componentNameCasing%" }, - "vue.complete.casing.props": { + "vue.suggest.propNameCasing": { "type": "string", "enum": [ - "autoKebab", - "autoCamel", - "kebab", - "camel" + "preferKebabCase", + "preferCamelCase", + "alwaysKebabCase", + "alwaysCamelCase" ], "enumDescriptions": [ - "Auto Detect from Content (Fallback to :kebab-case=\"...\" if detect failed)", - "Auto Detect from Content (Fallback to :camelCase=\"...\" if detect failed)", - ":kebab-case=\"...\"", - ":camelCase=\"...\"" + "Prefer kebab-case (lowercase with hyphens, e.g. my-prop)", + "Prefer camelCase (lowerCamelCase, e.g. myProp)", + "Always kebab-case (enforce kebab-case, e.g. my-prop)", + "Always camelCase (enforce camelCase, e.g. myProp)" ], - "default": "autoKebab", - "description": "Preferred attr name case." + "default": "preferKebabCase", + "markdownDescription": "%configuration.suggest.propNameCasing%" }, - "vue.complete.defineAssignment": { + "vue.suggest.defineAssignment": { "type": "boolean", "default": true, - "description": "Auto add `const props = ` before `defineProps` when selecting the completion item `props`. (also `emit` and `slots`)" + "markdownDescription": "%configuration.suggest.defineAssignment%" }, "vue.autoInsert.dotValue": { "type": "boolean", "default": false, - "description": "Auto-complete Ref value with `.value`." + "markdownDescription": "%configuration.autoInsert.dotValue%" }, "vue.autoInsert.bracketSpacing": { "type": "boolean", "default": true, - "description": "Auto add space between double curly brackets: {{|}} -> {{ | }}" + "markdownDescription": "%configuration.autoInsert.bracketSpacing%" }, "vue.inlayHints.destructuredProps": { "type": "boolean", "default": false, - "markdownDescription": "Show inlay hints for destructured props:\n\n```ts\nwatch(() => /* props. */foo, () => { ... });\n```" + "markdownDescription": "%configuration.inlayHints.destructuredProps%" }, "vue.inlayHints.missingProps": { "type": "boolean", "default": false, - "markdownDescription": "Show inlay hints for missing required props:\n\n```html\n\n\n```" + "markdownDescription": "%configuration.inlayHints.missingProps%" }, "vue.inlayHints.inlineHandlerLeading": { "type": "boolean", "default": false, - "markdownDescription": "Show inlay hints for event argument in inline handlers:\n\n```html\n */console.log($event)\" />\n```" + "markdownDescription": "%configuration.inlayHints.inlineHandlerLeading%" }, "vue.inlayHints.optionsWrapper": { "type": "boolean", "default": false, - "markdownDescription": "Show inlay hints for component options wrapper for type support:\n\n```vue\n\n```" + "markdownDescription": "%configuration.inlayHints.optionsWrapper%" }, "vue.inlayHints.vBindShorthand": { "type": "boolean", "default": false, - "markdownDescription": "Show inlay hints for v-bind shorthand:\n\n```html\n\n \n```" + "markdownDescription": "%configuration.inlayHints.vBindShorthand%" }, "vue.format.template.initialIndent": { "type": "boolean", - "default": true + "default": true, + "markdownDescription": "%configuration.format.template.initialIndent%" }, - "vue.format.style.initialIndent": { + "vue.format.script.initialIndent": { "type": "boolean", - "default": false + "default": false, + "markdownDescription": "%configuration.format.script.initialIndent%" }, - "vue.format.script.initialIndent": { + "vue.format.style.initialIndent": { "type": "boolean", - "default": false + "default": false, + "markdownDescription": "%configuration.format.style.initialIndent%" }, "vue.format.wrapAttributes": { "type": "string", @@ -447,35 +356,15 @@ "aligned-multiple", "preserve", "preserve-aligned" - ] + ], + "markdownDescription": "%configuration.format.wrapAttributes%" } } }, "commands": [ { "command": "vue.action.restartServer", - "title": "Restart Vue and TS servers", - "category": "Vue" - }, - { - "command": "vue.action.doctor", - "title": "Doctor", - "category": "Vue" - }, - { - "command": "vue.action.writeVirtualFiles", - "title": "Write Virtual Files", - "category": "Vue (Debug)" - }, - { - "command": "vue.action.splitEditors", - "title": "Split \n```", + "configuration.inlayHints.vBindShorthand": "v-bind の省略記法のインレイヒントを表示します:\n\n```html\n\n \n```", + "configuration.format.template.initialIndent": "`