VS Codeで文字列のちょっとした変換ができるんです!
〜実装まで覗くクイックツアー〜
VS Codeで文字列のちょっとした変換ができるんです!
〜実装まで覗くクイックツアー〜
- Event:
VS Code Conference JP 2024
- Presented:
2024/04/20 nikkie
お前、誰よ(自己紹介)
nikkie / 毎日 ブログ 執筆、連続520日突破
ソフトウェアエンジニアリングで突破するデータサイエンティスト(We're hiring!)
仕事もプライベートも VS Code で Python と戯れています
1年ぶりですね
このトークでは(fortee)
VS Codeで 文字列のちょっとした変換 をする方法を紹介
どのように実装されているか、少しだけ覗いてみよう(実装の話)
本題:文字列のちょっとした変換
英語の文字列(例えば、Emily Stewart)を すべて小文字 にしたい
手に馴染む言語(Python)でやってました
>>> "Emily Stewart".lower()
'emily stewart'
選択肢は色々ある
$ python -c 'print("Emily Stewart".lower())'
emily stewart
$ echo 'Emily Stewart' | tr '[:upper:]' '[:lower:]'
emily stewart
VS Codeでも、できます!!
テキストを選択
コマンドパレット
Transform で始まるコマンドを選択
コマンドパレットとは
押してみて!🫵
Linux または Windowsの方: Ctrl + Shift + P
macOSの方: ⇧⌘P (= Command + Shift + P)
デモ
VS Codeを操作して、Emily Stewartを小文字にする
Version: 1.88.1 (Universal)
OS: Darwin arm64 21.6.0
Transform to (1/2)
Lowercase |
emily stewart |
Uppercase |
EMILY STEWART |
Title Case |
Emily Stewart |
Transform to (2/2)
Snake Case |
emily_stewart |
Camel Case |
emilyStewart |
Pascal Case |
EmilyStewart |
Kebab Case |
emily-stewart |
例:Snake ➡️ Kebab
Transform to Kebab Case
sphinx_new_tab_link ➡️ sphinx-new-tab-link
自作ライブラリ の開発中にちょっと助かりそう
参考資料 🏃♂️
🏃♂️は本編ではスキップします。興味ある方向けです
コマンドパレットまわりで寄り道
Quick Open
コマンド
寄り道1️⃣ Quick Openとは
押してみて!🫵
Linux または Windowsの方: Ctrl + P
macOSの方: ⇧P (= Command + P)
> があるかないか
Quick Openで > と打ち込むと、コマンドパレット
コマンドパレットで > を消すと、Quick Open
時間に余裕があったら 行き来 するデモ
寄り道2️⃣ すべてはコマンド
コマンドパレットで Transform to コマンドを検索・実行 した
VS Code拡張開発でもコマンドを実装する
昨年の拡張開発の発表資料より
拙ブログ 🏃♂️
🌯まとめ:VS Codeで文字列のちょっとした変換ができるんです!
テキストを選択
コマンドパレット
Transform to コマンド
完
文字列のちょっとした変換にVS Codeを使ってみてください!
VS Codeで文字列のちょっとした変換ができるんです!
〜 実装まで覗くクイックツアー 〜
これらのコマンドはどう実装されている?
VS Codeの実装(TypeScript)を見ていくパート
コマンドごとにクラスがある
registerEditorAction()
関数
registerEditorAction(LowerCaseAction);
※コマンドパレットで提供される仕組みは、ここっぽいくらいにとどめます
Transform to コマンドに対応するクラス
UpperCaseAction
LowerCaseAction
TitleCaseAction
SnakeCaseAction
CamelCaseAction
PascalCaseAction
KebabCaseAction
共通のスーパークラス AbstractCaseAction
// https://github.com/microsoft/vscode/blob/1.88.1/src/vs/editor/contrib/linesOperations/browser/linesOperations.ts#L1088
export class LowerCaseAction extends AbstractCaseAction {
// 省略
}
Caseに関する 抽象 Action
サブクラスで具体のActionを実装する(例:小文字にする)
AbstractCaseAction
までの継承関係 🏃♂️
AbstractCaseAction
はGoFの Template Method パターン
// https://github.com/microsoft/vscode/blob/1.88.1/src/vs/editor/contrib/linesOperations/browser/linesOperations.ts#L1032
export abstract class AbstractCaseAction extends EditorAction {
public run(_accessor: ServicesAccessor, editor: ICodeEditor): void {
// 処理の中で _modifyText が呼ばれる
}
protected abstract _modifyText(text: string, wordSeparators: string): string;
}
穴埋め問題 になっている
_modifyText
: テキストの変更方法だけ、具体のCaseのActionで埋めればよい
_modifyText()
の実装を見る
LowerCaseAction
TitleCaseAction
1️⃣ LowerCaseAction
JavaScriptのメソッド を使う実装(
UpperCaseAction
も同様)(私の理解が雑すぎるかもですが、)TypeScriptは型付きのJavaScript
テストコードより
'HELLO WORLD'
➡️ 'hello world'
このメソッドで実装: toLocaleLowerCase()
呼び出した文字列の値を、ロケールに依存した対応付けに基づいて小文字に変換して返します。
LowerCaseAction
の _modifyText()
// https://github.com/microsoft/vscode/blob/1.88.1/src/vs/editor/contrib/linesOperations/browser/linesOperations.ts#L1098-L1100
protected _modifyText(text: string, wordSeparators: string): string {
return text.toLocaleLowerCase();
}
2️⃣ TitleCaseAction
正規表現 を使う実装
SnakeCaseAction
などなども同様
テストコードより
入力 |
出力 |
|
|
|
|
|
|
正規表現は、これだ!!
new RegExp('(^|[^\\p{L}\\p{N}\']|((^|\\P{L})\'))\\p{L}', 'gmu')
正規表現リテラルの作成は 2通り
スラッシュで囲む
/ab+c/g
new RegExp("ab+c", "g")
(👈今回はこちら)
MDN 正規表現の作成
RegExp
コンストラクタの引数
第1引数がパターン
\\p
は\p
の エスケープ
第2引数がフラグ
グローバル検索(g)・複数行の検索(m)・unicode(u)
独自クラス BackwardsCompatibleRegExp
🏃♂️
正確には、
RegExp
を返すBackwardsCompatibleRegExp
を定義した実装RegExp
インスタンス作成時に例外を送出したら、握りつぶしてnull
を返す(=後方互換)
正規表現を使った _modifyText()
// https://github.com/microsoft/vscode/blob/1.88.1/src/vs/editor/contrib/linesOperations/browser/linesOperations.ts#L1146-L1155
protected _modifyText(text: string, wordSeparators: string): string {
// 省略
return text
.toLocaleLowerCase()
.replace(titleBoundary, (b) => b.toLocaleUpperCase());
}
正規表現を使った replace()
置換文字列として関数を指定。シグネチャは
b
のみb
は 正規表現に一致した文字列キャプチャグループの
()
を(幾重も)含むが、このreplaceでは無視
MDN 置換文字列としての関数の指定
正規表現 '(^|[^\\p{L}\\p{N}\']|((^|\\P{L})\'))\\p{L}'
🤯
new RegExp('(^|[^\\p{L}\\p{N}\']|((^|\\P{L})\'))\\p{L}', 'gmu');
// /(^|[^\p{L}\p{N}']|((^|\P{L})'))\p{L}/gmu
titleBoundary
と呼ばれる(再掲)
\
をエスケープするために\\
\p{L}
一般カテゴリプロパティが L etter の Unicode文字の集合
文字にマッチ する。記号や空白文字にはマッチしない
3つの「または」( |
)
^
[^\\p{L}\\p{N}\']
((^|\\P{L})\')
これらのあとに \\p{L}
が続く
(1) ^
入力の先頭
mフラグで「改行文字の直後にも一致」
'(^)\\p{L}'
は 先頭の文字 に一致('hello world'
の'h'
)
MDN 境界型のアサーション
(2) 文字クラス [^\\p{L}\\p{N}\']
否定の
^
「UnicodeでLetterまたはNumber、またはシングルクォート」 でない
([^\\p{L}\\p{N}\'])\\p{L}
は'hello world'
の' w'
にマッチ
MDN 文字クラス 種類
(3) ((^|\\P{L})\')
\P{L}
は、\p{L}
の否定(すなわち、 文字でない )(^|\\P{L})
:先頭、または、文字でない
((^|\\P{L})\')\\p{L}
がマッチするもの
'\'physician\'s assistant\''
で'\'p'
にマッチ(先頭の方がマッチ)'hello \'world'
の' \'w'
にもマッチ(文字でない方がマッチ)
この正規表現の名は titleBoundary
タイトルケースにする上での 境界
'hello world'
であれば、境界に一致する部分文字列は'h'
と' w'
🌯要点:タイトルケースの境界部分を大文字にする
テキスト全体を 小文字 にする
タイトルケースの 境界部分 を 大文字 にする
// https://github.com/microsoft/vscode/blob/1.88.1/src/vs/editor/contrib/linesOperations/browser/linesOperations.ts#L1152-L1154
return text
.toLocaleLowerCase()
.replace(titleBoundary, (b) => b.toLocaleUpperCase());
拙ブログより 🏃♂️
🌯まとめ:VS Codeで文字列のちょっとした変換ができるんです!
コマンドパレット の Transform to コマンドで文字列を変換できる
コマンドごとにActionクラス。
_modifyText()
を実装する Template Method パターンUnicode文字クラスエスケープをはじめ、正規表現を駆使
ご清聴ありがとうございました
コマンドパレットで Enjoy coding!✨