更多文章請戳VSCode插件開發全攻略系列目錄導航。
跳轉到定義
跳轉到定義其實很簡單,通過vscode.languages.registerDefinitionProvider
注冊一個provider
,這個provider
如果返回了new vscode.Location()
就表示當前光標所在單詞支持跳轉,並且跳轉到對應location。
為了示例更加有意義,我在這里寫了一個支持package.json
中dependencies
、devDependencies
跳轉到對應依賴包的例子jump-to-definition.js
(當然我們這里只是很簡單的實現,沒有考慮特殊情況,直接從node_modules
文件夾下面去找):
代碼如下:
/**
* 跳轉到定義示例,本示例支持package.json中dependencies、devDependencies跳轉到對應依賴包。
*/
const vscode = require('vscode');
const path = require('path');
const fs = require('fs');
const util = require('./util');
/**
* 查找文件定義的provider,匹配到了就return一個location,否則不做處理
* 最終效果是,當按住Ctrl鍵時,如果return了一個location,字符串就會變成一個可以點擊的鏈接,否則無任何效果
* @param {*} document
* @param {*} position
* @param {*} token
*/
function provideDefinition(document, position, token) {
const fileName = document.fileName;
const workDir = path.dirname(fileName);
const word = document.getText(document.getWordRangeAtPosition(position));
const line = document.lineAt(position);
const projectPath = util.getProjectPath(document);
console.log('====== 進入 provideDefinition 方法 ======');
console.log('fileName: ' + fileName); // 當前文件完整路徑
console.log('workDir: ' + workDir); // 當前文件所在目錄
console.log('word: ' + word); // 當前光標所在單詞
console.log('line: ' + line.text); // 當前光標所在行
console.log('projectPath: ' + projectPath); // 當前工程目錄
// 只處理package.json文件
if (/\/package\.json$/.test(fileName)) {
console.log(word, line.text);
const json = document.getText();
if (new RegExp(`"(dependencies|devDependencies)":\\s*?\\{[\\s\\S]*?${word.replace(/\//g, '\\/')}[\\s\\S]*?\\}`, 'gm').test(json)) {
let destPath = `${workDir}/node_modules/${word.replace(/"/g, '')}/package.json`;
if (fs.existsSync(destPath)) {
// new vscode.Position(0, 0) 表示跳轉到某個文件的第一行第一列
return new vscode.Location(vscode.Uri.file(destPath), new vscode.Position(0, 0));
}
}
}
}
module.exports = function(context) {
// 注冊如何實現跳轉到定義,第一個參數表示僅對json文件生效
context.subscriptions.push(vscode.languages.registerDefinitionProvider(['json'], {
provideDefinition
}));
};
注意不要忘了修改activationEvents
:
"activationEvents": [
"onLanguage:json"
],
new vscode.Location
接收2個參數,第一個是要跳轉到文件的路徑,第二個是跳轉之后光標所在位置,接收Range
或者Position
對象,Position
對象的初始化接收2個參數,行row
和列col
。
高亮顯示范圍
這里有一個問題我一直沒找到解決方案,如下圖所示:
當按住Ctrl
跳轉的時候,雖然可以控制跳轉目標位置,但是卻無法控制高亮顯示的范圍,下圖我本應該讓page/video/list.html
全部變成藍色的,但是默認卻只能以單詞為粒度變色,這個問題我找了很久官方文檔就是沒找到解決辦法,如果大家有知道的歡迎評論指出。
自動補全
通過vscode.languages.registerCompletionItemProvider
方法注冊自動完成實現,接收3個參數:
- 第一個是要關聯的文件類型;
- 第二個是一個對象,里面必須包含
provideCompletionItems
和resolveCompletionItem
這2個方法; - 第三個是一個可選的觸發提示的字符列表;
這里我們實現這樣一個例子,當輸入this.dependencies.xxx
時自動把package.json
中的依賴全部帶出來,包括dependencies
、devDependencies
,就像這樣:
實現代碼如下:
const vscode = require('vscode');
const util = require('./util');
/**
* 自動提示實現,這里模擬一個很簡單的操作
* 當輸入 this.dependencies.xxx時自動把package.json中的依賴帶出來
* 當然這個例子沒啥實際意義,僅僅是為了演示如何實現功能
* @param {*} document
* @param {*} position
* @param {*} token
* @param {*} context
*/
function provideCompletionItems(document, position, token, context) {
const line = document.lineAt(position);
const projectPath = util.getProjectPath(document);
// 只截取到光標位置為止,防止一些特殊情況
const lineText = line.text.substring(0, position.character);
// 簡單匹配,只要當前光標前的字符串為`this.dependencies.`都自動帶出所有的依賴
if(/(^|=| )\w+\.dependencies\.$/g.test(lineText)) {
const json = require(`${projectPath}/package.json`);
const dependencies = Object.keys(json.dependencies || {}).concat(Object.keys(json.devDependencies || {}));
return dependencies.map(dep => {
// vscode.CompletionItemKind 表示提示的類型
return new vscode.CompletionItem(dep, vscode.CompletionItemKind.Field);
})
}
}
/**
* 光標選中當前自動補全item時觸發動作,一般情況下無需處理
* @param {*} item
* @param {*} token
*/
function resolveCompletionItem(item, token) {
return null;
}
module.exports = function(context) {
// 注冊代碼建議提示,只有當按下“.”時才觸發
context.subscriptions.push(vscode.languages.registerCompletionItemProvider('javascript', {
provideCompletionItems,
resolveCompletionItem
}, '.'));
};
懸停提示
從上面的跳轉到定義我們可以看到,雖然我們只是定義了如何調整,到按住Ctrl鍵但是不點擊的時候,vscode默認就會幫我們預覽一部分內容作為提示,除此之外,如果想獲得更多的提示,我們還可以通過vscode.languages.registerHoverProvider
命令來實現。
下面我們依然通過package.json中依賴跳轉的例子來演示如何實現一個自定義hover,如下標紅的內容是我們自己實現的,當鼠標停在package.json的dependencies
或者devDependencies
時,自動顯示對應包的名稱、版本號和許可協議:
如何實現的呢?也很簡單,我們直接上代碼:
const vscode = require('vscode');
const path = require('path');
const fs = require('fs');
/**
* 鼠標懸停提示,當鼠標停在package.json的dependencies或者devDependencies時,
* 自動顯示對應包的名稱、版本號和許可協議
* @param {*} document
* @param {*} position
* @param {*} token
*/
function provideHover(document, position, token) {
const fileName = document.fileName;
const workDir = path.dirname(fileName);
const word = document.getText(document.getWordRangeAtPosition(position));
if (/\/package\.json$/.test(fileName)) {
console.log('進入provideHover方法');
const json = document.getText();
if (new RegExp(`"(dependencies|devDependencies)":\\s*?\\{[\\s\\S]*?${word.replace(/\//g, '\\/')}[\\s\\S]*?\\}`, 'gm').test(json)) {
let destPath = `${workDir}/node_modules/${word.replace(/"/g, '')}/package.json`;
if (fs.existsSync(destPath)) {
const content = require(destPath);
console.log('hover已生效');
// hover內容支持markdown語法
return new vscode.Hover(`* **名稱**:${content.name}\n* **版本**:${content.version}\n* **許可協議**:${content.license}`);
}
}
}
}
module.exports = function(context) {
// 注冊鼠標懸停提示
context.subscriptions.push(vscode.languages.registerHoverProvider('json', {
provideHover
}));
};
有些時候某個字段可能本身已經有提示內容了,如果我們仍然給它注冊了hover的實現的話,那么vscode會自動將多個hover內容合並一起顯示。