ลองเขียน VS Code extension เพื่อเพิ่มประสิทธิภาพในการทำงาน

ปกติเวลาทำงานในเวลาจะใช้ Atom (Nuclide) ซึ่งมี extension หลายอันที่เขียนไว้เองมาก่อนแล้ว หรือ ที่ออฟฟิศเขียนไว้ให้ใช้แล้ว แต่พอกลับมาเขียน hobby project ก็เจอว่า extension ที่เคยใช้กลับไม่มีให้ใช้ จะใช้ที่มีอยู่ใน Visual Studio Marketplace ก็ได้ แต่รู้สึกว่าลองเขียนเองดีกว่า จะได้ลอง port ตัวที่มีอยู่ใน atom มาใช้ด้วย เลยเป็นที่มาของ blog นี้ครับ

ในการพัฒนา Extension ของ VS Code นั้น สามารถเริ่มต้นได้ง่ายโดยใช้ Yeoman ซึ่งเป็นเครื่องมือสำหรับ scaffold เพื่อช่วยเริ่มต้นในการพัฒนา โดยใช้ generator template ที่ทางทีม Visual Studio Code เตรียมไว้ชื่อว่า generator-code โดยขั้นตอนแรกสุดก็ต้องติดตั้งเครื่องมือให้พร้อมก่อนนะครับ โดยใช้ npm ติดตั้งตามนี้

npm install -g yo generator-code

ในการเริ่มพัฒนานั้นเราก็จะเริ่มจากการสร้าง project ด้วยคำสั่ง yo codeดังนี้ โดยทั่วๆไปก็มักจะเขียน Extension กันด้วย JavaScript ดังนั้นผมก็เลยเลือกท่ีจะเขียน Extension ด้วย TypeScript ด้วยความอยากลอง LOLz

1__rW57UmMfY37ooZa041Beg

โดยตัว code generator ก็จะถามรายละเอียดเกี่ยวกับ project ของเรา โดยอันที่สำคัญๆมีสองอันคือ

name — อันนี้จะใช้เป็น display name ใน Extension Marketplace ครับ เรียกว่าเป็น User friendly name ละกัน
identifier — อันนี้จะใช้เป็น folder name, project name และเป็นชื่อเรียกสำหรับในการ reference ในการ publish และการเรียกใช้ใน vscode ครับ

1_oXAP3QiKHPmncfde8YxaqQ

ถ้าเราเปิดดู project structure เราก็จะเห็นโครงสร้างเหมือนกับที่เห็นในภาพนี้นะครับ โดยส่วนสำคัญจะประกอบด้วย

./package.json เราจะต้องกำหนดรายละเอียดของ extension ว่าเราจะ provide คำสั่ง, เมนู หรือความสามารถอะไรให้กับ VS Code บ้าง
./README.md อันนี้ก็สำคัญไม่แพ้กัน เพราะรายละเอียดที่จะแสดงใน Marketplace และใน VS Code จะมาจากไฟล์นี้เองครับ
src/extension.ts อันนี้จะเป็นตัว extension ของเรานั่นเอง

1_At6BL10XMGAkqPga2tzmgw

เริ่มต้นจากการจัดการ package.json ให้เรียบร้อย

มาเริ่มต้นกันที่ package.json ก่อนนะครับ ไฟล์นี้เราจะต้องจัดการให้เสร็จก่อนที่จะทำการทดสอบ และ publish ไปที่ marketplace นะครับ เดี๋ยวจะใช้ไม่ได้ โดยตัวอย่างของ package.json ที่ผมสร้างขึ้นก็จะมีรายละเอียดดังนี้ครับ

{
	"name": "bigbears-toolbox",
	"displayName": "BigBears Toolbox",
	"description": "BigBears Toolbox",
	"version": "0.0.1",
	"publisher": "bigbears",
	"author": {
		"name": "Mahasak Pijittum"
	},
	"repository": {"url": "https://github.com/mahasak/bigbear-toolbox-vscode"},
	"engines": {
		"vscode": "^1.30.0"
	},
	"categories": [
		"Other"
	],
	"activationEvents": [
		"onCommand:extension.bigbearsCreateSnippet"
	],
	"main": "./out/extension.js",
	"contributes": {
		"commands": [
			{
				"command": "extension.bigbearsCreateSnippet",
				"title": "BIGBEARS : Create from Selected"
			}
		],
		"menus": {
			"editor/context": [
				{
					"command": "extension.bigbearsCreateSnippet",
					"group": "bigbears"
				}
			]
		}
	},
	"scripts": {
		"vscode:prepublish": "npm run compile",
		"compile": "tsc -p ./",
		"watch": "tsc -watch -p ./",
		"postinstall": "node ./node_modules/vscode/bin/install",
		"test": "npm run compile && node ./node_modules/vscode/bin/test"
	},
	"devDependencies": {
		"typescript": "^3.1.4",
		"vscode": "^1.1.25",
		"tslint": "^5.8.0",
		"@types/node": "^8.10.25",
		"@types/mocha": "^2.2.42"
	},
	"dependencies": {
		"await-fs": "^1.0.0"
	}
}

โดยส่วนที่ผมเพิ่มเข้าไปและแก้ไขจะประกอบด้วย

"publisher": "bigbears",
"author": {
    "name": "Mahasak Pijittum"
},
"repository": {
    "url": "https://github.com/mahasak/bigbear-toolbox-vscode"
},

และ

"activationEvents": [
    "onCommand:extension.bigbearsCreateSnippet"
],
"main": "./out/extension.js",
"contributes": {
    "commands": [
        {
            "command": "extension.bigbearsCreateSnippet",
            "title": "BIGBEARS : Create from Selected"
        }
    ],
    "menus": {
        "editor/context": [
            {
                "command": "extension.bigbearsCreateSnippet",
                "group": "bigbears"
            }
        ]
    }
},

ทีนี้มาทำความเข้าใจแต่ละ element ที่เราเพิ่มเข้าไปดีกว่า

publisher คืออะไร — publisher เป็น id ที่เราจะได้มาเพื่อใช้ในการ publish extension โดยขั้นตอนในการได้มานั้นก็ไม่ยากครับ โดยขั้นตอนแรกคือ เราต้อง register และสร้าง personal access token จาก https://dev.azure.com/ ก่อนนะครับ โดยสามารถสร้างได้จาก menu ด้านบนขวา -> security หลังจากนั้นก็เลือกสร้าง personal access token ได้เลย

1_nUqRizgMwSrwpzDgupr8Tw

จุดสำคัญคือ เราต้องสร้าง token นี้ให้สามารถ access ได้ทุก organization ของเราใน azure devops และเลือก security scope แบบ custom และเลือกทุกสิทธิ์ใน scope ของ marketplace

หลังจากนี้เราจะต้องการใช้เครื่องมืออีกตัวเพื่อสร้าง publisher id สำหรับ extension ของเรานะครับ เครื่องมือนี้เรียกว่า vsce (visual studio code extionsion) CLI โดยสามารถติดตั้งได้ดังนี้

npm install -g vsce

และหลังจากนั้น เราจะสร้าง publisher id ที่ใช้ใน package.json ของเราได้ด้วย

vsce create-publisher (publisher name)

หลังจากนั้นเราสามารถใช้ publisher id นี้ในการ publish extension ลงใน marketplace ได้แล้วครับ

author และ repository — property นี้ใช้สำหรับเพื่อบอกรายละเอียดของผู้พัฒนาและ repository นั่นเอง

activationEvents — เป็นกลุ่มของ configuration ที่จะบอกว่า extension ของเราจะถูก activate ด้วย signal อะไรนั่นเอง

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

ในตัวอย่างนี้เป็น config ที่บอกว่า extension นี้จะทำงานเมื่อมี command ชื่อว่า extension.bigbearsCreateSnippet ถูกเรียกใช้ ซึ่งอาจจะถูกเรียกใช้ด้วย command palette หรือ context menu ก็ได้นะครับ สามารถดูรายละเอียดของ activationEvents อื่นๆได้นะครับ

main — เป็น property ที่บอกว่าตัว extension ที่เราเขียนคืออันไหนครับ

contributes — เป็น attributes ที่เป็น mapping บอกว่า extension ที่เราเขียนนั้น ได้เพิ่มความสามารถอะไรให้ command palette หรือ เพิ่ม menu อะไรลงไปบ้างนะครับ ซึ่ง contribution points นั้นรวมไปถึง keybinding และอื่นๆอีกมากมายครับ

"commands": [
    {
        "command": "extension.bigbearsCreateSnippet", 
        "title": "BIGBEARS : Create from Selected"
    }
]

ใน commmands นี้บอกว่า registered function ที่ชื่อว่า extension.bigbearsCreateSnippet นั้นจะเป็น command ใน command paletteโดยมี label เป็น BIGBEARS : Create from Selected นั่นเอง

1_T6_YxD1J4IIs2ppJFR9ZpA

ส่วน config ด้านล่างนี้ก็จะบอกให้ VS Code เข้าใจว่า ด้วย extension นี้เราจะได้ context menu ที่เรียกใช้ command ที่เราได้ register ไปก่อนหน้านี้ และอยู่ในกลุ่มของ bigbears ใน context menu

"menus": {
    "editor/context": [
        {
            "command": "extension.bigbearsCreateSnippet",
            "group": "bigbears"
        }
    ]
}

1_ZyXX03aD9ELTrxuEvvIfUA

อันนี้เป็นส่วนของ package.json ครับ ถ้าเข้าใจแล้วเราก็ไปกันต่อเลย

โครงสร้างของ extension

เมื่อเราใช้ generator สร้าง extension ขึ้นมา เราก็จะได้ function skeleton ออกมาหน้าตาแบบนี้ครับ


import * as vscode from 'vscode';

export function activate(context: vscode.ExtensionContext) {
	console.log('Congratulations, your extension "idenExt" is now active!');
	
	let disposable = vscode.commands.registerCommand('extension.helloWorld', () => {
		vscode.window.showInformationMessage('Hello World!');
	});

	context.subscriptions.push(disposable);
}

export function deactivate() {}

function activate นั้นจะเป็น function ที่ expose ไปให้ตัว VS Code ใช้ในการ activate ความสามารถที่เราได้เพิ่มเข้าไปใน VS Code ครับ โดยมี parameter คือ context ซึ่งเป็นตัวแปรของ VS code extension context ครับ โดยมีส่วนสำคัญสองจุดคือ

let disposable = vscode.commands.registerCommand(‘extension.commandName’, ()=>{} ) 

ตรงนี้เป็นการ register arrow function ที่เราใช้ alias ว่า extension.commandName ว่าให้ทำอะไรบ้าง ซึ่งตอนนี้ใน code ที่ generator ที่สร้างมาให้เรา ใช้การสร้าง window และแสดง alert ขึ้นมาเมื่อถูกเรียกใช้นะครับ

context.subscriptions.push(disposable)

ตรงนี้จะเป็นการ subscribe ตัว function ที่เราสร้างขึ้นไปที่ context ตอนนี้ VS code ก็จะรับรู้การมีตัวตนของ extension ของเราแล้ว

เราสามารถ debug / run extension ของเราได้โดยใช้ debugging tools ของ VS Code ได้เลยโดยตรง

1_Ji8LZYbprr0GMyvU3i2PeA--1-

เมื่อเรากด Run extension ใน debugging tools ของ VS code ตัว VS Code ก็จะรัน local sandbox ขึ้นมาและติดตั้ง Extension ให้เราใช้ได้เลย เราสามารถกดเรียก command palette และค้นหา แล้วเรียก command ที่เราสร้างขึ้นมาได้เลย

เมื่อเราทดสอบ extension จนเป็นที่พอใจแล้ว เราก็สามารถ pubish extension ของเราขึ้นไปบน Marketplace ได้เลย โดยใช้คำสั่ง vsce publish ได้เลยนะครับ

vsce publish <major|minor|patch>

โดยถ้าเราใช้ major vsce จะทำการ update major version ของ extension ใน marketplace ให้เองอัตโนมัติ (minor และ patch เช่นกัน)

และเราก็จะสามารถใช้ extension ที่เราสร้างขึ้นมาได้อย่างเต็มที่

1_mf7DaYbtSFrznQpwS1vjbQ

ตัวอย่างของ extension คือ snippet creator ที่ผมทำเป็นตัวอย่างบางส่วนของความต้องการส่วนตัวของผมคือ เพื่อใช้ในการจัดการ code snippet เล็กๆส่วนตัวง่ายๆ เช่น ถ้าพิมพ์ ctor ผมก็จะสามารถเก็บ snippet ของ constructor ในแต่ละภาษาได้ตามที่ผมต้องการครับ

https://github.com/mahasak/bigbear-toolbox-vscode/blob/master/src/extension.ts

สุดท้ายนี้ก็ขอให้ทุกคนมีความสุขกับการ coding และเรียนรู้กับทุกๆวันของชีวิตครับ

Happy Coding !!!