使用TypeScript拓展你本身的VS Code!

0x00 前言

在前几天的美国纽约,微软举行了Connect(); //2015大会。经过此次大会,咱们能够很高兴的看到微软的确变得更加开放也更加务实了。固然,会上放出了很多新产品和新功能,其中就包括了VS Code的beta版本。并且微软更进一步,已经在github将VS Code的代码开源了。除了这些让人兴奋的消息,咱们还应该注意到VS Code提供了对拓展的支持。
因此本文就来聊一聊使用TypeScript开发VS Code拓展的话题吧。
此处输入图片的描述
本文所使用的拓展的应用商店页面:node

https://marketplace.visualstudio.com/items/JiadongChen.LicenseHeaderpython

github页面:git

https://github.com/chenjd/VSCode-StandardHeadergithub

0x01 你好,世界

"万事开头,Hello World"。因此咱们就从一个Hello World做为起点,开始一步一步构建本身的VScode的拓展。
在开发vscode的拓展以前,咱们先要确保电脑上已经安装了Node.js。以后,咱们即可以利用微软所提供的基于Yeoman的模板生成器来生成vscode拓展的模板了。typescript

npm install -g yo generator-code

以后,咱们只须要在终端中运行yo code命令,便可以进入建立拓展模板的引导过程。npm

yo code

在引导过程当中将TypeScript选择为开发语言,而且填完一些基本信息以后,咱们的第一个vscode拓展便建立完成了。
那么,yo code命令主要为咱们作了一些什么事情呢?json

  1. 会根据咱们在引导中所填的信息,在当前目录生成一个拓展的文件夹,文件夹名即咱们在引导中填入的拓展名称。并发

  2. 该文件夹中主要包括了盛放源文件(extension.ts)的src文件夹、test文件夹以及out文件夹。须要注意的是out文件夹,在编译typescript源文件以前out文件夹是没有建立的,而编译以后会建立out文件夹而且其中的内容是typescript通过编译以后生成的js文件。函数

  3. 生成了资源配置文件package.json,package.json文件描述了该拓展的信息和它的功能。下文我还会具体说明package.json文件。工具

  4. 建立了launch.json以及tasks.json和settings.json(位于项目中的.vscode目录下),其中launch.json文件规定了启动一个在拓展开发(Extension Development)模式的VS Code进程,而且规定在VS Code启动以前会先运行tasks.json文件中所定义的task(根据tasks.json中的定义,至关于npm run compile),即先使用TypeScript的编译器将ts文件编译为js文件。这样咱们就能够直接编译(由于是typescript)运行而且调试咱们的拓展项目了。

  5. 固然,还会根据咱们在引导中的选择来决定是否建立一个Git仓库,用来作版本控制。

下面即是咱们的拓展项目的完整目录:

.
├── .gitignore                    //配置不须要加入版本管理的文件
├── .vscode                     // VS Code的整合
│   ├── launch.json
│   ├── settings.json
│   └── tasks.json
├── .vscodeignore                //配置不须要加入最终发布到拓展中的文件
├── README.md
├── src                         // 源文件
│   └── extension.ts            // 若是咱们使用js来开发拓展,则该文件的后缀为.js
├── test                        // test文件夹
│   ├── extension.test.ts       // 若是咱们使用js来开发拓展,则该文件的后缀为.js
│   └── index.ts                // 若是咱们使用js来开发拓展,则该文件的后缀为.js
├── node_modules
│   ├── vscode                  // vscode对typescript的语言支持。
│   └── typescript              // TypeScript的编译器
├── out                         // 编译以后的输出文件夹(只有TypeScript须要,JS无需)
│   ├── src
│   |   ├── extension.js
│   |   └── extension.js.map
│   └── test
│       ├── extension.test.js
│       ├── extension.test.js.map
│       ├── index.js
│       └── index.js.map
├── package.json                // 该拓展的资源配置文件
├── tsconfig.json               // 
├── typings                     // 类型定义文件夹
│   ├── node.d.ts               // 和Node.js关联的类型定义
│   └── vscode-typings.d.ts     // 和VS Code关联的类型定义
└── vsc-extension-quickstart.md

此时,咱们只须要点击在VS Code的Debug区域的Start按钮便可。
此处输入图片的描述

在接下来开启的一个处于拓展开发模式的新的VS Code中,咱们只须要在命令面板中输入Hello World即可以激活这个模板拓展,弹出一个Hello World的消息弹窗。
此处输入图片的描述
那么,VS Code究竟是如何加载并运行拓展的呢?咱们又应该如何开发拓展供本身甚至是分享给更多的人更好的使用VS Code呢?

0x02 关于package.json的两三事

做为拓展项目的资源配置文件,package.json不只仅描述了该拓展的信息还描述了拓展的功能。须要说明的是,当VS Code启动时,拓展的package.json文件便会被读取,而且VS Code还会对package.json文件中的contributes部分的内容作处理。这一点和拓展的源文件extension.ts有所区别。VS Code并不会在启动时便加载拓展的源代码。
下面咱们就来看一看咱们刚刚生成的这个拓展项目中的package.json文件的内容吧。

{
    "name": "Standard-Header",
    "displayName": "Standard-Header",
    "description": "standard-header",
    "version": "0.0.1",
    "publisher": "JiadongChen",
    "engines": {
        "vscode": "^0.10.1"
    },
    "categories": [
        "Other"
    ],
    "activationEvents": [
        "onCommand:extension.sayHello"
    ],
    "main": "./out/src/extension",
    "contributes": {
        "commands": [{
            "command": "extension.sayHello",
            "title": "Hello World"
        }]
    },
    "scripts": {
        "vscode:prepublish": "node ./node_modules/vscode/bin/compile",
        "compile": "node ./node_modules/vscode/bin/compile -watch -p ./"
    },
    "devDependencies": {
        "typescript": "^1.6.2",
        "vscode": "0.10.x"
    }
}

咱们能够看到,package.json描述了该拓展的一些基本信息,例如拓展的名称name、拓展的描述信息description以及拓展的版本号version和发布者的信息publisher等等。顺带提一句,直接经过提升版本号的方式就可使用拓展发布工具来更新已经发布到VS Code应用商店的拓展版本。
除了这些,咱们甚至还能够在package.json文件中配置一些拓展在VS Code的应用商店中的展现信息,例如拓展在商店中的种类:categories、拓展的icon图:icon以及拓展在应用市场主页的一些展现信息:galleryBanner等等。

...
"icon": "res/icon.png",
"galleryBanner": {
    "color": "#5c2d91",
    "theme": "dark"
},
"categories": [
    "Other"
],
...

此处输入图片的描述

固然,更重要的内容是activationEvents、contributes以及scripts和main这几项内容。其中scripts项的内容是使用TypeScript进行开发时所特有的,主要的做用是用来提供对ts文件进行编译的相关信息。而main项则指向了将ts源文件编译后所生成的js文件。

ActivationEvents

正如上文所说,VS Code默认情况下并不会在启动时马上执行拓展中的代码。所以,为了使拓展可以被激活,咱们须要在package.json文件中定义activationEvents项的内容。例如上例中,activationEvents的定义以下:

...
"activationEvents": [
    "onCommand:extension.sayHello"
],
...

意思是当“onCommand:extension.sayHello”事件被触发以后,拓展会被激活。那么在VS Code中,激活事件都有哪些种类呢?下面咱们就来归一下类。

根据文件所使用的语言:onLanguage:${language}

当打开的文件是使用onLanguage所规定的语言时,拓展会被激活。
例如:

...
"activationEvents": [
    "onLanguage:python"
]
...

根据所输入的命令:onCommand:${command}

当在onCommand中规定的命令被触发时,拓展会被激活。在上文生成的Hello World模板中,咱们就能够看到。

...
"activationEvents": [
"onCommand:extension.sayHello"
]
...

根据文件夹:workspaceContains:${toplevelfilename}

"activationEvents": [
    "workspaceContains:package.json"
],

无限制:*

...
"activationEvents": [
    "*"
]
...

因为没有限制,所以在VS Code一启动时便会激活该拓展。所以,这种不加以条件限制就激活拓展的方式并非十分推荐的。你们最好谨慎使用。

Contribution

在package.json文件中的contributes项中,一样包含不少种类。若是须要归类的话,能够分为如下几个类型。

configuration
commands
keybindings
languages
debuggers
grammars
themes
snippets

可是本文限于篇幅,将只关注commands和keybindings这两种。

commands

还以上文中的Hello World为例,咱们在commands项中定义了命令的title以及title所对应的具体命令。以下所示:

"contributes": {
    "commands": [{
        "command": "extension.sayHello",
        "title": "Hello World"
    }]
},

此时一旦安装了该拓展,咱们就能够在VS Code中的命令面板(我在Mac上的快捷键是Shift+cmd+p)中找到“Hello World”,输入便会触发extension.sayHello这条命令,而上文中的拓展激活事件项activationEvents中若是定义的内容是"onCommand:extension.sayHello",则激活事件被触发,拓展的代码被激活。
此处输入图片的描述

keybindings

固然,和commands对应的,咱们还能够经过在package.json中定义keybindings项,经过绑定快捷键的方式触发命令。这样操做起来更加便捷,例以下文中咱们本身开发一个插入标准页眉的拓展时,使用的即是绑定快捷键的方式。

"contributes": {
    "keybindings": [{
        "command": "extension.insertHeader",
        "key": "shift+cmd+1",
        "when": "editorTextFocus"
    }]
},

若是咱们在VS Code中安装了定义了快捷键触发的拓展,则打开VS Code的KeyBoard ShortCut就能够看到拓展中定义的快捷键和它对应的命令了。
此处输入图片的描述

0x03 VS Code运行拓展的原理

经过上一小节的内容,咱们就能很清楚的理解上文中那个模板拓展的运行过程了。

VS Code首先会检测到拓展而且读取拓展的package.json文件的内容并将package.json文件中的contributes项应用到VS Code中。这样,咱们就能够根据contributes项中的内容,或者是在命令面板中输入contributes项中定义的commands,或者是使用contributes项中所定义的快捷键keybindings来触发extension.sayHello命令。
一旦extension.sayHello触发,VS Code会建立一个叫作onCommand:extension.sayHello的激活事件。
与此同时,全部在本身的package.json文件中将activationEvents设置为onCommand:extension.sayHello的拓展会被激活。而且会根据package.json文件中main项的内容,将./out/src/extension.js文件加载到JavaScript VM中。接下来,VS Code会调用在extension.js文件中的active函数,在active函数中,咱们须要提供“extension.sayHello”这个函数的具体实现并执行。
好了,了解了VS Code拓展的基本知识以后咱们就能够开始着手开发本身的拓展了,这个拓展的目的是向源文件添加License页眉。

0x04 一个插入License页眉的拓展

既然要打造本身的VS Code,那么本身喜欢的一些操做习惯天然但愿可以用在VS Code上。而插入页眉即是这样一个简单的小功能。

咱们首先来看一看在上文中那个Hello World模板拓展的源文件即extension.ts文件的基本内容。

import * as vscode from 'vscode';

export function activate(context: vscode.ExtensionContext) {

    var disposable = vscode.commands.registerCommand('extension.sayHello', () => {
    ...
     vscode.window.showInformationMessage('Hello World!');
       ...
    });
}

首先,咱们天然要引入vscode命名空间下的API,以得到控制VS Code的能力。

其次,咱们注意到了拓展的源文件须要export一个叫作activate的函数,当拓展的package.json文件中activationEvents所定义的事件被触发时,VS Code会调用activate函数。

最后,咱们能够看到咱们使用vscode的API注册了一个叫作“extension.sayHello”的命令,而且提供了它的具体实现,按照模板中的逻辑,“extension.sayHello”命令的逻辑是在窗口中显示一个内容为“Hello World”的信息消息。具体的效果在本文一开始的部分各位已经看到了。

那么接下来就十分简单了,咱们首先新建一个叫作HeaderGenerator的类,用来执行插入页眉的任务。

class HeaderGenerator {
    private _disposable: Disposable;
    public insertHeader() {

        // Get the current text editor 
        let editor = window.activeTextEditor; 
        if (!editor) { 
            return; 
        } 
        
        // Define header content
        var header = "License Header";
        
        // Get the document
        var doc = editor.document;
        
        // Insert header
        editor.edit((eb) => {
            eb.insert(doc.positionAt(0), header);
        });
    } 
    
     dispose() {
        this._disposable.dispose();
    }
}

插入页眉的具体执行是当前窗口的editor(TextEditor类)的edit方法,经过利用TextEditorEdit类的insert方法实现对当前的文档doc(TextDocument类)插入新的内容。

以后,咱们只需在extension.ts文件中的activate函数中实现调用HeaderGenerator类的insertHeader方法便可。

import * as vscode from 'vscode'; 
import {window, commands, Disposable, ExtensionContext, TextDocument} from 'vscode';

export function activate(context: vscode.ExtensionContext) {
    console.log('Congratulations, your extension "standard-header" is now active!'); 
    let headerGen = new HeaderGenerator();
    var disposable = vscode.commands.registerCommand('extension.insertHeader', () => {
        headerGen.insertHeader();
    });
    context.subscriptions.push(headerGen);
    context.subscriptions.push(disposable);
}

固然,为了增长快捷键“shift+cmd+1”的支持,咱们还须要在package.json中修改contributes的内容:

"contributes": {
    "keybindings": [{
        "command": "extension.insertHeader",
        "key": "shift+cmd+1",
        "when": "editorTextFocus"
    }]
},

以后,让咱们先进入拓展开发模式看一下效果。使用快捷键shift+cmd+1,咱们能够看到在VS Code的窗口中插入了页眉(截图中的页眉内容我已经换成了MIT协议)。
此处输入图片的描述
好了,到此咱们的小小的在源文件中插入页眉的拓展就完成了,下面就让咱们来使用安装、甚至是发布到VS Code的应用商店让你们一块儿分享咱们的劳动成果吧。

0x05 本地安装或在应用商店发布

为了避免必再每次都要进入拓展开发模式才能“使用”咱们的拓展,咱们就必须让本身的VS Code安装这个拓展。

事实上在本地安装供本身的拓展是十分简单而且方便的事情。VS Code会在.vscode/extensions文件夹中获取本地的拓展。而.vscode/extensions文件夹所在的位置咱们能够总结以下:

Windows %USERPROFILE%\.vscode\extensions
Mac $HOME/.vscode/extensions
Linux $HOME/.vscode/extensions

咱们只需拷贝一份咱们的拓展到.vscode/extensions文件夹中,VS Code在启动时便可以检查到该拓展了。

固然,除了本身使用本身开发的拓展以外,咱们还能够将拓展发布到VS Code的应用商店。此时咱们就要借助一个拓展发布工具——vsce了。

首先是安装vsce:

npm install -g vsce

以后咱们还须要到Visual Studio Team Services注册并登录,以获取Personal Access Token。

一旦获取了Personal Access Token,咱们就可使用vsce建立一个发布者帐号了。

vsce create-publisher

紧接着,登录并发布:

vsce login 
vsce publish

发布成功以后,咱们就能够在VS Code的应用商店中看到本身的拓展了。
此处输入图片的描述

而且在VS Code的命令面板中,咱们也可使用命令安装:

ext install

此处输入图片的描述好啦,行文至此,使用TypeScript拓展你本身的VS Code讲的已经差很少了。但愿各位多多交流~

相关文章
相关标签/搜索