├── .gitignore
├── .vscode
├── launch.json
└── tasks.json
├── .vscodeignore
├── CHANGELOG.md
├── LICENSE
├── LICENSE.txt
├── README-EN.md
├── README.md
├── asset
└── logo.png
├── dist
├── client.js
└── server.js
├── l10n
├── bundle.l10n.json
└── bundle.l10n.zh-CN.json
├── package-lock.json
├── package.json
├── package.nls.json
├── package.nls.zh-CN.json
├── scripts
└── build.js
├── src
├── assist.ts
├── client.ts
├── explorer.ts
├── framework.ts
├── frameworks
│ ├── element-ui
│ │ ├── attribute.ts
│ │ ├── document.ts
│ │ ├── globalAttribute.ts
│ │ ├── index.ts
│ │ ├── jsTag.ts
│ │ └── tag.ts
│ └── index.ts
├── index.vue
├── monitor.ts
├── server.ts
├── util
│ ├── traverse.ts
│ └── util.ts
└── vue
│ ├── snippets-html.ts
│ └── snippets-js.ts
├── tsconfig.build-ci.json
├── tsconfig.build.json
└── tsconfig.json
/.gitignore:
--------------------------------------------------------------------------------
1 | out
2 | node_modules
3 | *.tsbuildinfo
4 | *.vsix
5 | .vscode-test-web
6 | helper.json
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // 使用 IntelliSense 了解相关属性。
3 | // 悬停以查看现有属性的描述。
4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "Launch Client",
9 | "type": "extensionHost",
10 | "request": "launch",
11 | "runtimeExecutable": "${execPath}",
12 | "args": [
13 | // "--disable-extensions",
14 | "--extensionDevelopmentPath=${workspaceRoot}"
15 | ],
16 | "outFiles": [
17 | "${workspaceRoot}/out/**/*.js"
18 | ],
19 | "preLaunchTask": {
20 | "type": "npm",
21 | "script": "dev"
22 | }
23 | }
24 | ]
25 | }
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.0",
3 | "tasks": [
4 | {
5 | "type": "npm",
6 | "script": "compile",
7 | "group": "build",
8 | "presentation": {
9 | "panel": "dedicated",
10 | "reveal": "never"
11 | },
12 | "problemMatcher": [
13 | "$tsc"
14 | ]
15 | },
16 | {
17 | "type": "npm",
18 | "script": "dev",
19 | "isBackground": true,
20 | "group": {
21 | "kind": "build",
22 | "isDefault": true
23 | },
24 | "presentation": {
25 | "panel": "dedicated",
26 | "reveal": "never"
27 | },
28 | "problemMatcher": [
29 | "$tsc-watch"
30 | ]
31 | }
32 | ]
33 | }
--------------------------------------------------------------------------------
/.vscodeignore:
--------------------------------------------------------------------------------
1 | out
2 | scripts
3 | src
4 | tsconfig.build.json
5 | tsconfig.build.tsbuildinfo
6 | meta.json
7 | stats.html
8 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## version 3.5.0
2 | 1. 表单生成器位置调整问题修复
3 | 2. 表单生成器生成代码冲突问题解决
4 | 3. alt + enter生成重复方法处理
5 | 4. 增强api接口生成兼容性
6 |
7 | # version 3.0.0
8 | * 增加Pro功能
9 | * 通过swagger一键生成后端对接接口
10 | 
11 |
12 | # version 2.5.0
13 |
14 | 1. underlying refactoring to completely solve performance problems.
15 | 2. support [Element Plus](/document/element-plus.md) framework,247 code blocks are supported。
16 | 3. resolve issue [71](https://github.com/jiaolong1021/vue-helper/issues/71)、[64](https://github.com/jiaolong1021/vue-helper/issues/64)、
17 | [57](https://github.com/jiaolong1021/vue-helper/issues/57)、[54](https://github.com/jiaolong1021/vue-helper/issues/54)、[40](https://github.com/jiaolong1021/vue-helper/issues/40)
18 |
19 | ## release 2.4.7 notes
20 | resolve issue [14](https://github.com/jiaolong1021/vue-helper/issues/14)
21 | resolve issue [15](https://github.com/jiaolong1021/vue-helper/issues/15)
22 | resolve issue [16](https://github.com/jiaolong1021/vue-helper/issues/16)
23 | optimize block select function.
24 |
25 | ## release 2.4.6 notes
26 | add $refs tips
27 |
28 | ## release 2.4.5 notes
29 | optimize some bug.
30 |
31 | ## release 2.4.4 notes
32 | resolve issue [11](https://github.com/jiaolong1021/vue-helper/issues/11)
33 | optimize some bug.
34 |
35 | ## release 2.4.3 notes
36 | vue file support js tips
37 |
38 | ## release 2.4.0 notes
39 | optimize local file import
40 |
41 | ## release 2.3.8 notes
42 | support local file property and methods tips through showQuickFix (default ctrl + space).
43 | 
44 |
45 | ## release 2.3.7 notes
46 | support local file property and methods tips through showQuickFix (default ctrl + space).
47 | 
48 |
49 | ## release 2.3.6 notes
50 | support router definition jump
51 | 
52 |
53 | ## release 2.3.5 notes
54 | optimize html block select
55 |
56 | ## release 2.3.4 notes
57 | fixed bug
58 | [issue9](https://github.com/jiaolong1021/vue-helper/issues/9)
59 |
60 | ## release 2.3.3 notes
61 | fixed bug
62 | [issue9](https://github.com/jiaolong1021/vue-helper/issues/9)
63 |
64 | ## release 2.3.2 notes
65 | alt + shift + enter to auto import
66 | bug fixed
67 |
68 | ## release 2.3.1 notes
69 | 1. support internal component tips.
70 | alt + shift + enter to auto import
71 | 
72 | 2. support import file tips
73 | 
74 | you can set vue-helper.componentIgnore to ignore files to search component.
75 | set vue-helper.componentPath to assign the search dir.
76 | set vue-helper.componentPrefix to replace prefix in file path.
77 | 
78 |
79 | ## release 2.3.0 notes
80 | support internal component tips.
81 | 
82 | alt + shift + enter to auto import
83 | 
84 |
85 | ## release 2.2.6 notes
86 | support internal component tips.
87 | 
88 |
89 | ## release 2.2.5 notes
90 | solve short cut (backspace) can not delete in seach extention, or search.
91 |
92 | ## release 2.2.4 notes
93 | optimize block selection
94 |
95 | ## release 2.2.3 notes
96 | solve short cut (backspace) can not delete in seach extention, or search.
97 | now only work in editor.
98 |
99 | ## release 2.2.0 notes
100 | 1. optimize block select function.
101 | 2. remove some unused code.
102 |
103 | ## release 2.1.16 notes
104 | optimize jump to definition
105 |
106 | ## release 2.1.15 notes
107 | solve issue #5 [definition jump feature can't jump to async function ](https://github.com/jiaolong1021/vue-helper/issues/5)
108 |
109 | ## release 2.1.14 notes
110 | optimize rem transfer to px
111 |
112 | ## release 2.1.13 notes
113 | optimize some bugs.
114 |
115 | ## release 2.1.11 notes
116 | + enhance element-ui tips.
117 | [el-timeline, el-timeline-item, el-divider, el-calendar, el-image, el-backtop, el-drawer]
118 | + object key value optimize. [alt + shift + enter]
119 | 
120 |
121 | ## release 2.1.10 notes
122 | add common code snippets for vue
123 |
124 | ## release 2.1.9 notes
125 | optimize img, br complete tag
126 |
127 | ## release 2.1.8 notes
128 | remove jquery
129 | add el-table-column tips
130 | optimize block select
131 |
132 | ## release 2.1.7 notes
133 | optimize iview tag,
134 |
135 | ## release 2.1.6 notes
136 | use webpack to Bundle Extension
137 |
138 | ## release 2.1.4 notes
139 | optimize iview tag
140 |
141 | ## release 2.1.3 notes
142 | modify snippets short cut, remove begin with vg or vo. all begin with v
143 |
144 | ## release 2.1.0 notes
145 | add common code snippets for vue
146 | all begin with v
147 | | prefix | vue html snippet |
148 | | --- | --- |
149 | | vfor | v-for="(item, index) in items" :key="index" |
150 | | vcomponent | \\ |
151 | | vka | \\ |
152 | | vtransition | \\ |
153 | | vtg | \\ |
154 | | vrl | \\ |
155 | | vrlt | \\ |
156 | | vrv | \\ |
157 |
158 | | prefix | vue javascript snippet |
159 | | --- | --- |
160 | | vgsilent | Vue.config.silent = true |
161 | | vgeh | Vue.config.errorHandler = function (err, vm, info) {} |
162 | | vgwh | Vue.config.warnHandler = function (msg, vm, trace) {} |
163 | | vgextend | Vue.extend({template: template}) |
164 | | vgset | Vue.set(target, key, value) |
165 | | vgdelete | Vue.delete(target, key) |
166 | | vgdirective | Vue.directive({id, [definition]}) |
167 | | vgfilter | Vue.filter({id, [definition]}) |
168 | | vgcomponent | Vue.component({id, [definition]}) |
169 | | vgnt | Vue.nextTick({}) |
170 | | vguse | Vue.use(plugin) |
171 | | vgmixin | Vue.mixin({mixin}) |
172 | | vgcompile | Vue.compile(template) |
173 | | vodata | data() { return {} } |
174 | | vomounted | mounted () {} |
175 | | vobm | beforeMount () {} |
176 | | vocreated | created () {} |
177 | | vobc | beforeCreate () {} |
178 | | voupdated | updated () {} |
179 | | vobu | beforeUpdate () {} |
180 | | voactivated | activated () {} |
181 | | vodeactivated | deactivated () {} |
182 | | vobd | beforeDestroy () {} |
183 | | vodestroyed | destroyed () {} |
184 | | voprops | props: {} |
185 | | vopd | propsData: {} |
186 | | vocomputed | computed: {} |
187 | | vomethods | methods: {} |
188 | | vowatch | watch: {} |
189 | | vowo | key: { deep: true, immediate: true, handler: function (val, oldVal}) { } } |
190 | | vodirectives | directives: {} |
191 | | vofilters | filters: {} |
192 | | vocomponents | components: {} |
193 | | vomixins | mixins:[] |
194 | | voprovide | provide: {} |
195 | | voinject | inject: [] |
196 | | vomodel | model: {prop: '', event: ''} |
197 | | vorender | render(h) {} |
198 | | vnew | new Vue({}) |
199 | | vnt | this.$nextTick(() => {}) |
200 | | vdata | this.$data |
201 | | vprops| this.$props |
202 | | vel | this.$el |
203 | | voptions | this.$options |
204 | | vparent | this.$parent |
205 | | vroot | this.$root |
206 | | vchildren | this.$children |
207 | | vslots | this.$slots |
208 | | vss | this.$scopedSlots.default({}) |
209 | | vrefs | this.$refs |
210 | | vis | this.$isServer |
211 | | vattrs | this.$attrs |
212 | | vlisteners | this.$listeners |
213 | | vwatch | this.$watch(expOrFn, callback, [opitons]) |
214 | | vset | this.$set(target, key, value) |
215 | | vdelete | this.$delete |
216 | | von | this.$on(event, callback) |
217 | | vonce | this.$once(event, callback) |
218 | | voff | this.$off(event, callback) |
219 | | vemit | this.$emit(event, args) |
220 | | vmount | this.$mount() |
221 | | vfu | this.$forceUpdate() |
222 | | vdestroy | this.$destroy() |
223 |
224 | ## release 2.0.4 notes
225 | optimize block select function.
226 |
227 | ## release 2.0.3 notes
228 | optimize block select function.
229 |
230 | ## release 2.0.1 notes
231 | optimize tips for javascript
232 | 
233 | tips.json
234 | ```
235 | {
236 | "plus": {
237 | "field": {
238 | "ROUTE_SPEASKER": ""
239 | },
240 | "method": {
241 | "getRecorder": {
242 | "params": ["url, id, styles, extras", "url, id, styles"],
243 | "returnType": "object",
244 | "return": "name"
245 | },
246 | "get": {
247 | "params": "url, id, styles, extras",
248 | "returnType": "object",
249 | "return": "name"
250 | }
251 | }
252 | },
253 | "name": {
254 | "field": {
255 | "hello": ""
256 | }
257 | }
258 | }
259 | ```
260 | ### release 2.0.0 notes
261 | support tips for javascript through local json file.
262 | you can config like this:
263 | 
264 | tips result like this:
265 | 
266 |
267 | ### release 1.7.2 notes
268 | go to definition supports auto add index path
269 |
270 | ### release 1.7.1 notes
271 | enhance go to definition in javascript file.
272 | detail config see release 1.4.2 notes
273 |
274 | ### release 1.7.0 notes
275 | 1. optimize choice for code block
276 | 2. add vue html attr select function. shortkey(**alt + x**)
277 | 
278 |
279 | ### release 1.6.12 notes
280 | optimize write snippet
281 |
282 | ### release 1.6.11 notes
283 | optimize write snippet
284 |
285 | ### release 1.6.10 notes
286 | add snippet for pagination and regExp for phone and email [prefix with reg-]
287 |
288 | ### release 1.6.9 notes
289 | add snippets for element-ui
290 |
291 | ### release 1.6.8 notes
292 | optimize jump definition function
293 |
294 | ### release 1.6.7 notes
295 | optimize jump definition function
296 |
297 | ### release 1.6.6 notes
298 | enhance select block ability for css select.
299 |
300 | ### release 1.6.5 notes
301 | support select variable for obj and array. shortkey(**alt + x**)
302 | 
303 |
304 | ### release 1.6.4 notes
305 | optimize select function
306 |
307 | ### release 1.6.3 notes
308 | support global vue component jump
309 |
310 | ### release 1.6.2 notes
311 | fixed relative path jump.
312 |
313 | ### release 1.6.1 notes
314 | fixed relative path jump.
315 |
316 | ### release 1.6.0 notes
317 | add select block fucntion. shortkey(**alt + x**)
318 | now support function, html tag, if, for, while, json, array block select
319 | 
320 |
321 | ### release 1.5.1 notes
322 | fix bug jump to definition with postfix
323 |
324 | ### release 1.5.0 notes
325 | exchange rem to px or exchange px to rem for all file through command
326 | 
327 |
328 | ### release 1.4.5 notes
329 | rem px exchange, shortkey (**alt + z**)
330 | 
331 | rem px exchange setting
332 | 
333 |
334 | ### release 1.4.4 notes
335 | 1. optimize go to definition function
336 | 2. optimize alias setting.
337 |
338 | ### release 1.4.3 notes
339 | 1. support path ignore index
340 | 
341 | 2. iview Input add on-search method tip
342 |
343 | ### release 1.4.2 notes
344 | support add alias through user settings. (use for jump to definition function)
345 | alias support relative path
346 | 
347 | support iview, element-ui tag jump to definition
348 | 
349 |
350 | ### release 1.4.1 notes
351 | solve issue:
352 | [#2 Move to Definition function does not work](https://github.com/jiaolong1021/vue-helper/issues/2)
353 |
354 | ### release 1.4.0 notes
355 | jump to definition function support self define component.
356 | not supoort global component, must import by import or require.
357 | the jump path support begin with @ and relation path
358 | 
359 |
360 | ### release 1.3.10 notes
361 | optimize jump to definition function
362 |
363 | ### release 1.3.9 notes
364 | add iview page snippets. iv-page, iv-page-data, iv-page-method
365 |
366 | ### release 1.3.8 notes
367 | close tag support tag attributu name include .
368 |
369 | ### release 1.3.7 notes
370 | 1. close tag support tag attributu name include : or @
371 | 2. iview split, cell, divider snippet.
372 |
373 | ### release 1.3.6 notes
374 | close tag support tag attributu name include : or @
375 |
376 | ### release 1.3.5 notes
377 | add iview icon tip, optimize close tag function
378 |
379 | ### release 1.3.4 notes
380 | add iview modal snippets
381 |
382 | ### release 1.3.3 notes
383 | add iview form, rules snippets
384 |
385 | ### release 1.3.2 notes
386 | 1. optimize go to definition method
387 | 2. add iview form code snippets.
388 | 
389 |
390 | ### release 1.3.0 notes
391 | property or method go to definiton in current page (keyword: cmd(mac) | ctrl(win))
392 | 
393 |
394 | ### release 1.2.1 notes
395 | optimize backspace handle
396 |
397 | ### release 1.2.0 notes
398 | 1. now support element tag see document through hover.
399 | 
400 | 2. enhance tag close function
401 | 
402 | 3. fix bugs
403 |
404 | ### release 1.1.8 notes
405 | 1. optimize attribute hover
406 | 2. add line empty delete funciton(enter backspace key).
407 | 
408 |
409 | ### release 1.1.5 notes
410 | optimize close tag function、 {{}} function, add change log.
411 |
412 | ### release 1.1.4 notes
413 | optimize columns tip and message snippet show
414 |
415 | ### 1.1.3
416 | ---
417 | auto close tag optinize, table columns attribute tips
418 | 
419 | 
420 |
421 | ### version 1.1.2
422 | #### 1. vue hook function tip
423 | 
424 | #### 2. method completion snippets (keyboard shortcut: alt + shift + enter)
425 | 
426 | #### 3. autoclose html tag
427 | 
428 | #### 4. {{}} completion in vue template
429 | 
430 |
431 | ### version 1.0.0
432 | ## basic functions introduce
433 | ---
434 | ### 1. see document detail through hover tag (**now only support iview**)
435 | 
436 |
437 | ### 2. edit through tag name (friendly tip tag name about framework element-ui
、vux
、iview
)
438 | 
439 |
440 | ### 3. tag attribute tip
441 | 
442 |
443 | ### 4. method tip (tip begin: element -> el-
、iview -> iv-
)
444 | 
445 |
446 | ### questions feedback
447 | if you has any questions or good idea, you can feedback through issue.
448 |
449 | **Enjoy!**
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021-present SHENJIAOLONG
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 - present SHENJIAOLONG Corporation
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README-EN.md:
--------------------------------------------------------------------------------
1 | # vue-helper
2 |
3 |
4 | aim to create a full-link efficiency improvement development tool for VUE development
5 |
6 |
7 | Enhance the VUE development experience. support framework Element-UI、Element-Plus、Ant Design Vue. Enhance your Vue-development experience with visualizations, foundational templates, and rich materials.
8 |
9 | ## Getting Started
10 | Please follow the documentation at [vue-helper](http://vue-helper.80fight.cn/)!
11 |
12 | ## version 3.5.0
13 | 1. jump compatible vetur, Vue-Official
14 | 2. api generate support swagger admin
15 | 3. Variable generation shortcut key is replaced with[alt + enter], you can change it by yourself. [detail](http://vue-helper.80fight.cn/en/document/pro/mv.html)
16 | 4. to solve the problem generate variables with multiple touches
17 |
18 | ## Features
19 | ### 1、framework support element-ui、element-plus、ant-design-vue
20 | 1.1、code block generate
21 |
22 | 1.2、attribute tip
23 |
24 | 1.3、docuement see, or go official
25 |
26 | 2、go to definition of method、variable
27 |
28 | 3、component jump
29 |
30 | 4、expand select [alt + x]
31 |
32 | 5、enhance [alt + enter]
33 |
34 |
35 | more detail see document [vue-helper](http://vue-helper.80fight.cn/)!
36 |
37 | **Enjoy it!**
38 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | *# vue-helper
2 |
3 | [English](https://github.com/jiaolong1021/vue-helper/blob/HEAD/README-EN.md)
4 |
5 |
6 | 旨在打造vue开发的全链路提效开发工具
7 |
8 |
9 | 增强vue开发体验,支持 Element-UI、Element-Plus、Ant Design Vue。通过可视化、基础模板、丰富物料增强 vue 开发。
10 |
11 | ## 好物推荐 ✨✨✨
12 | * AI 编码助手: [阿里-通义灵码](https://developer.aliyun.com/topic/lingma/september?taskCode=18486&recordId=1627dde5f632a75ff7177759fffb99a0)
13 | * 豆包助手: [字节跳动-MarsCode](https://www.marscode.cn/events/s/iSDf7sB6/)
14 | * 创意白板: [博思白板](https://boardmix.cn/invitation/invitee/?code=P6cgeIlptlVu)
15 |
16 | ## vue-helper 能让你获得什么?
17 | vue-helper能让你的开发效率提高30%到100%。
18 |
19 | ## 如何提高效率?
20 | vue-helper具有大量功能,所以想掌握好每个好用的功能,都必须实战才行。
21 |
22 | ## 视频教程
23 | [bilibili地址](https://space.bilibili.com/179666728/channel/seriesdetail?sid=4376604&ctype=0)
24 |
25 | ## 快速开始
26 | 请查看文档 [vue-helper](http://vue-helper.80fight.cn/)!
27 |
28 | ## version 4.2.7
29 | 1. 自定义框架提示
30 |
31 | 2. 打造个人知识库
32 |
33 | 3. 组件导入优化
34 | 4. 性能优化
35 | 5. fixed: [101](https://github.com/jiaolong1021/vue-helper/issues/101)
36 |
37 | ## 功能特性
38 | ### 1、框架支持 element-ui、element-plus、ant-design-vue
39 | 1.1、组件代码块生成
40 |
41 | 1.2、属性提示
42 |
43 | 1.3、文档查看,官网跳转
44 |
45 | 2、变量、方法快速定位
46 | 当你还在使用vue2的时候,这个功能绝对对你有用。
当你在变量或者方法上按住ctrl键时,能够自动定位到相应的定义变量/方法上。
47 |
48 | 3、全局组件跳转
49 | 在工程项目中的vue文件、全局组件等外部组件都支持跳转。
50 |
51 | 4、扩选 [alt + x]
52 | 通过快捷键 [**alt + x**] 扩选代码范围,支持属性、标签、对象、函数扩选
53 |
54 | 5、增强 [alt + enter]
55 |
56 | **Enjoy it!**
57 | *
--------------------------------------------------------------------------------
/asset/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiaolong1021/vue-helper/cd22be4854a2f5e5c10c6f53d4a96e8b7ca07261/asset/logo.png
--------------------------------------------------------------------------------
/dist/server.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | // out/server.js
4 | Object.defineProperty(exports, "__esModule", { value: true });
5 | exports.activate = void 0;
6 | function activate(context) {
7 | console.log("vue-helper activate", context.extension.id);
8 | }
9 | exports.activate = activate;
10 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-helper",
3 | "displayName": "vue-helper",
4 | "version": "4.2.2",
5 | "description": "vue enhance, extension for Element-UI, Element Plus, Ant Desigin Vue",
6 | "keywords": [
7 | "vue-helper",
8 | "element-ui",
9 | "Element Plus",
10 | "Ant",
11 | "Ant Desigin Vue",
12 | "vue",
13 | "vscode"
14 | ],
15 | "icon": "asset/logo.png",
16 | "publisher": "shenjiaolong",
17 | "author": "shenjiaolong",
18 | "homepage": "http://vue-helper.80fight.cn/",
19 | "license": "MIT",
20 | "main": "dist/client",
21 | "l10n": "./l10n",
22 | "directories": {
23 | "lib": "lib",
24 | "test": "__tests__"
25 | },
26 | "publishConfig": {
27 | "registry": "https://registry.npm.taobao.org/"
28 | },
29 | "repository": {
30 | "type": "git",
31 | "url": "https://github.com/jiaolong1021/vue-helper.git"
32 | },
33 | "gitHead": "b1cc40df44b9cd73ba96d8b7f0369cc3ce690c45",
34 | "scripts": {
35 | "dev": "concurrently \"npm:dev-tsc\" \"npm:dev-build\"",
36 | "dev-tsc": "tsc -b tsconfig.build.json -w",
37 | "dev-build": "node scripts/build -- --watch",
38 | "build": "node scripts/build -- --minify",
39 | "release": "npm run build && npm run publish",
40 | "publish": "vsce package",
41 | "test": "echo \"Error: run tests from root\" && exit 1"
42 | },
43 | "engines": {
44 | "vscode": "^1.74.0"
45 | },
46 | "categories": [
47 | "Snippets",
48 | "Other"
49 | ],
50 | "activationEvents": [
51 | "onStartupFinished",
52 | "onLanguage:vue"
53 | ],
54 | "contributes": {
55 | "commands": [
56 | {
57 | "command": "vue-helper.backspace",
58 | "title": "vue-helper: backspace"
59 | },
60 | {
61 | "command": "vue-helper.blockSelect",
62 | "title": "vue-helper: blockSelect"
63 | },
64 | {
65 | "command": "vue-helper.funcEnhance",
66 | "title": "vue-helper: funcEnhance"
67 | },
68 | {
69 | "command": "vue-helper.login",
70 | "title": "vue-helper.login"
71 | },
72 | {
73 | "command": "vue-helper.generateConfig",
74 | "title": "vue-helper.generateConfig"
75 | },
76 | {
77 | "command": "vue-helper.token.clear",
78 | "title": "vue-helper.token.clear"
79 | },
80 | {
81 | "command": "vue-helper.form",
82 | "title": "%extension.form%"
83 | },
84 | {
85 | "command": "vue-helper.upload.block",
86 | "title": "%extension.block%"
87 | },
88 | {
89 | "command": "vue-helper.common.position",
90 | "title": "%common.position%",
91 | "icon": "$(eye)"
92 | },
93 | {
94 | "command": "vue-helper.common.link",
95 | "title": "%common.link%",
96 | "icon": "$(link)"
97 | },
98 | {
99 | "command": "vue-helper.common.refresh",
100 | "title": "%common.refresh%",
101 | "icon": "$(refresh)"
102 | },
103 | {
104 | "command": "vue-helper.add.component",
105 | "title": "%add.component%"
106 | },
107 | {
108 | "command": "vue-helper.add.page",
109 | "title": "%add.page%"
110 | },
111 | {
112 | "command": "vue-helper.upload.component",
113 | "title": "%upload.component%"
114 | },
115 | {
116 | "command": "vue-helper.add.file",
117 | "title": "%add.file%"
118 | }
119 | ],
120 | "views": {
121 | "explorer": [
122 | {
123 | "id": "vue-helper.common",
124 | "name": "%extension.common%",
125 | "icon": "$(json)",
126 | "visibility": "hidden",
127 | "contextualTitle": "vue-helper",
128 | "when": "vue-helper.pro"
129 | }
130 | ]
131 | },
132 | "menus": {
133 | "editor/context": [
134 | {
135 | "command": "vue-helper.form",
136 | "when": "!inOutput && vue-helper.pro",
137 | "group": "navigation"
138 | },
139 | {
140 | "command": "vue-helper.upload.block",
141 | "when": "!inOutput && vue-helper.pro",
142 | "group": "navigation"
143 | }
144 | ],
145 | "view/item/context": [
146 | {
147 | "command": "vue-helper.common.position",
148 | "group": "inline",
149 | "when": "view == vue-helper.common && viewItem == vue-helper.common"
150 | },
151 | {
152 | "command": "vue-helper.common.link",
153 | "group": "inline",
154 | "when": "view == vue-helper.common && viewItem == vue-helper.common"
155 | }
156 | ],
157 | "view/title": [
158 | {
159 | "command": "vue-helper.common.refresh",
160 | "group": "navigation",
161 | "when": "view == vue-helper.common"
162 | }
163 | ],
164 | "explorer/context": [
165 | {
166 | "command": "vue-helper.add.component",
167 | "group": "0_vue-helper",
168 | "when": "vue-helper.pro"
169 | },
170 | {
171 | "command": "vue-helper.add.page",
172 | "group": "0_vue-helper",
173 | "when": "explorerResourceIsFolder && vue-helper.pro"
174 | },
175 | {
176 | "command": "vue-helper.add.file",
177 | "group": "0_vue-helper",
178 | "when": "!explorerResourceIsFolder && vue-helper.self"
179 | },
180 | {
181 | "command": "vue-helper.upload.component",
182 | "group": "1_vue-helper",
183 | "when": "vue-helper.pro"
184 | }
185 | ]
186 | },
187 | "keybindings": [
188 | {
189 | "key": "alt+x",
190 | "command": "vue-helper.blockSelect",
191 | "when": "editorTextFocus"
192 | },
193 | {
194 | "key": "alt+enter",
195 | "command": "vue-helper.funcEnhance",
196 | "when": "editorTextFocus"
197 | },
198 | {
199 | "key": "backspace",
200 | "command": "vue-helper.backspace",
201 | "when": "vue-helper.backspace && textInputFocus && editorLangId =~ /^vue$|^typescript$|^javascript$|^html$|^json$|^css$|^scss$/"
202 | }
203 | ],
204 | "configuration": {
205 | "title": "vue-helper",
206 | "type": "object",
207 | "properties": {
208 | "vue-helper.use-vue-snippets": {
209 | "type": "boolean",
210 | "default": true
211 | },
212 | "vue-helper.show-new-after-upgrade": {
213 | "type": "boolean",
214 | "default": true
215 | },
216 | "vue-helper.config-auto-generate": {
217 | "type": "boolean",
218 | "default": true
219 | },
220 | "vue-helper.component-dir": {
221 | "type": "string",
222 | "default": ""
223 | },
224 | "vue-helper.hub": {
225 | "type": "string",
226 | "default": ""
227 | },
228 | "vue-helper.framework": {
229 | "type": "string",
230 | "default": ""
231 | }
232 | }
233 | }
234 | },
235 | "devDependencies": {
236 | "@types/node": "^20.12.2",
237 | "@types/uuid": "^9.0.0",
238 | "@types/vscode": "^1.74.0",
239 | "@vscode/l10n-dev": "^0.0.35",
240 | "axios": "^0.21.1",
241 | "camelcase": "^7.0.1",
242 | "concurrently": "^8.2.2",
243 | "esbuild": "0.15.18",
244 | "execa": "^6.1.0",
245 | "js-beautify": "^1.15.1",
246 | "jsonc-parser": "^3.2.0",
247 | "jsonwebtoken": "^9.0.2",
248 | "opn": "^6.0.0",
249 | "uuid": "^9.0.0",
250 | "vsce": "latest"
251 | },
252 | "dependencies": {
253 | "@vscode/l10n": "^0.0.18",
254 | "glob": "^10.3.12"
255 | }
256 | }
257 |
--------------------------------------------------------------------------------
/package.nls.json:
--------------------------------------------------------------------------------
1 | {
2 | "extentions.blockSelect": "vue-helper: blockSelect"
3 | }
--------------------------------------------------------------------------------
/package.nls.zh-CN.json:
--------------------------------------------------------------------------------
1 | {
2 | "extentions.blockSelect": "vue-helper: 块选择"
3 | }
--------------------------------------------------------------------------------
/scripts/build.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const fs = require('fs');
3 |
4 | require('esbuild').build({
5 | entryPoints: {
6 | client: './out/client.js',
7 | server: './out/server.js',
8 | },
9 | bundle: true,
10 | metafile: process.argv.includes('--metafile'),
11 | outdir: './dist',
12 | external: [
13 | 'vscode',
14 | 'typescript', // vue-component-meta
15 | ],
16 | format: 'cjs',
17 | platform: 'node',
18 | tsconfig: 'tsconfig.build.json',
19 | define: { 'process.env.NODE_ENV': '"production"' },
20 | minify: process.argv.includes('--minify'),
21 | watch: process.argv.includes('--watch'),
22 | plugins: [
23 | {
24 | name: 'umd2esm',
25 | setup(build) {
26 | build.onResolve({ filter: /^(vscode-.*|estree-walker|jsonc-parser)/ }, args => {
27 | const pathUmdMay = require.resolve(args.path, { paths: [args.resolveDir] })
28 | // Call twice the replace is to solve the problem of the path in Windows
29 | const pathEsm = pathUmdMay.replace('/umd/', '/esm/').replace('\\umd\\', '\\esm\\')
30 | return { path: pathEsm }
31 | })
32 | },
33 | },
34 | ],
35 | }).then(() => {
36 | }).catch(() => process.exit(1))
37 |
--------------------------------------------------------------------------------
/src/assist.ts:
--------------------------------------------------------------------------------
1 | import { commands, window, Position, TextEditor, Selection, workspace, Range } from 'vscode'
2 | import ExplorerProvider from './explorer'
3 |
4 | export default class Assist {
5 | private explorer: ExplorerProvider
6 |
7 | constructor(explorer: ExplorerProvider) {
8 | this.explorer = explorer
9 | }
10 |
11 | public register() {
12 | this.explorer.context.subscriptions.push(commands.registerCommand('vue-helper.blockSelect', () => {
13 | this.blockSelect()
14 | }))
15 | this.explorer.context.subscriptions.push(commands.registerCommand('vue-helper.funcEnhance', () => {
16 | this.funcEnhance()
17 | }))
18 | this.explorer.context.subscriptions.push(commands.registerCommand('vue-helper.backspace', () => {
19 | this.backspce()
20 | }))
21 | this.explorer.setContext('vue-helper.backspace', true)
22 | }
23 |
24 | asNormal(key: string, modifiers?: string) {
25 | switch (key) {
26 | case 'enter':
27 | if (modifiers === 'ctrl') {
28 | return commands.executeCommand('editor.action.insertLineAfter');
29 | } else {
30 | return commands.executeCommand('type', { source: 'keyboard', text: '\n' });
31 | }
32 | case 'tab':
33 | if (workspace.getConfiguration('emmet').get('triggerExpansionOnTab')) {
34 | return commands.executeCommand('editor.emmet.action.expandAbbreviation');
35 | } else if (modifiers === 'shift') {
36 | return commands.executeCommand('editor.action.outdentLines');
37 | } else {
38 | return commands.executeCommand('tab');
39 | }
40 | case 'backspace':
41 | return commands.executeCommand('deleteLeft');
42 | }
43 | }
44 |
45 | // 回退删除处理
46 | async backspce() {
47 | let editor: any = window.activeTextEditor;
48 | if(!editor) {
49 | this.asNormal('backspace');
50 | return;
51 | }
52 | // 多选择点删除处理
53 | if(window.activeTextEditor?.selections.length && window.activeTextEditor?.selections.length > 1) {
54 | let selections = window.activeTextEditor?.selections;
55 | let selectionList: Array = [];
56 | for (let index = 0; index < selections.length; index++) {
57 | const selection = selections[index];
58 | if(selection.start.line === selection.end.line && selection.start.character === selection.end.character) {
59 | if(selection.anchor.character > 0) {
60 | selectionList.push(new Selection(new Position(selection.anchor.line, selection.anchor.character - 1), selection.anchor));
61 | } else if (selection.anchor.line > 0) {
62 | let len = editor.document.lineAt(selection.anchor.line - 1).text.length;
63 | selectionList.push(new Selection(new Position(selection.anchor.line - 1, len), selection.anchor));
64 | }
65 | } else {
66 | selectionList.push(selection);
67 | }
68 | }
69 | await editor.edit((editBuilder: any) => {
70 | for (let i = 0; i < selectionList.length; i++) {
71 | editBuilder.delete(selectionList[i]);
72 | }
73 | });
74 | return;
75 | }
76 | if(window.activeTextEditor?.selection.start.line === window.activeTextEditor?.selection.end.line
77 | && window.activeTextEditor?.selection.start.character === window.activeTextEditor?.selection.end.character) {
78 | // 首行
79 | if(editor.selection.anchor.line === 0) {
80 | if(editor.selection.anchor.character > 0) {
81 | await editor.edit((editBuilder: any) => {
82 | editBuilder.delete(new Selection(new Position(editor.selection.anchor.line, editor.selection.anchor.character - 1), editor.selection.anchor));
83 | });
84 | }
85 |
86 | } else {
87 | let isLineEmpty = editor.document.lineAt(editor.selection.anchor.line).text.trim() === '';
88 | // 整行都是空格
89 | if(isLineEmpty) {
90 | let preText = '';
91 | let line = editor.selection.anchor.line;
92 | while(preText.trim() === '' && line >= 0) {
93 | line -= 1;
94 | preText = editor.document.lineAt(line).text;
95 | }
96 | await editor.edit((editBuilder: any) => {
97 | editBuilder.delete(new Selection(new Position(line, preText.length), editor.selection.anchor));
98 | });
99 | } else {
100 | let startPosition: Position;
101 | let endPosition: Position = editor.selection.anchor;
102 | let preLineText = editor.document.getText(new Range(new Position(endPosition.line, 0), endPosition));
103 | if(endPosition.character === 0 || preLineText.trim() === '') {
104 | startPosition = new Position(endPosition.line - 1, editor.document.lineAt(endPosition.line - 1).text.length);
105 | } else {
106 | startPosition = new Position(endPosition.line, endPosition.character - 1);
107 | // 对{}, (), [], '', "", <>进行成对删除处理
108 | let txt = editor.document.getText(new Range(new Position(endPosition.line, endPosition.character - 1), endPosition));
109 | if(editor.document.lineAt(endPosition.line).text.length > endPosition.character) {
110 | let nextTxt = editor.document.getText(new Range(endPosition, new Position(endPosition.line, endPosition.character + 1)));
111 | if((txt === '{' && nextTxt === '}')
112 | || (txt === '(' && nextTxt === ')')
113 | || (txt === '\'' && nextTxt === '\'')
114 | || (txt === '"' && nextTxt === '"')
115 | || (txt === '[' && nextTxt === ']')
116 | || (txt === '<' && nextTxt === '>')) {
117 | endPosition = new Position(endPosition.line, endPosition.character + 1);
118 | }
119 | }
120 | }
121 | await editor.edit((editBuilder: any) => {
122 | editBuilder.delete(new Selection(startPosition, endPosition));
123 | });
124 | }
125 | }
126 | } else {
127 | // 选择块
128 | this.asNormal('backspace');
129 | }
130 | }
131 |
132 | // 函数增强
133 | public funcEnhance() {
134 | let editor = window.activeTextEditor;
135 | if (!editor) { return; }
136 | let character = (window.activeTextEditor?.selection.anchor.character || 0) - 1;
137 | let txt = window.activeTextEditor?.document.lineAt(window.activeTextEditor?.selection.anchor.line).text;
138 | let word: string = '';
139 | let isWordEnd = false
140 | let type = '' // '0': 变量 '1': 函数
141 | if (txt && txt.includes('"')) {
142 | type = '0'
143 | }
144 | while(txt && character && character > 0) {
145 | let wordSingle = txt[character]
146 | if (wordSingle === '"') {
147 | type = '0'
148 | isWordEnd = true
149 | }
150 | if (wordSingle === ' ') {
151 | break
152 | }
153 | if (!isWordEnd) {
154 | word = wordSingle + word;
155 | }
156 | if (wordSingle === '@') {
157 | type = '1'
158 | }
159 | --character;
160 | }
161 | // 没有参数往后找
162 | character = (window.activeTextEditor?.selection.anchor.character || 0);
163 | while(txt && character && txt.length > character) {
164 | if (txt[character] === '"') {
165 | break;
166 | }
167 | word = word + txt[character];
168 | ++character;
169 | }
170 | // 如果是函数引用方式,则认为是生成函数
171 | if (word.includes(')')) {
172 | type = '1'
173 | }
174 | console.log('type' + type)
175 |
176 | switch (type) {
177 | default:
178 | this.autoEnhance()
179 | break;
180 | }
181 | }
182 |
183 | public autoEnhance() {
184 | let editor: any = window.activeTextEditor;
185 | if (!editor) { return; }
186 | let txt = editor.document.lineAt(editor.selection.anchor.line).text;
187 | if(editor.document.lineCount <= editor.selection.anchor.line + 1) { return; }
188 | // 组件自动导入
189 | if (/<.*>\s?<\/.*>/gi.test(txt.trim()) || /<.*\/>/gi.test(txt.trim())) {
190 | this.autoImportComponent(txt, editor, editor.selection.anchor.line);
191 | return;
192 | }
193 | // 本地文件自动导入
194 | let nextLineTxt = editor.document.lineAt(editor.selection.anchor.line + 1).text;
195 |
196 | let baseEmpty = txt.replace(/(\s)\S.*/gi, '$1');
197 | let replaceTxt = ` {\n${baseEmpty}${this.explorer.tabSize}\n${baseEmpty}}`;
198 | // 本行全是空
199 | if(/^\s*$/gi.test(txt) || txt === '') {
200 | replaceTxt = 'name (params)' + replaceTxt;
201 | } else if (/[0-9a-zA-Z]\s{0,1}:\s{0,1}[\w\"\']/gi.test(txt)) {
202 | // key: value
203 | replaceTxt = ',\n' + baseEmpty;
204 | } else if(txt.indexOf(')') === -1) {
205 | replaceTxt = ' (params)' + replaceTxt;
206 | }
207 | // 判断下一行是否是单行注释
208 | if(/\s*\/\/\s+.*/gi.test(nextLineTxt)) {
209 | if(editor.document.lineCount <= editor.selection.anchor.line + 2) { return; }
210 | nextLineTxt = editor.document.lineAt(editor.selection.anchor.line + 2).text;
211 | }
212 | // 下一行是一个函数
213 | if (/.*(.*).*{.*/gi.test(nextLineTxt)) {
214 | let isCond = false;
215 | let txtTrim = txt.trim();
216 | const condList = ['if', 'for', 'while', 'switch'];
217 | condList.forEach(cond => {
218 | if (txtTrim.indexOf(cond) === 0) {
219 | isCond = true;
220 | }
221 | });
222 | if (!isCond) {
223 | replaceTxt += ',';
224 | }
225 | }
226 | editor.edit((editBuilder: any) => {
227 | editBuilder.insert(new Position(editor.selection.anchor.line, txt.length + 1), replaceTxt);
228 | });
229 | let newPosition = editor.selection.active.translate(1, (baseEmpty + this.explorer.tabSize).length);
230 | editor.selection = new Selection(newPosition, newPosition);
231 | }
232 |
233 | // 组件自动导入
234 | autoImportComponent(txt: string, editor: TextEditor, line: number) {
235 | let tag = txt.trim().replace(/<([\w-]*)[\s>].*/gi, '$1');
236 | for (let i = 0; i < this.explorer.vueFiles.length; i++) {
237 | const vf : any = this.explorer.vueFiles[i];
238 | if (tag === vf.name) {
239 | let name = vf.name;
240 | // 不重复插入引入
241 | if (editor.document.getText().includes(`import ${name}`)) {
242 | return
243 | }
244 | let countLine = editor.document.lineCount;
245 | // 找script位置
246 | while (!/^\s*\s*$/.test(editor.document.lineAt(line).text)) {
247 | if (countLine > line) {
248 | line++;
249 | } else {
250 | break;
251 | }
252 | }
253 | let activeEditorPath = this.explorer.getActiveEditorDir(editor.document.uri.path)
254 | let importString = `import ${name} from '${this.explorer.getVueRelativePath(activeEditorPath, vf.path)}'\n`;
255 | if (editor.document.lineAt(line).text.includes('setup')) {
256 | // 组合式
257 | editor.edit((editBuilder) => {
258 | importString = importString.replace(/\\/gi, '/');
259 | editBuilder.insert(new Position(line + 1, 0), importString);
260 | });
261 | return
262 | }
263 | if (editor.document.lineAt(line + 1).text.includes('export default')) {
264 | line += 1;
265 | } else {
266 | line += 1;
267 | if (countLine < line) {
268 | return;
269 | }
270 | // 找import位置
271 | while (/import /gi.test(editor.document.lineAt(line).text.trim())) {
272 | if (countLine > line) {
273 | line++;
274 | } else {
275 | break;
276 | }
277 | }
278 | }
279 | let importLine = line;
280 | if (line < countLine) {
281 | let prorityInsertLine = 0;
282 | let secondInsertLine = 0;
283 | let hasComponents = false;
284 | let baseEmpty = '';
285 | while(!/\s*<\/script>\s*/gi.test(editor.document.lineAt(line).text.trim())) {
286 | if (/\s*components\s*:\s*{.*}.*/gi.test(editor.document.lineAt(line).text.trim())) {
287 | let text = editor.document.lineAt(line).text;
288 | let preText = text.replace(/\s*}.*$/, '');
289 | let insertPos = preText.length;
290 | editor.edit((editBuilder) => {
291 | importString = importString.replace(/\\/gi, '/');
292 | editBuilder.insert(new Position(importLine, 0), importString);
293 | editBuilder.insert(new Position(line, insertPos), ', ' + name);
294 | });
295 | break;
296 | }
297 | if (hasComponents && /\s*},?\s*$/gi.test(editor.document.lineAt(line).text.trim())) {
298 | let text = editor.document.lineAt(line - 1).text;
299 | let insertPos = text.indexOf(text.trim());
300 | let empty = '';
301 | for (let i = 0; i < insertPos; i++) {
302 | empty += ' ';
303 | }
304 | editor.edit((editBuilder) => {
305 | importString = importString.replace(/\\/gi, '/');
306 | editBuilder.insert(new Position(importLine, 0), importString);
307 | editBuilder.insert(new Position(line - 1, editor.document.lineAt(line - 1).text.length), ',\n' + empty + name);
308 | });
309 | break;
310 | }
311 | if (/\s*components\s*:\s*{\s*$/gi.test(editor.document.lineAt(line).text.trim())) {
312 | hasComponents = true;
313 | }
314 | if (/\s*export\s*default\s*{\s*/gi.test(editor.document.lineAt(line).text.trim())) {
315 | secondInsertLine = line;
316 | }
317 | if (/\s*data\s*\(\s*\)\s*{\s*/gi.test(editor.document.lineAt(line).text.trim())) {
318 | let text = editor.document.lineAt(line).text;
319 | let insertPos = text.indexOf(text.trim());
320 | for (let i = 0; i < insertPos; i++) {
321 | baseEmpty += '';
322 | }
323 | prorityInsertLine = line;
324 | }
325 | if (countLine > line) {
326 | line++;
327 | } else {
328 | break;
329 | }
330 | }
331 | if (prorityInsertLine > 0) {
332 | editor.edit((editBuilder) => {
333 | importString = importString.replace(/\\/gi, '/');
334 | editBuilder.insert(new Position(importLine - 1, 0), importString);
335 | editBuilder.insert(new Position(prorityInsertLine - 1, editor.document.lineAt(prorityInsertLine - 1).text.length), `\n\t${baseEmpty}components: { ${name} },`);
336 | });
337 | break;
338 | }
339 | if (secondInsertLine > 0) {
340 | editor.edit((editBuilder) => {
341 | importString = importString.replace(/\\/gi, '/');
342 | editBuilder.insert(new Position(importLine, 0), importString);
343 | editBuilder.insert(new Position(secondInsertLine, editor.document.lineAt(secondInsertLine).text.length), `\n${this.explorer.tabSize}components: { ${name} },`);
344 | });
345 | }
346 | }
347 |
348 | break;
349 | }
350 | }
351 | }
352 |
353 | // 代码块选择
354 | public blockSelect() {
355 | let editor = window.activeTextEditor;
356 | if(!editor) { return; }
357 |
358 | let startPosition = editor.selection.start;
359 | let lineTextObj = editor.document.lineAt(startPosition.line);
360 | let lineText = lineTextObj.text;
361 | if (lineText.length > 0 && startPosition.character === 0 && lineText[startPosition.character] === '[') {
362 | this.selectJsBlock(editor, lineText.substring(startPosition.character, lineText.length), startPosition, 'array');
363 | } else if (lineText.length > 0 && startPosition.character > 0 && lineText[startPosition.character - 1] === '[') {
364 | this.selectJsBlock(editor, lineText.substring(startPosition.character - 1, lineText.length), new Position(startPosition.line, startPosition.character - 1), 'array');
365 | } else if (lineText.length > 0 && startPosition.character < lineText.length && lineText[startPosition.character] === '[') {
366 | this.selectJsBlock(editor, lineText.substring(startPosition.character, lineText.length), startPosition, 'array');
367 | } else if (lineText.length > 0 && startPosition.character === 0 && lineText[startPosition.character] === '{') {
368 | this.selectJsBlock(editor, lineText.substring(startPosition.character, lineText.length), startPosition, 'json');
369 | } else if (lineText.length > 0 && startPosition.character > 0 && lineText[startPosition.character - 1] === '{') {
370 | this.selectJsBlock(editor, lineText.substring(startPosition.character - 1, lineText.length), new Position(startPosition.line, startPosition.character - 1), 'json');
371 | } else if (lineText.length > 0 && startPosition.character < lineText.length && lineText[startPosition.character] === '{') {
372 | this.selectJsBlock(editor, lineText.substring(startPosition.character, lineText.length), startPosition, 'json');
373 | } else if (lineText.trim().length > 0 && lineText.trim()[0] === '<' && startPosition.character <= lineText.indexOf('<')) {
374 | lineText = lineText.substring(startPosition.character, lineText.length);
375 | this.selectHtmlBlock(editor, lineText, startPosition);
376 | } else if (lineText.trim().length > 0 && lineText.trim()[0] === '<' && startPosition.character <= lineText.indexOf('<')) {
377 | lineText = lineText.substring(startPosition.character, lineText.length);
378 | this.selectHtmlBlock(editor, lineText, startPosition);
379 | } else if (/^\s*[\sa-zA-Z:_-]*\s*\[\s*$/gi.test(lineText)) {
380 | this.selectJsBlock(editor, lineText, new Position(startPosition.line, lineText.length - lineText.replace(/\s*/, '').length), 'array');
381 | } else if ((lineText.trim().length > 0 && /(function|if|for|while)?.+\(.*\)\s*{/gi.test(lineText) && /^\s*(function|if|for|while)?\s*$/g.test(lineText.substr(0, startPosition.character)))
382 | || (/^(\s*[\sa-zA-Z_-]*\([\sa-zA-Z_-]*\)\s*{\s*)|(\s*[\sa-zA-Z:_-]*\s*{\s*)$/gi.test(lineText)) && /^\s*(function|if|for|while)?\s*$/g.test(lineText.substr(0, startPosition.character))) {
383 | this.selectJsBlock(editor, lineText, new Position(startPosition.line, lineText.length - lineText.replace(/\s*/, '').length), 'function');
384 | } else {
385 | // 在本行选择
386 | this.selectLineBlock(editor, lineText, startPosition);
387 | }
388 | return ;
389 | }
390 |
391 | // 选择函数块
392 | selectJsBlock(editor: any, lineText: string, startPosition: Position, type: string) {
393 | let lineCount = editor.document.lineCount;
394 | let lineCurrent = startPosition.line;
395 | let braceLeftCount = 0;
396 | let tagLeft = '{';
397 | let tagRight = '}';
398 | if (type === 'array') {
399 | tagLeft = '[';
400 | tagRight = ']';
401 | }
402 | while(lineCurrent <= lineCount) {
403 | let pos: number = 0;
404 | while((lineText.indexOf(tagLeft, pos) !== -1 || lineText.indexOf(tagRight, pos) !== -1) && pos < lineText.length) {
405 | let i = -1;
406 | // 左标签
407 | if (lineText.indexOf(tagLeft, pos) !== -1) {
408 | i = lineText.indexOf(tagLeft, pos);
409 | }
410 | // 右标签
411 | if (lineText.indexOf(tagRight, pos) !== -1) {
412 | if (i === -1 || i > lineText.indexOf(tagRight, pos)) {
413 | // 左标签不存在、左右标签都存在,右标签在前
414 | --braceLeftCount;
415 | pos = lineText.indexOf(tagRight, pos) + 1;
416 | } else {
417 | ++braceLeftCount;
418 | pos = i + 1;
419 | }
420 | } else {
421 | // 存在左标签
422 | if (i !== -1) {
423 | ++braceLeftCount;
424 | pos = i + 1;
425 | }
426 | }
427 | if (braceLeftCount === 0) {
428 | break;
429 | }
430 | }
431 |
432 | if (braceLeftCount === 0) {
433 | let extra = 0;
434 | let textExtra = editor.document.lineAt(lineCurrent).text;
435 | if (lineCurrent === startPosition.line) {
436 | extra = textExtra.indexOf(lineText);
437 | }
438 | if (type === 'function' && textExtra[pos + extra - 1] === '}' && textExtra[pos + extra] === ')') {
439 | extra += 1;
440 | }
441 | editor.selection = new Selection(startPosition, new Position(lineCurrent, pos + extra));
442 | return;
443 | }
444 |
445 | ++lineCurrent;
446 | if (lineCount >= lineCurrent) {
447 | lineText = editor.document.lineAt(lineCurrent).text;
448 | }
449 | }
450 | return;
451 | }
452 |
453 | // 选择html代码块
454 | selectHtmlBlock(editor: any, lineText: string, startPosition: Position) {
455 | const ncname = '[a-zA-Z_][\\w\\-\\.]*';
456 | const qnameCapture = '((?:' + ncname + '\\:)?' + ncname + ')';
457 | const startTagOpen = new RegExp('^<' + qnameCapture);
458 | const endTag = new RegExp('^(<\\/' + qnameCapture + '[^>]*>)');
459 | const comment = /^';
461 | const lineCount = editor.document.lineCount;
462 | let lineCurrent = startPosition.line;
463 |
464 | let isNoIncludeTag = false;
465 | let tagStack: any = null;
466 | let col = lineText.indexOf(lineText.trim()) + startPosition.character;
467 | let beginPosition = new Position(startPosition.line, startPosition.character + lineText.length - lineText.replace(/\s*(.*)/, '$1').length);
468 | lineText = lineText.trim();
469 | let noIncludeTags = ['input', 'img'];
470 |
471 | while(lineText) {
472 | let textTagPos = lineText.indexOf('<');
473 | if (textTagPos === 0) {
474 | let hasEndTag = false;
475 | let hasTag = false;
476 | if (comment.test(lineText)) {
477 | let commentIndex = lineText.indexOf(commentEnd);
478 | while(commentIndex === -1 && lineCurrent < lineCount) {
479 | lineText = editor.document.lineAt(++lineCurrent).text;
480 | commentIndex = lineText.indexOf(commentEnd);
481 | }
482 | lineText = lineText.substr(commentIndex + 3, lineText.length);
483 | }
484 | const endTagMatch = lineText.match(endTag);
485 | if (endTagMatch) {
486 | hasEndTag = true;
487 | if (Array.isArray(tagStack)) {
488 | let tagIndex = tagStack.length;
489 | if (tagIndex > 0) {
490 | let isTagMatch = false;
491 | while(tagIndex > 0 && !isTagMatch) {
492 | let tag = tagStack[tagIndex - 1];
493 | if (tag === endTagMatch[2]) {
494 | isTagMatch = true;
495 | }
496 | tagStack.pop();
497 | --tagIndex;
498 | }
499 | }
500 | }
501 | let endAdvance = lineText.indexOf(endTagMatch[1]) + endTagMatch[1].length;
502 | col += endAdvance;
503 | lineText = lineText.substr(endAdvance, lineText.length);
504 | }
505 |
506 | if (Array.isArray(tagStack) && tagStack.length === 0) {
507 | editor.selection = new Selection(beginPosition, new Position(lineCurrent, col));
508 | break;
509 | }
510 |
511 | const startTagMatch = lineText.match(startTagOpen);
512 | if (startTagMatch) {
513 | hasTag = true;
514 | if (isNoIncludeTag) {
515 | let lineTextCur = editor.document.lineAt(lineCurrent).text;
516 | lineText = lineTextCur.substr(0, col);
517 | let indexLast = lineText.lastIndexOf('>');
518 | while (indexLast === -1 && lineCurrent > 0) {
519 | --lineCurrent;
520 | lineText = editor.document.lineAt(lineCurrent).text;
521 | indexLast = lineText.lastIndexOf('>');
522 | }
523 | editor.selection = new Selection(beginPosition, new Position(lineCurrent, indexLast + 2));
524 | break;
525 | }
526 | if (Array.isArray(tagStack)) {
527 | tagStack.push(startTagMatch[1]);
528 | } else {
529 | tagStack = [startTagMatch[1]];
530 | if (noIncludeTags.indexOf(startTagMatch[1]) !== -1) {
531 | isNoIncludeTag = true;
532 | }
533 | }
534 | const startAdvance = lineText.indexOf(startTagMatch[1]) + startTagMatch[1].length;
535 | col += startAdvance;
536 | lineText = lineText.substr(startAdvance, lineText.length);
537 | }
538 | if (lineText.indexOf('/>') !== -1 && Array.isArray(tagStack) && tagStack.length === 1) {
539 | let tagCloseIndex = lineText.indexOf('/>');
540 | let prevText = lineText.substr(0, tagCloseIndex + 2);
541 | let tagReg = /<([\w-]+)(\s*|(\s+[\w-_:@\.]+(=("[^"]*"|'[^']*'))?)+)\s*(\/)?>/gim;
542 | if (!tagReg.test(prevText)) {
543 | tagStack.pop();
544 | }
545 | editor.selection = new Selection(beginPosition, new Position(lineCurrent, col + tagCloseIndex + 2));
546 | break;
547 | }
548 | if (!lineText && lineCurrent < lineCount && tagStack.length > 0) {
549 | do {
550 | ++lineCurrent;
551 | lineText = editor.document.lineAt(lineCurrent).text;
552 | } while (!lineText && lineCurrent < lineCount);
553 | col = lineText.indexOf(lineText.trim());
554 | lineText = lineText.trim();
555 | continue;
556 | }
557 | if (!hasTag && !hasEndTag && lineText.length > 0) {
558 | let noTagIndex = lineText.indexOf(lineText, 1);
559 | if (noTagIndex === -1) {
560 | if (lineCurrent < lineCount) {
561 | do {
562 | ++lineCurrent;
563 | lineText = editor.document.lineAt(lineCurrent).text;
564 | } while (!lineText && lineCurrent < lineCount);
565 | col = lineText.indexOf(lineText.trim());
566 | lineText = lineText.trim();
567 | } else {
568 | break;
569 | }
570 | } else {
571 | lineText = lineText.substr(noTagIndex, lineText.length);
572 | }
573 | }
574 | } else if (textTagPos > 0) {
575 | lineText = lineText.substr(textTagPos, lineText.length);
576 | col += textTagPos;
577 | } else if (textTagPos < 0) {
578 | if (lineCurrent < lineCount) {
579 | // 一行最前面是否有 />
580 | if (lineText.indexOf('/>') !== -1 && Array.isArray(tagStack) && tagStack.length > 0) {
581 | let tagCloseIndex = lineText.indexOf('/>');
582 | let prevText = lineText.substr(0, tagCloseIndex + 2);
583 | let tagReg = /<([\w-]+)(\s*|(\s+[\w-_:@\.]+(=("[^"]*"|'[^']*'))?)+)\s*(\/)?>/gim;
584 | if (!tagReg.test(prevText)) {
585 | tagStack.pop();
586 | }
587 | if(tagStack.length === 0) {
588 | editor.selection = new Selection(beginPosition, new Position(lineCurrent, col + tagCloseIndex + 2));
589 | break;
590 | }
591 | }
592 | do {
593 | ++lineCurrent;
594 | lineText = editor.document.lineAt(lineCurrent).text;
595 | if (lineText.replace(/\s/gi, '') === '') {
596 | lineText = '';
597 | }
598 | } while (!lineText && lineCurrent < lineCount);
599 | col = lineText.indexOf(lineText.trim());
600 | lineText = lineText.trim();
601 | } else {
602 | lineText = '';
603 | }
604 | }
605 | }
606 | }
607 |
608 | selectLineBlock(editor: TextEditor, lineText: String, startPosition: Position) {
609 | // "" '' () {}, >< 空格
610 | // 1. 遍历左侧查询结束标签
611 | let TAGS = ["\"", "'", "(", "{", "[", " ", "`", ">"];
612 | let TAGS_CLOSE: any = {
613 | "\"": "\"",
614 | "'": "'",
615 | "(": ")",
616 | "{": "}",
617 | "[": "]",
618 | " ": " ",
619 | "`": "`",
620 | ">": "<"
621 | };
622 | let pos = startPosition.character - 1;
623 | let endTag = '',
624 | beginPos = 0,
625 | endPos = 0,
626 | inBeginTags: any [] = [],
627 | includeTags = false;
628 | beginPos = pos;
629 | while (pos >= 0) {
630 | if (TAGS.indexOf(lineText[pos]) !== -1) {
631 | endTag = lineText[pos];
632 | break;
633 | }
634 | --pos;
635 | }
636 | if (beginPos === pos) {
637 | includeTags = true;
638 | beginPos = pos;
639 | } else {
640 | beginPos = pos + 1;
641 | }
642 | // 存在结束标签
643 | if (endTag.length > 0) {
644 | pos = startPosition.character;
645 | if (endTag === '>') {
646 | while (pos <= lineText.length && pos >= 0) {
647 | let txt = lineText[pos];
648 | if ((txt === TAGS_CLOSE[endTag] || txt === '>') && pos > beginPos) {
649 | break;
650 | }
651 | ++pos;
652 | }
653 | } else {
654 | while (pos <= lineText.length && pos >= 0) {
655 | let txt = lineText[pos];
656 | if (inBeginTags.length === 0 && (txt === TAGS_CLOSE[endTag] || txt === '>') && pos > beginPos) {
657 | break;
658 | }
659 | if (inBeginTags.length > 0 && TAGS_CLOSE[inBeginTags[inBeginTags.length - 1]] === txt) {
660 | inBeginTags.pop();
661 | } else if (TAGS.indexOf(txt) !== -1 && txt !== ' ') {
662 | inBeginTags.push(txt);
663 | }
664 |
665 | ++pos;
666 | }
667 | }
668 | }
669 | includeTags ? (endPos = pos + 1) : (endPos = pos);
670 | editor.selection = new Selection(new Position(startPosition.line, beginPos), new Position(startPosition.line, endPos));
671 | }
672 | }
--------------------------------------------------------------------------------
/src/client.ts:
--------------------------------------------------------------------------------
1 | import { ExtensionContext } from 'vscode';
2 | import ExplorerProvider from './explorer';
3 | import FrameworkProvider from './framework';
4 | import Assist from './assist';
5 | import MonitorProvider from './monitor'
6 |
7 | export function activate(context: ExtensionContext) {
8 | init(context)
9 | }
10 |
11 | function init(context: ExtensionContext) {
12 | const explorer = new ExplorerProvider(context)
13 |
14 | const framework = new FrameworkProvider(explorer)
15 | framework.register()
16 |
17 | const assist = new Assist(explorer)
18 | assist.register()
19 |
20 | new MonitorProvider(explorer)
21 | }
--------------------------------------------------------------------------------
/src/explorer.ts:
--------------------------------------------------------------------------------
1 | import { ExtensionContext, workspace, ConfigurationTarget, commands, window, StatusBarAlignment, TextDocument } from 'vscode'
2 | import { getTabSize, getWorkspaceRoot, winRootPathHandle } from './util/util'
3 | import Traverse from './util/traverse'
4 | import * as path from 'path'
5 | import * as fs from 'fs'
6 |
7 | export interface Prefix {
8 | alias: string
9 | path: string
10 | }
11 |
12 | export default class ExplorerProvider {
13 | // 全局变量
14 | public name: string = 'vue-helper'
15 | public context: ExtensionContext
16 | // 工程根目录
17 | public projectRootPath: string = ''
18 | public projectRootPathReg: RegExp
19 | public tabSize = ''
20 | public traverse: Traverse
21 | public isTs = false
22 | public prefix: Prefix = {
23 | alias: '@',
24 | path: 'src'
25 | }
26 | public vueFiles: any = []
27 | public store = {
28 | set: (key: string, value: any) => {
29 | workspace.getConfiguration(this.name).update(key, value, ConfigurationTarget.Global);
30 | },
31 | get: (key: string) => {
32 | return workspace.getConfiguration(this.name).get(key);
33 | }
34 | }
35 | public inits: any[] = []
36 |
37 | public setContext(name: string, value: boolean) {
38 | commands.executeCommand('setContext', name, value);
39 | }
40 |
41 | public getActiveEditorDir(activePath: string, ) {
42 | return activePath.replace(this.projectRootPathReg, '').replace(/[\/|\\]\w*\.\w*$/gi, '')
43 | }
44 |
45 | public getActiveEditorPath(activePath: string, ) {
46 | return activePath.replace(this.projectRootPathReg, '')
47 | }
48 |
49 | public getVueRelativePath(activeEditorPath: string, vuePath: string) {
50 | let vfPath = path.relative(activeEditorPath, vuePath)
51 | vfPath = './' + vfPath
52 | return vfPath.replace(/\\/gi, '/')
53 | }
54 |
55 | constructor(context: ExtensionContext) {
56 | this.context = context
57 | this.projectRootPath = getWorkspaceRoot('')
58 | this.projectRootPathReg = new RegExp(`.*${this.projectRootPath}/`, 'gi')
59 | this.traverse = new Traverse(this, this.prefix)
60 | this.tabSize = getTabSize()
61 | const tsconfigPath = winRootPathHandle(path.join(this.projectRootPath, 'tsconfig.json'))
62 | this.isTs = fs.existsSync(tsconfigPath)
63 |
64 | const vueHelperStatusBar = window.createStatusBarItem(StatusBarAlignment.Right, -99999)
65 | vueHelperStatusBar.text = '$(extensions-view-icon) helper'
66 | // const vueHelperStatusBarTooltip = new MarkdownString('vue-helper2
')
67 | // vueHelperStatusBarTooltip.supportHtml = true
68 | // vueHelperStatusBar.tooltip = vueHelperStatusBarTooltip
69 | vueHelperStatusBar.show()
70 | this.context.subscriptions.push(vueHelperStatusBar)
71 |
72 | this.vueFiles = this.traverse.search('.vue', '', false)
73 | const watcher = workspace.createFileSystemWatcher('**/*.vue')
74 | watcher.onDidCreate(() => { this.vueFiles = this.traverse.search('.vue', '', false) })
75 | watcher.onDidDelete(() => { this.vueFiles = this.traverse.search('.vue', '', false) })
76 |
77 | workspace.onDidOpenTextDocument((e: TextDocument) => {
78 | this.openDocument(e)
79 | })
80 | }
81 |
82 | register() {
83 | }
84 |
85 | public resetInit() {
86 | this.inits.forEach(initObj => {
87 | initObj.init()
88 | });
89 | }
90 |
91 | public addInit(obj: any) {
92 | this.inits.push(obj)
93 | }
94 |
95 | public openDocument(e: TextDocument) {
96 | // 当多工程打开时,进入文件重新获取工程路径
97 | let docPath = e.uri.path.replace(/.*:\//gi, '\/')
98 | if (!this.projectRootPath || !docPath.includes(this.projectRootPath.replace(/.*:\//gi, '\/'))) {
99 | this.projectRootPath = getWorkspaceRoot(e.uri.path)
100 | this.vueFiles = this.traverse.search('.vue', '', false)
101 | this.resetInit()
102 | }
103 | }
104 | }
--------------------------------------------------------------------------------
/src/framework.ts:
--------------------------------------------------------------------------------
1 | import { CancellationToken, CompletionContext, CompletionItem, CompletionItemProvider, CompletionList, Position, ProviderResult, TextDocument,
2 | languages, workspace, Range, window, Selection, CompletionItemKind, SnippetString,
3 | l10n, HoverProvider, Hover, TextLine, DefinitionProvider, Definition, Uri, Location } from "vscode";
4 | import ExplorerProvider from './explorer'
5 | import * as fs from 'fs'
6 | import * as path from 'path'
7 | import { winRootPathHandle, getRelativePath, getCurrentWord, getWord } from './util/util'
8 | import { getJsTag, getTag, getAttribute, getGlobalAttribute, getDocument } from "./frameworks";
9 | import vueSnippetsHtml from './vue/snippets-html'
10 | import vueSnippetsJs from './vue/snippets-js'
11 | const paramCamse = require('param-case')
12 | const glob = require('glob')
13 |
14 | export interface TagObject {
15 | text: string,
16 | offset: number
17 | };
18 |
19 | export default class FrameworkProvider {
20 | public explorer: ExplorerProvider
21 | public frameworks: string[] = []
22 | public pathAlias = {
23 | alias: '',
24 | path: ''
25 | }
26 |
27 | constructor(explorer: ExplorerProvider) {
28 | this.explorer = explorer
29 | this.init()
30 | this.explorer.addInit(this)
31 | }
32 |
33 | init() {
34 | try {
35 | if (this.explorer.projectRootPath) {
36 | this.frameworks = []
37 | const pkg = fs.readFileSync(winRootPathHandle(path.join(this.explorer.projectRootPath, 'package.json')), 'utf-8')
38 | pkg.includes('element-plus') && this.frameworks.push('element-plus')
39 | pkg.includes('element-ui') && this.frameworks.push('element-ui')
40 | pkg.includes('ant-design-vue') && this.frameworks.push('ant-design-vue')
41 | }
42 | } catch (error) {
43 | }
44 | }
45 |
46 | register() {
47 | this.explorer.context.subscriptions.push(languages.registerCompletionItemProvider(['vue', 'javascript', 'typescript', 'html', 'wxml'], new FrameworkCompletionItemProvider(this), '', ':', '<', '"', "'", '/', '@', '(', '>', '{'))
48 | this.explorer.context.subscriptions.push(languages.registerHoverProvider(['vue', 'wxml'], new FrameworkHoverProvider(this)))
49 | this.explorer.context.subscriptions.push(languages.registerDefinitionProvider(['vue', 'javascript', 'html', 'wxml'], new vueHelperDefinitionProvider(this)))
50 | }
51 |
52 | }
53 |
54 | class FrameworkCompletionItemProvider implements CompletionItemProvider {
55 | public frameworkProvider: FrameworkProvider
56 | public attribute: any = {}
57 | public jsTag: any = {}
58 | public tag: any = {}
59 | public globalAttribute: any = {}
60 | public tagReg: RegExp = /<([\w-]+)\s+/g;
61 | public attrReg: RegExp = /(?:\(|\s*)((\w(-)?)*)=['"][^'"]*/; // 能够匹配 left-right 属性
62 |
63 | constructor(frameworkProvider: FrameworkProvider) {
64 | this.frameworkProvider = frameworkProvider
65 | this.init()
66 | this.frameworkProvider.explorer.addInit(this)
67 | }
68 |
69 | init() {
70 | this.attribute = getAttribute(this.frameworkProvider.frameworks, this.frameworkProvider.explorer.tabSize)
71 | this.tag = getTag(this.frameworkProvider.frameworks, this.frameworkProvider.explorer.tabSize)
72 | this.jsTag = getJsTag(this.frameworkProvider.frameworks, this.frameworkProvider.explorer.tabSize)
73 | this.globalAttribute = getGlobalAttribute(this.frameworkProvider.frameworks, this.frameworkProvider.explorer.tabSize)
74 | }
75 |
76 | isCloseTag(document: TextDocument, position: Position) {
77 | let txt = document.getText(new Range(new Position(position.line, 0), position)).trim();
78 | if(!txt.endsWith('>') || /.*=("[^"]*>|'[^']*>)$/gi.test(txt) || txt.endsWith('/>')) {
79 | return false;
80 | }
81 | let txtArr = txt.match(/<([\w-]+)(\s*|(\s+[\w-_:@\.]+(=("[^"]*"|'[^']*'))?)+)\s*>/gim);
82 | if(Array.isArray(txtArr) && txtArr.length > 0) {
83 | let txtStr = txtArr[txtArr.length - 1];
84 | return /<([\w-]+)(\s*|(\s+[\w-_:@\.]+(=("[^"]*"|'[^']*'))?)+)\s*>$/gi.test(txtStr);
85 | }
86 | return false;
87 | }
88 |
89 | // 自动补全关闭标签
90 | getCloseTagSuggestion(document: TextDocument, position: Position) {
91 | let txtInfo = document.lineAt(position.line);
92 | let txtArr = txtInfo.text.match(/<([\w-]+)(\s*|(\s+[\w-_:@\.]+(=("[^"]*"|'[^']*'))?)+)\s*>/gim);
93 | let tag = 'div';
94 | if(txtArr) {
95 | tag = txtArr[txtArr.length - 1].replace(/<([\w-]+)(\s*|(\s+[\w-_:@\.]+(=("[^"]*"|'[^']*'))?)+)\s*>/gim, '$1');
96 | }
97 | let exclude = ['br', 'img'];
98 | if (exclude.indexOf(tag) === -1 && window.activeTextEditor) {
99 | window.activeTextEditor.edit((editBuilder) => {
100 | editBuilder.insert(position, '' + tag + '>');
101 | });
102 | let newPosition = window.activeTextEditor.selection.active.translate(0, 0);
103 | if (newPosition) {
104 | window.activeTextEditor.selection = new Selection(newPosition, newPosition);
105 | }
106 | }
107 | }
108 |
109 | // 获取本行位置前的文本
110 | getTextBeforePosition(position: Position, document: TextDocument): string {
111 | var start = new Position(position.line, 0);
112 | var range = new Range(start, position);
113 | return document.getText(range);
114 | }
115 |
116 | // 匹配标签
117 | matchTag(reg: RegExp, txt: string, line: number, document: TextDocument, position: Position): TagObject | string {
118 | let match: any;
119 | let arr: any[] = [];
120 | if (/<\/?[-\w]+[^<>]*>[\s\w]*\s*[\w-]*$/.test(txt) || (position.line === line && (/^\s*[^<]+\s*>[^<\/>]*$/.test(txt) || /[^<>]*<$/.test(txt[txt.length - 1])))) {
121 | return 'break';
122 | }
123 | while ((match = reg.exec(txt))) {
124 | arr.push({
125 | text: match[1],
126 | offset: document.offsetAt(new Position(line, match.index))
127 | });
128 | }
129 | return arr.pop();
130 | }
131 |
132 | getPreTag(document: TextDocument, position: Position): TagObject | undefined {
133 | let line = position.line;
134 | let tag: TagObject | string;
135 | let txt = this.getTextBeforePosition(position, document);
136 |
137 | while (position.line - line < 10 && line >= 0) {
138 | if (line !== position.line) {
139 | txt = document.lineAt(line).text;
140 | }
141 | tag = this.matchTag(this.tagReg, txt, line, document, position);
142 |
143 | if (tag === 'break') {return;}
144 | if (tag) {return tag;}
145 | line--;
146 | }
147 | return;
148 | }
149 |
150 | // 获取预览属性
151 | getPreAttr(document: TextDocument, position: Position): string | undefined {
152 | let txt = this.getTextBeforePosition(position, document).replace(/"[^'"]*(\s*)[^'"]*$/, '');
153 | let end = position.character;
154 | let start = txt.lastIndexOf(' ', end) + 1;
155 | let parsedTxt = document.getText(new Range(position.line, start, position.line, end));
156 |
157 | return this.matchAttr(this.attrReg, parsedTxt);
158 | }
159 |
160 | // 匹配属性
161 | matchAttr(reg: RegExp, txt: string): string {
162 | let match: any;
163 | match = reg.exec(txt);
164 | return !/"[^"]*"/.test(txt) && match && match[1];
165 | }
166 |
167 | // 属性值开始
168 | isAttrValueStart(tag: Object | string | undefined, attr: any) {
169 | return tag && attr;
170 | }
171 |
172 | // 获取属性值
173 | getAttrValues(tag: string, attr: string) {
174 | let attrValues: string[] = []
175 | // 全局
176 | if (this.globalAttribute[attr]) {
177 | attrValues = this.globalAttribute[attr].values
178 | }
179 |
180 | if (this.attribute[tag] && this.attribute[tag][attr]) {
181 | attrValues = this.attribute[tag][attr].values
182 | }
183 |
184 | return attrValues
185 | }
186 |
187 | // 属性值建议值
188 | getAttrValueSuggestion(tag: string, attr: string): CompletionItem[] {
189 | let suggestions: CompletionItem[] = [];
190 | const values = this.getAttrValues(tag, attr);
191 | values.forEach((value: string) => {
192 | suggestions.push({
193 | sortText: `000${value}`,
194 | label: value,
195 | detail: this.frameworkProvider.explorer.name,
196 | kind: CompletionItemKind.Value,
197 | });
198 | });
199 | return suggestions;
200 | }
201 |
202 | // 属性建议值
203 | getAttrSuggestion(tag: string, document: TextDocument, position: Position) {
204 | let suggestions: any[] = [];
205 | let tagAttrs = this.getTagAttrs(tag);
206 |
207 | let preText = this.getTextBeforePosition(position, document);
208 | let prefix: any = preText.replace(/['"]([^'"]*)['"]$/, '').split(/\s|\(+/).pop();
209 | // 方法属性
210 | const type = prefix[0] === '@' ? 'method' : 'attribute';
211 |
212 | tagAttrs.forEach((attr: any) => {
213 | if (attr.type === type) {
214 | suggestions.push(this.buildAttrSuggestion(attr))
215 | }
216 | });
217 |
218 | for (let attr in this.globalAttribute) {
219 | let gAttr = this.globalAttribute[attr]
220 | if (gAttr.type === type) {
221 | suggestions.push(this.buildAttrSuggestion({
222 | name: attr,
223 | ...gAttr
224 | }))
225 | }
226 | }
227 | return suggestions;
228 | }
229 |
230 | buildAttrSuggestion(attr: any) {
231 | const completionItem = new CompletionItem(attr.name)
232 | completionItem.sortText = `000${attr.name}`
233 | completionItem.insertText = attr.name
234 | completionItem.kind = attr.type === 'method' ? CompletionItemKind.Method : CompletionItemKind.Property
235 | completionItem.detail = this.frameworkProvider.explorer.name
236 | completionItem.documentation = l10n.t(attr.description)
237 | return completionItem
238 | }
239 |
240 | // 获取标签包含的属性
241 | getTagAttrs(tag: string) {
242 | let attrs: any = []
243 | if (this.attribute[tag]) {
244 | for (const key in this.attribute[tag]) {
245 | if (key !== '_self') {
246 | attrs.push({
247 | name: key,
248 | ...this.attribute[tag][key]
249 | })
250 | }
251 | }
252 | }
253 | return attrs
254 | }
255 |
256 | // 获取props属性值
257 | getPropAttr(document: TextDocument, tagName: any) {
258 | let documentText = document.getText()
259 | // 1. 找出标签所在路径
260 | let tagNameUpper = tagName.replace(/(-[a-z])/g, (_: any, c: any) => {
261 | return c ? c.toUpperCase() : '';
262 | }).replace(/-/gi, '');
263 | let pathReg = RegExp('import\\\s+(' + tagName + '|' + tagNameUpper + ')\\\s+from\\\s+[\'\"]([^\'\"]*)', 'g');
264 | let pathRegArr = documentText.match(pathReg);
265 | if (pathRegArr && pathRegArr.length > 0) {
266 | let tagPath = pathRegArr[0];
267 | tagPath = tagPath.replace(/(.*['"])/, '');
268 | tagPath = tagPath.replace(this.frameworkProvider.pathAlias.alias, this.frameworkProvider.pathAlias.path);
269 | if (!tagPath.endsWith('.vue')) {
270 | tagPath += '.vue';
271 | }
272 | if (tagPath.indexOf('./') > 0 || tagPath.indexOf('../') > 0) {
273 | tagPath = path.join(document.fileName, '../', tagPath);
274 | } else {
275 | tagPath = path.join(workspace.rootPath || '', tagPath);
276 | }
277 | documentText = fs.readFileSync(tagPath, 'utf8');
278 | } else {
279 | return;
280 | }
281 |
282 | // 2. 获取标签文件中的prop属性
283 | let props: CompletionItem[] = [];
284 | let scriptIndex = documentText.indexOf('
6 |
--------------------------------------------------------------------------------
/src/monitor.ts:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import ExplorerProvider from "./explorer";
3 | import * as fs from 'fs'
4 | import * as path from 'path'
5 | import { v4 as uuid } from "uuid";
6 |
7 | interface User {
8 | id: string
9 | active: string
10 | }
11 |
12 | export default class MonitorProvider {
13 | private url = 'https://int.miaixyt.com'
14 | private userPath = ''
15 | private user: User = {
16 | id: '',
17 | active: ''
18 | }
19 | public explorer: ExplorerProvider
20 |
21 | constructor(explorer: ExplorerProvider) {
22 | this.explorer = explorer
23 | try {
24 | this.userPath = path.join(this.explorer.context.extensionPath, 'asset/user.json')
25 | const user = fs.readFileSync(this.userPath, 'utf-8')
26 | let today = new Date().getDate()
27 | if (user) {
28 | this.user = JSON.parse(user)
29 | }
30 | let canSend = false
31 | if (!this.user.id) {
32 | this.user.id = uuid()
33 | this.user.active = today.toString()
34 | fs.writeFileSync(this.userPath, JSON.stringify(this.user), 'utf-8')
35 | canSend = true
36 | } else {
37 | if (parseInt(this.user.active) !== today) {
38 | canSend = true
39 | this.user.active = today.toString()
40 | fs.writeFileSync(this.userPath, JSON.stringify(this.user), 'utf-8')
41 | }
42 | }
43 | if (canSend) {
44 | this.active()
45 | }
46 | } catch (_error: any) {
47 | }
48 | }
49 |
50 | active() {
51 | axios.post(this.url + '/api/sm/addArticleReadLog', {
52 | device_id: this.user.id,
53 | platform: 'IDE'
54 | })
55 | }
56 |
57 | }
--------------------------------------------------------------------------------
/src/server.ts:
--------------------------------------------------------------------------------
1 | import * as vscode from 'vscode';
2 |
3 | export function activate(context: vscode.ExtensionContext) {
4 | console.log('vue-helper activate', context.extension.id)
5 | }
--------------------------------------------------------------------------------
/src/util/traverse.ts:
--------------------------------------------------------------------------------
1 | import * as fs from 'fs'
2 | import * as path from 'path'
3 | import ExplorerProvider, { Prefix } from '../explorer'
4 |
5 | export default class Traverse {
6 | public explorer: ExplorerProvider
7 | public prefix: Prefix
8 | public constructor(explorer: ExplorerProvider, prefix: Prefix) {
9 | this.explorer = explorer
10 | this.prefix = prefix
11 | }
12 |
13 | // 遍历组件
14 | search(poster: any, searchName: any, usePrefix: boolean) {
15 | let files: any[] = [];
16 | let cond: Function;
17 | let componentPath = ''
18 | let prefix = usePrefix ? this.prefix : {}
19 | // if (this.explorer.config.rootPath) {
20 | // componentPath = this.explorer.config.rootPath.component
21 | // let pathAlias = this.explorer.config.rootPath.root.split('=')
22 | // if (pathAlias.length === 2) {
23 | // prefix = {
24 | // alias: pathAlias[0],
25 | // path: pathAlias[1],
26 | // }
27 | // }
28 | // }
29 | if (!this.explorer.projectRootPath) {
30 | // 未打开工程
31 | return []
32 | }
33 |
34 | if (componentPath && Array.isArray(componentPath) && componentPath.length > 0) {
35 | cond = function (rootPath: any) {
36 | return componentPath.indexOf(rootPath) !== -1;
37 | };
38 | } else {
39 | let ignore: string[] = ['node_modules', 'dist', 'build'];
40 | cond = function (rootPath: any) {
41 | return !(rootPath.charAt(0) === '.' || ignore.indexOf(rootPath) !== -1);
42 | };
43 | }
44 | let rootPathes = fs.readdirSync(this.explorer.projectRootPath || '');
45 |
46 | for (let i = 0; i < rootPathes.length; i++) {
47 | const rootPath = rootPathes[i];
48 | if (cond(rootPath)) {
49 | let stat = fs.statSync(path.join(this.explorer.projectRootPath || '', rootPath));
50 | if (stat.isDirectory()) {
51 | this.traverseHandle(rootPath, files, prefix, poster, searchName);
52 | } else {
53 | this.traverseAdd(rootPath, rootPath, files, prefix, poster, searchName);
54 | }
55 | }
56 | }
57 | return files;
58 | }
59 |
60 | // 遍历添加
61 | traverseAdd(rootPath: string, dir: string, files: any[], prefix: any, poster: string, search: string) {
62 | if (rootPath.endsWith(poster)) {
63 | let posterReg = new RegExp('-?(.*)' + (poster ? poster : '\\.\\w*') + '$', 'gi');
64 | let name = rootPath;
65 | name = name.replace(posterReg, '$1');
66 | if (!search || (search && dir.includes(search))) {
67 | files.push({
68 | name: name,
69 | path: dir.replace(new RegExp(`^${prefix.path}`, 'gi'), prefix.alias).replace(/\\/gi, '/')
70 | });
71 | if (name === 'index') {
72 | name = dir.replace(/\\/gi, '/').replace(/.*\/(\w*)\/\w*.\w*/gi, '$1')
73 | files.push({
74 | name: name,
75 | path: dir.replace(new RegExp(`^${prefix.path}`, 'gi'), prefix.alias).replace(/\\/gi, '/')
76 | })
77 | }
78 | }
79 | }
80 | }
81 |
82 | // 遍历处理
83 | traverseHandle(postPath: string, files: any [], prefix: any, poster: string, search: string) {
84 | let fileDirs = fs.readdirSync(path.join(this.explorer.projectRootPath || '', postPath));
85 | for (let i = 0; i < fileDirs.length; i++) {
86 | const rootPath = fileDirs[i];
87 | if (!(rootPath.charAt(0) === '.')) {
88 | let dir = path.join(postPath, rootPath);
89 | let stat = fs.statSync(path.join(this.explorer.projectRootPath || '', dir));
90 | if (stat.isDirectory()) {
91 | this.traverseHandle(dir, files, prefix, poster, search);
92 | } else {
93 | this.traverseAdd(rootPath, dir, files, prefix, poster, search);
94 | }
95 | }
96 | }
97 | }
98 | }
--------------------------------------------------------------------------------
/src/util/util.ts:
--------------------------------------------------------------------------------
1 | import { workspace, TextDocument, Position } from 'vscode'
2 | import * as os from 'os'
3 | import * as path from 'path'
4 |
5 | // windows根路径处理
6 | export function winRootPathHandle(pagePath: string) {
7 | if (!pagePath) {
8 | return ''
9 | }
10 | if (os.platform().includes("win") && pagePath.length > 0 && (pagePath[0] === "\\" || pagePath[0] === "/")) {
11 | return pagePath.substr(1, pagePath.length);
12 | } else {
13 | return pagePath;
14 | }
15 | }
16 |
17 | export function getWorkspaceRoot(documentUrl: string) {
18 | let url: string = '';
19 | if (workspace.workspaceFolders?.length === 1) {
20 | return winRootPathHandle(workspace.workspaceFolders[0].uri.path)
21 | }
22 | workspace.workspaceFolders?.forEach((workspaceFolder) => {
23 | // windows盘去除
24 | if(documentUrl.includes(workspaceFolder.uri.path.replace(/.*:\//gi, '\/'))) {
25 | url = workspaceFolder.uri.path
26 | }
27 | })
28 | return winRootPathHandle(url)
29 | }
30 |
31 | export function getTabSize() {
32 | const tabSize = workspace.getConfiguration('editor').tabSize;
33 | let space = '';
34 | for (let i = 0; i < tabSize; i++) {
35 | space += ' ';
36 | }
37 | if (space.length === 0) {
38 | space = ' '
39 | }
40 | return space
41 | }
42 |
43 | export function getCurrentWord(document: TextDocument, position: Position) {
44 | let i = position.character - 1;
45 | const text = document.lineAt(position.line).text;
46 | while (i >= 0 && ' \t\n\r\v":{[,'.indexOf(text.charAt(i)) === -1) {
47 | i--;
48 | }
49 | return text.substring(i + 1, position.character);
50 | }
51 |
52 | export function getRelativePath(src: string, dist: string) {
53 | let vfPath = path.relative(winRootPathHandle(src), dist)
54 | vfPath = vfPath.replace(/\\/gi, '/')
55 | if (vfPath.startsWith('../')) {
56 | vfPath = vfPath.substr(1, vfPath.length)
57 | }
58 | return vfPath
59 | }
60 |
61 | export function getWord(document: TextDocument, position: Position, textSplite: string[]) {
62 | const line = document.lineAt(position.line);
63 | // 通过前后字符串拼接成选择文本
64 | let posIndex = position.character;
65 | let textSingle = line.text.substring(posIndex, posIndex + 1);
66 | let selectText = '';
67 | // 前向获取符合要求的字符串
68 | while(textSplite.indexOf(textSingle) === -1 && posIndex <= line.text.length) {
69 | selectText += textSingle;
70 | ++posIndex
71 | textSingle = line.text.substring(posIndex, posIndex + 1)
72 | }
73 | // 往后获取符合要求的字符串
74 | posIndex = position.character - 1;
75 | textSingle = line.text.substring(posIndex, posIndex + 1);
76 | while(textSplite.indexOf(textSingle) === -1 && posIndex > 0) {
77 | selectText = textSingle + selectText;
78 | --posIndex
79 | textSingle = line.text.substring(posIndex, posIndex + 1);
80 | }
81 | textSingle = line.text.substring(posIndex, posIndex + 1);
82 | return {
83 | selectText,
84 | startText: textSingle
85 | }
86 | }
--------------------------------------------------------------------------------
/src/vue/snippets-html.ts:
--------------------------------------------------------------------------------
1 | export default (tabSize: string) => {
2 | return {
3 | "vcomponent": `$0`,
4 | "vka": `
5 | ${tabSize}$2
6 | $0`,
7 | "vtransition": `
8 | ${tabSize}$2
9 | $0`,
10 | "vtg": `
11 | ${tabSize}$2
12 | `,
13 | "vrl": `$2$0`,
14 | "vrlt": `$2$0`,
15 | "vrv": `$1$0`,
16 | }
17 | }
--------------------------------------------------------------------------------
/src/vue/snippets-js.ts:
--------------------------------------------------------------------------------
1 | export default (tabSize: string) => {
2 | return {
3 | "vnew": `new Vue({
4 | ${tabSize}$1
5 | })`,
6 | "vgsilent": `Vue.config.silent = \${1:true}`,
7 | "vgeh": `Vue.config.errorHandler = function (err, vm, info) {
8 | ${tabSize}\${1:// handle error}
9 | }`,
10 | "vgwh": `Vue.config.warnHandler = function (msg, vm, trace) {
11 | ${tabSize}\${1:// handle warn}
12 | }`,
13 | "vgextend": `Vue.extend({
14 | ${tabSize}template:\${1:template}
15 | })`,
16 | "vgnt": `Vue.nextTick({
17 | ${tabSize}$1
18 | })`,
19 | "vgset": `Vue.set(\${2:target}, \${3:key}, \${4:value})`,
20 | "vgdelete": `Vue.delete(\${2:target}, \${3:key})`,
21 | "vgdirective": `Vue.directive(\${2:id}\${3:, [definition]})`,
22 | "vgfilter": `Vue.filter(\${1:id}\${2:, [definition]})`,
23 | "vgcomponent": `Vue.component(\${1:id}\${2:, [definition]})$0`,
24 | "vguse": `Vue.use(\${1:plugin})$0`,
25 | "vgmixin": `Vue.mixin({\${1:mixin}})`,
26 | "vgcompile": `Vue.compile(\${1:template})`,
27 | "vdata": `data() {
28 | ${tabSize}return {
29 | ${tabSize}${tabSize}$1
30 | ${tabSize}}
31 | },$0`,
32 | "vmounted": `mounted () {
33 | ${tabSize}$1
34 | }`,
35 | "vbm": `beforeMount () {
36 | ${tabSize}$1
37 | }`,
38 | "vcreated": `created () {
39 | ${tabSize}$1
40 | }`,
41 | "vbc": `beforeCreate () {
42 | ${tabSize}$1
43 | }`,
44 | "vupdated": `updated () {
45 | ${tabSize}$1
46 | }`,
47 | "vbu": `beforeUpdate () {
48 | ${tabSize}$1
49 | }`,
50 | "vactivated": `activated () {
51 | ${tabSize}$1
52 | }`,
53 | "vdeactivated": `deactivated () {
54 | ${tabSize}$1
55 | }`,
56 | "vbd": `beforeDestroy () {
57 | ${tabSize}$1
58 | }`,
59 | "vdestroyed": `destroyed () {
60 | ${tabSize}$1
61 | }`,
62 | "vprops": `props: {
63 | ${tabSize}$1
64 | }`,
65 | "vpd": `propsData: {
66 | ${tabSize}$1
67 | }`,
68 | "vcomputed": `computed: {
69 | ${tabSize}$1
70 | }`,
71 | "vmethods": `methods: {
72 | ${tabSize}$1
73 | }`,
74 | "vwatch": `watch: {
75 | ${tabSize}$1
76 | }`,
77 | "vwo": `\${1:key}: {
78 | ${tabSize}deep: \${2:true},
79 | ${tabSize}immediate: \${3:true},
80 | ${tabSize}handler: function (\${4:val}, \${5:oldVal}) {
81 | ${tabSize}${tabSize}$6
82 | ${tabSize}}
83 | },$0`,
84 | "vdirectives": `directives: {
85 | ${tabSize}$1
86 | }`,
87 | "vfilters": `filters: {
88 | ${tabSize}$1
89 | }`,
90 | "vcomponents": `components: {
91 | ${tabSize}$1
92 | }`,
93 | "vmixins": `mixins:[$1],`,
94 | "vprovide": `provide: {
95 | ${tabSize}$1
96 | }`,
97 | "vinject": `inject: [$1],`,
98 | "vmodel": `model: {
99 | ${tabSize}prop: $1,
100 | ${tabSize}event: $2
101 | }`,
102 | "vrender": `render(h) {
103 | ${tabSize}$1
104 | },`,
105 | "vel": `\${1|this,vm|}.$el$2`,
106 | "voptions": `\${1|this,vm|}.$options$2`,
107 | "vparent": `\${1|this,vm|}.$parent$2`,
108 | "vroot": `\${1|this,vm|}.$root$2`,
109 | "vchildren": `\${1|this,vm|}.$children$2`,
110 | "vslots": `\${1|this,vm|}.$slots$2`,
111 | "vss": `\${1|this,vm|}.$scopedSlots.default({
112 | ${tabSize}$2
113 | })`,
114 | "vrefs": `\${1|this,vm|}.$refs$2`,
115 | "vis": `\${1|this,vm|}.$isServer$2`,
116 | "vattrs": `\${1|this,vm|}.$attrs$2`,
117 | "vlisteners": `\${1|this,vm|}.$listeners$2`,
118 | "vset": `\${1|this,vm|}.$set(\${2:target}, \${3:key}, \${4:value})`,
119 | "vdelete": `\${1|this,vm|}.$delete(\${2:target}, \${3:key})`,
120 | "von": `\${1|this,vm|}.$on('\${2:event}', \${3:callback})$4`,
121 | "vonce": `\${1|this,vm|}.$once('\${2:event}', \${3:callback})$4`,
122 | "voff": `\${1|this,vm|}.$off('\${2:event}', \${3:callback})$4`,
123 | "vemit": `\${1|this,vm|}.$emit('\${2:event}'\${3:, args})$4`,
124 | "vmount": `\${1|this,vm|}.$mount('$2')`,
125 | "vfu": `\${1|this,vm|}.$forceUpdate()`,
126 | "vdestroy": `\${1|this,vm|}.$destroy()$2`,
127 | "vnt": `\${1|this,vm|}.$nextTick(() => {
128 | ${tabSize}$2
129 | })`,
130 | }
131 | }
--------------------------------------------------------------------------------
/tsconfig.build-ci.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "include": [],
4 | "references": [
5 | ]
6 | }
--------------------------------------------------------------------------------
/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "noEmit": false,
5 | "outDir": "out",
6 | "rootDir": "src",
7 | },
8 | "include": [
9 | "src",
10 | "src/**/*.json"
11 | ],
12 | "exclude": [
13 | "node_modules",
14 | ".vscode-test"
15 | ],
16 | "references": [
17 | ]
18 | }
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2016",
4 | "lib": [
5 | "WebWorker",
6 | "ES2021",
7 | ],
8 | "module": "commonjs",
9 | "moduleResolution": "node",
10 | "sourceMap": true,
11 | "composite": true,
12 | "declaration": true,
13 | "strict": true,
14 | "alwaysStrict": false,
15 | "resolveJsonModule": true,
16 | "skipLibCheck": true,
17 | "noUnusedLocals": true,
18 | "noUnusedParameters": true,
19 | "baseUrl": "./",
20 | "paths": {
21 | },
22 | "noEmit": true,
23 | },
24 | "include": [
25 | "*/*/src",
26 | "*/*/tests",
27 | ],
28 | "exclude": [
29 | "node_modules",
30 | ],
31 | }
--------------------------------------------------------------------------------