遇到的问题
使用vue时,每次导入组件都十分的麻烦,得先写明组件标签,然后在script标签中import导入,在components中显式声明。。。遇到这种劳动重复性问题时,我都会想是否能用脚本完成?有幸使用了vscode,可以自定义打造我们的自动导入插件
开始
使用yo code
初始化项目。百度上关于初始化项目一大堆,不重点说了。。
自动提示组件名称
第一步就是要先能自动提示组件名称,vscode中关于显示建议的核心API是( 看API直接ctrl+左键点击const vscode = require(‘vscode’) 即可,d.ts描述文件比官网API文档都详细 )
export function registerCompletionItemProvider(selector: DocumentSelector, provider: CompletionItemProvider, ...triggerCharacters: string[]): Disposable;
其中: CompletionItemProvider
包含provideCompletionItems
和resolveCompletionItem
.
provideCompletionItems
返回选项集合(暂时这么叫吧),resolveCompletionItem
具体处理你选择选项时的事件
根目录下新建autotip.js文件, 具体用法见注释
const vscode = require('vscode');
const fs = require('fs');
module.exports = function (context) {
let rangeline = 0
let rangeCol = 0
/**
* 只支持单文件夹!
* @param {*} document
* @param {*} position
*/
function provideCompletionItems(document, position) {
let arr = [];
// 递归寻找组件名
let readFile = (fileDir, filter) => {
let filearr = fs.readdirSync(fileDir)
filearr.forEach((v) => {
if (fs.statSync(fileDir + '/' + v).isDirectory()) {
readFile(fileDir + '/' + v, filter)
} else {
if (v.indexOf(filter) !== -1 && v.substring(v.lastIndexOf('.') + 1) === 'vue') {
arr.push(v.substring(0, v.lastIndexOf('.')))
}
}
})
}
// 找到光标所在的一行
const line = document.lineAt(position);
const lineText = line.text.substring(0, position.character);
rangeline = position.line
rangeCol = position.character
let workspaceFolders = vscode.workspace.workspaceFolders.map(item => item.uri.path);
// 找到根目录
let rootpath = workspaceFolders[0];
// 递归寻找src目录下的vue文件
readFile(rootpath.substring(1) + '/src', lineText.substring(lineText.indexOf('<') + 1));
// 返回所有vue文件
return arr.map((v) => {
return new vscode.CompletionItem(v, vscode.CompletionItemKind.Field);
})
}
function resolveCompletionItem(item) {
// 把vue文件名处理成xxx-xxx的形式
let content = item.label[0].toLowerCase() + item.label.substring(1)
content = content.replace(/[A-Z]/g, (text) => {
return `-${text.toLowerCase()}`
})
item.insertText = content
return item;
}
context.subscriptions.push(vscode.languages.registerCompletionItemProvider('vue', {
provideCompletionItems,
resolveCompletionItem
}, '<')); // 触发建议的按键
};
完成后,在extension.js
中引入
// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
const vscode = require('vscode');
const autotip = require('./autptip')
// this method is called when your extension is activated
// your extension is activated the very first time the command is executed
function activate(context) {
// Use the console to output diagnostic information (console.log) and errors (console.error)
// This line of code will only be executed once when your extension is activated
console.log('Congratulations, your extension "autotip" is now active!');
// The command has been defined in the package.json file
// Now provide the implementation of the command with registerCommand
// The commandId parameter must match the command field in package.json
// 引入
autotip(context)
}
exports.activate = activate;
// this method is called when your extension is deactivated
function deactivate() {
}
exports.deactivate = deactivate;
在package.json
中注册
"activationEvents": [
"onLanguage:vue"
],
现在你可以按F5测试,是否生成了建议列表
接受建议时触发动作
按下tab或者enter时触发以下动作:
- 在script标签中写入
import xxx from 'xxxx'
模板 - 在components对象中写入引用
万变不离其中,都是用nodejs的IO文件流完成工作
找到script标签所在位置
先写几个通用方法
// 找到过滤文件
let readFile = (fileDir, filter) => {
let filearr = fs.readdirSync(fileDir)
filearr.forEach((v) => {
if (fs.statSync(fileDir + '/' + v).isDirectory()) {
readFile(fileDir + '/' + v, filter)
} else {
let reg = new RegExp(filter, 'i'); // 忽略大小写
if (reg.test(v) && v.substring(v.lastIndexOf('.') + 1) === 'vue' && v.length === filter.length + 4) {
obj.name = v.substring(0, v.lastIndexOf('.'))
obj.path = fileDir + '/' + v.substring(0, v.lastIndexOf('.'))
}
}
})
}
// 找到某个字符串在编辑器中的行号
let getLineNumber = (filter) => {
let scriptline = -1;
for (let i = 0; i < document.lineCount; i++) {
let str = document.lineAt(i).text;
if (str.trim().indexOf(filter) !== -1) {
scriptline = i;
break;
}
}
return scriptline;
}
找到script标签的位置如下:
let scriptline = getLineNumber('<script>');
完成文件模板写入
complementtag.js具体代码
const vscode = require('vscode');
const fs = require('fs');
const os = require('os')
module.exports = function (context) {
let rangeline = 0
let rangeCol = 0
function provideCompletionItems(document, position) {
let obj = {};
let readFile = (fileDir, filter) => {
let filearr = fs.readdirSync(fileDir)
filearr.forEach((v) => {
if (fs.statSync(fileDir + '/' + v).isDirectory()) {
readFile(fileDir + '/' + v, filter)
} else {
let reg = new RegExp(filter, 'i');
if (reg.test(v) && v.substring(v.lastIndexOf('.') + 1) === 'vue' && v.length === filter.length + 4) {
obj.name = v.substring(0, v.lastIndexOf('.'))
obj.path = fileDir + '/' + v.substring(0, v.lastIndexOf('.'))
}
}
})
}
let getLineNumber = (filter) => {
let scriptline = -1;
for (let i = 0; i < document.lineCount; i++) {
let str = document.lineAt(i).text;
if (str.trim().indexOf(filter) !== -1) {
scriptline = i;
break;
}
}
return scriptline;
}
rangeline = position.line
rangeCol = position.character
let item = document.getText(new vscode.Range(new vscode.Position(rangeline, 0), new vscode.Position(rangeline, rangeCol)));
item = item.trim()
item = item.substring(1, item.length - 1)
item = item.replace(/-/g, '')
let workspaceFolders = vscode.workspace.workspaceFolders.map(item => item.uri.path);
let rootpath = workspaceFolders[0];
readFile(rootpath.substring(1) + '/src', item)
let scriptline = getLineNumber('<script>');
// 完成import部分
vscode.window.activeTextEditor.edit(editBuilder => {
const text = `import ${obj.name} from '${obj.path.substring(obj.path.indexOf('src'))}'`
editBuilder.insert(new vscode.Position(scriptline + 1, 0), text + os.EOL);
// 寻找components字符所在行
let componentsLine = getLineNumber('components');
if (componentsLine !== -1) {
let old = document.lineAt(componentsLine).text
console.log(old.trim().indexOf('{'))
console.log('总长度' + old.trim().length - 1)
// 存在components选项
let i = document.lineAt(componentsLine).text.trim().indexOf('components')
if (i === 0) {
if (old.trim().indexOf('{') === old.trim().length - 1) {
// components: { 格式
const text = `${obj.name},${os.EOL}`;
editBuilder.insert(new vscode.Position(componentsLine + 1, 0), text)
} else {
// components: { }, 格式
const text = old.substring(0, old.length - 2) + ',' + obj.name + '},' + os.EOL;
editBuilder.replace(new vscode.Range(new vscode.Position(componentsLine, 0), new vscode.Position(componentsLine + 1, 0)), text)
}
}
} else {
// 没有components选项
const text = `components: { ${obj.name} },${os.EOL}`
editBuilder.replace(new vscode.Range(new vscode.Position(componentsLine, 0), new vscode.Position(componentsLine + 1, 0)), text)
}
});
}
function resolveCompletionItem(item) {
return item;
}
context.subscriptions.push(vscode.languages.registerCompletionItemProvider('vue', {
provideCompletionItems,
resolveCompletionItem
}, '>'));
};
如法炮制,在extension.js
引入
// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
const vscode = require('vscode');
const autotip = require('./autptip')
const complementtag = require('./complementtag')
// this method is called when your extension is activated
// your extension is activated the very first time the command is executed
function activate(context) {
// Use the console to output diagnostic information (console.log) and errors (console.error)
// This line of code will only be executed once when your extension is activated
console.log('Congratulations, your extension "autotip" is now active!');
// The command has been defined in the package.json file
// Now provide the implementation of the command with registerCommand
// The commandId parameter must match the command field in package.json
autotip(context)
complementtag(context)
}
exports.activate = activate;
// this method is called when your extension is deactivated
function deactivate() {
}
exports.deactivate = deactivate;
最后的话
可以直接在vscode中搜索autotip
这个插件,当然这个插件有很多不完善的地方,但是最重要的怎么自己学会去写插件,自己写插件自己觉得方便就好。别人写插件考虑的自然是自己工作中的问题。
因为是根据components
这个字符串作为判断依据的,只要你其他出现了components这个字眼,那么这个插件就有问题,还有这个插件只支持单文件夹工作空间,尽管有这么多问题但是我却用的很舒服,因为我工作中不会遇到这些问题,如果有一天遇到了就再改一下嘛。插件这东西方便就好,所以自己写适合自己的插件是多么重要啊