9
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

画像ファイルを外部アプリで開く拡張機能 <VSCode拡張機能開発~公開まで>

Last updated at Posted at 2019-03-24

はじめに

VSCode の拡張機能を作ってみた。名前は何のひねりもなく OpenImageFile。その名の通り、Markdown から直接、画像ファイルを編集ソフトで開く拡張機能。なんてことはない拡張機能だけれども Markdown でマニュアル書いたりするときに効果絶大かと。

OpenImageFile.gif

目次

ことのはじまり

最近、簡易マニュアルを Markdown で書いたのだけれども、マニュアルといえばスクショ。スクショといえばマニュアル。
あんまりにもスクショが多いので、やってらんないということで、なんか便利な拡張機能ないかしら、と調べてみたらありました、PasteImage
この拡張機能はスクショを取った状態で、PasteImage すると自動的に保存して、マークダウンの記述も入れてくれるという素敵な拡張機能。

んなわけで、スクショとっては PasteImage しまくってご機嫌にマニュアルを作っていたのだけれど、ちょっと画像を編集してコメント入れたい、みたいな状況に置かれたときに、愕然とした。
PasteImage のファイル名は自動的に連番で振られるので、ファイル名見ただけでどのファイルかわからなくて、ファイル開くまでに手間がかかりすぎる!

というわけで、今度はカーソルの下にあるファイルを外部のアプリで開いてくれる拡張機能ないかしら、と調べてみたけど出てこない…。

無いなら仕方ない!、ということで作ることにしました、拡張機能。

使い方

  1. OpenImageFile にアクセスして、Install をクリック。
  2. 開きたいファイルパスがある行にカーソルを移動して、コマンドパレット(Ctrl+Shift+P)からOpenImageFileを選択すると、デフォルトではペイントが開く。
  3. 以下の設定を変更することで、編集ソフトを変更できる(ただし、編集ソフトがコマンド起動できる必要はある)
    Open Image File: Open Application

OpenImageFile.gif

作った

以下、VSCode の拡張機能を初めて作ったはなし。使ってみたいだけならスルーでOK。

環境・前提条件

環境と作成にあたっての前提条件は以下の通り。

環境

  • OS
    • Windows 10 (Windows_NT x64 10.0.17763)
  • エディタ
    • Visual Studio Code バージョン: 1.32.3 (system setup)

前提条件

  • Microsoftアカウントを持っている。
  • GitHubアカウントを持っている。

VSCode 拡張機能開発 こと始め

所属する組織では基本 C++ とか C# とか Python あたりを触っているので、Typescript なにそれ?おいしいの? というところからスタート。

ともかく、開発環境を整えないことには話が始まらない、という事で以下のページを参考にしながら、何とか HelloWorld までたどり着く。

次に、Typescript の雰囲気をもう少し知っておきたいという事で、Github から適当にシンプルな機能の拡張機能を漁ってきた。具体的には以下のあたり。

あと、わからないなりに MS の APIリファレンスも合わせて読んだ。

コーディング

そんで、Typescriptの雰囲気はなんとなくわかったので、続いて作りたいものを考えてみる。ざっくり処理を考えてみたところ、API的なのが必要なのは、以下の3つ。

  1. カーソルがある行のテキスト取得
  2. 文字列の正規表現処理(テキストからファイルパスを抜くための)
  3. 外部コマンドの発行処理

1と2については、Typescript について調べていた時になんとなくわかっていたが、3が少し手こずった。調べた結果、以下のサイトに解決手段を見つけた。

これで、パーツはそろったので、作ってみることに。なにしろTypescript はじめてなので、ホントにこの書き方でいいのかという疑惑が常に付きまといつつも、ちょぼちょぼとコーディング。

特に、相対パスから絶対パスに変換する resolve なんて、絶対にライブラリがあると思うのだけれども(path の中の resolve では機能不足だった)、どうやって調べりゃいいかもよくわからないので、とりあえずは自作。

で、出来上がったのは次のようなコード(ライブラリの調べ方は今後の課題)。

import {window, workspace} from 'vscode';
import * as child_process from 'child_process';
import * as path from 'path';

// メイン処理
export function execute()
{
	let editor = window.activeTextEditor;

	if(!editor){ return; }

	let doc = editor.document;

	if (doc.languageId === "markdown")
	 {
		let nLine = editor.selection.active.line;
		let linkMatchResult = doc.lineAt(nLine).text.match(/\!\[.*\]\((.*)\)/);

		if(linkMatchResult && linkMatchResult.length === 2)
		{
			let filePath = linkMatchResult[1] ;
			filePath = path.isAbsolute(filePath) ?
				filePath :
				resolve(path.dirname(doc.fileName), filePath);
			
			executeCommand(filePath);
		}
	}

	return;
}


// 相対パスから絶対パスに変換
export function resolve(baseDirPath: string, filePath: string)
{
	let filePathElements = filePath.replace(/\\/gi, "/").replace(/\/$/, "").split("/");
	let baseDirPathElements = baseDirPath.replace(/\\/gi, "/").replace(/\/$/, "").split("/");
	let nUpDir = 0;

	filePathElements.forEach(element => {
		if(element==="..")
		{
			nUpDir++;
		}
	});

	return (baseDirPathElements.length-nUpDir > 0) ? 
		baseDirPathElements.slice(0, baseDirPathElements.length-nUpDir).join("/") + "/" + filePathElements.slice(nUpDir, filePathElements.length).join("/") : 
		baseDirPathElements[0] + "/" + filePathElements.slice(nUpDir, filePathElements.length).join("/");
}


// アプリ起動のコマンド発行
export function executeCommand(filePath: string)
{
	let execCommand = getOpenApplication() + " \"" + filePath.replace(/\//gi, "\\"); + "\"";

	child_process.exec(execCommand, (error, stdout, stderror) => {});
}


// 設定(画像ファイルを開くアプリへのパス)を取得
export function getOpenApplication(): string
{
	const openImageFileConfiguration = workspace.getConfiguration('openImageFile');

	return openImageFileConfiguration.get("openApplication", "mspaint.exe");
}

で、これを openImageFile.ts として保存。あとは、extension.ts を次のようにして、いっちょ上がり(ホントか…)。


import {window, workspace, commands, Disposable, ExtensionContext, StatusBarAlignment, StatusBarItem, TextDocument} from 'vscode';

import * as openImageFile from './openImageFile';

export function activate(context: ExtensionContext) 
{
	let disposable = commands.registerCommand('openImageFile.openFile', () =>{
		openImageFile.execute();
		}
	);

	context.subscriptions.push(disposable);
}

export function deactivate() {}

あと、package.json を以下を参考に編集した。コーディングとデバッグできるようになったら、何をどこに書けばいいのかは、割とすぐにわかるようになると思う。

試験する

とりあえず、書いてみたはいいものの色々怪しい。特に resolve() が怪しい。怪しすぎる。というわけで、少なくとも正常系ではちゃんと動くことくらいは確認しておきたい。幸い yo code したら extension.test.ts ができていたので、ここに試験コードを書いて動きを確認。

import * as assert from 'assert';
import * as openImageFile from '../openImageFile';

suite("Open Image File Tests", function () {

    test("resolve", function() {
        assert.equal(openImageFile.resolve("C:\\hoge1\\hoge2\\hoge3\\hoge4\\hoge5\\hoge6", "../../../path.png"),  "C:/hoge1/hoge2/hoge3/path.png");
		assert.equal(openImageFile.resolve("C:\\hoge1\\hoge2\\hoge3\\hoge4\\hoge5\\hoge6\\", "../../../path.png"),"C:/hoge1/hoge2/hoge3/path.png");
        
        // 以下同様に続く…
    });

    test("getOpenApplication",  function(){
        assert.equal(openImageFile.getOpenApplication(),"mspaint.exe");
    });
});

内部の関数は extension.test.ts に試験コード書いたのだけれど、UIが絡む execute() の試験コードはどう書きゃいいんだよ!ってことでとりあえず自分が使いそうな範囲で Markdown を書いて、動作を確認。

あとは、1週間ほど使ってみて自分の使い方の範囲で問題がないことを確認。

公開する

折角作ったんだから、ということで GitHub と Marketplace に公開する事にした。

GitHub にソースを公開

これについては、別の記事にまとめた。公開した時の URL はこの後の package.json の編集で使うのでメモっておく。

ソースコードを GitHub に公開する方法

Marketplaceに公開

以下のサイトを参考にしながら拡張機能を Marletplace に公開した。この手の作業をするのが初めてだったってのもあって、正直ここが一番手間取った。

VSCode 拡張機能を作って公開してみた : non-italic-monokai

大まかな流れは以下の通り。

  1. Personal Access Token を作成
  2. Publisher を作成
  3. package.json を編集
  4. VSCE をインストール
  5. Marketplace に登録

Personal Access Token を作成

  1. Visual Studio Team Services | Sign In にアクセスして、右上のメニューから Security を選択。
    2019-03-24-12-51-55.png

  2. New Token をクリックして、NameOrganizationExpiriation を入力。また、ScopesFull access にしておく。ここでの注意は OrganizationAll accessible organization にしておくこと。 これを忘れててちょっとハマった…。で、Create ボタンをクリック。
    2019-03-24-14-01-42.png

  3. Personal Access Token が発行されるので、Copy ボタンをクリックして、別のところに保存しておく。この後 Close をクリックすると二度と取得できないので注意。
    2019-03-24-12-55-57.png

Publisher を作成

Manage Publishers & Extensions からパブリッシャーを作成。

2019-03-24-14-14-59.png

package.json を編集

package.json に以下を追加。

"repository": {
	"type": "git",
	"url": "<github の URL>"
},
"publisher": "<Publisher名>",

VSCE をインストール

拡張機能を公開するため用ツールとしてVSCEがMSから提供されているので、これをインストール。インストールは、コンソールを開いて以下を実行。

$ npm install -g vsce

Marketplace に登録

ここまでくれば、あとはもう少し。VSCode 拡張機能があるディレクトリでコンソールを開いて、以下のコマンドを実行

$ vsce login <Publisher名>
(PersonalAccessToken を入力する)
$ vsce publish

ちなみに、vsce login でエラー403が出た場合は、Personal Access Token の OrganizationAll accessible organization になっていない可能性が高いので、設定を確認するとよい。

最後に

はじめて拡張機能開発から Marketplace への登録をやってみた。開発自体は思ってたよりは手軽にできるな、という印象(まぁ、大したもの作ってないんだから当たり前だけど)。登録も手こずりはしたものの、一回やってしまえばあとはどうという事はないな、という印象。

ドキュメント作成にかかる工数を削減したい、というモチベーションは自分の中で根強くあるので、今後もちょぼちょぼと軽い拡張機能を作っていきたいと思う。(なお、念のために書いておくと、文章を打ち込む作業であったり、図表作成であったり、と思考以外の、作業の部分を効率化したい、というキモチ)

9
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?