ラベル Vim の投稿を表示しています。 すべての投稿を表示
ラベル Vim の投稿を表示しています。 すべての投稿を表示

2026年5月13日水曜日

Vim のステータスラインにカーソル下の文字コードを表示する

この記事はVim 駅伝の 2026-5-13 の記事です。 Vim 駅伝は常に参加者を募集しています。詳しくはこちらのページをご覧ください。


元々のステータスラインの設定が以下だとして、

set laststatus=2
set statusline=%<%f%h%m%r%y%=[%{&fenc!=''?&fenc:&enc}][%{&ff}][%l,%c%V]\ [%P]

文字コードの左隣にカーソル下の文字コードを表示するには、以下のように statusline を変更( %{CharCodeLabel()} を追加)したうえで、

set statusline=%<%f%h%m%r%y%=[%{CharCodeLabel()}][%{&fenc!=''?&fenc:&enc}][%{&ff}][%l,%c%V]\ [%P]

以下のような関数を定義すれば OK.

" カーソル下の文字の Unicode コードポイントを表示する関数
function! CharCodeLabel() abort
  " カーソル下の文字を取得
  let l:char = matchstr(getline('.'), '\%' . col('.') . 'c.')

  " 文字がない場合は空文字を返す
  if empty(l:char)
    return ''
  endif

  " 文字の Unicode コードポイントを取得
  let l:nr = char2nr(l:char)

  " コードポイントを U+XXXX 形式で返す
  return printf('U+%04X', l:nr)
endfunction

これで、 a の上にカーソルがあるときは U+0061 と表示されるようになる。

以上。

2026年3月2日月曜日

ローカル LLM でコード補完する Vim プラグインを作った

最近、ローカル LLM を使ってコード補完する Vim プラグインを作りました。以下はその概要と使用方法です。

前提

  • OS: Windows 11 Pro 25H2 ビルド 26200.7922
  • GPU: NVIDIA GeForce RTX 4070 Ti SUPER

Ollama のインストール

Download Ollama on Windows の手順通りにインストールします。

irm https://ollama.com/install.ps1 | iex

Qwen2.5-14B-Instruct のインストール

Ollama のライブラリに Qwen2.5-14B-Instruct があるので、以下のコマンドでインストール・実行します。

ollama run qwen2.5:14b-instruct

なんとこれだけ。

動作確認

実行で来たら対話を試みましょう。

>>> あなたが動いているプログラムと、自身のモデルを教えてください。
私はアリババクラウドによって作成された大規模な言語モデルです。私の名前はQwenで、ユーザーに多様な情報を提供し、さまざ
まな質問やタスクに対するサポートを提供することを目指しています。ただし、具体的なソフトウェアの実装詳細や内部構造につ
いては開示されていません。ユーザーの皆さまに対しては、幅広い情報提供とタスク支援を行うことを目的としています。

はい、動いていそうです。

run している間は API が立ち上がっているので、Vim プラグインからもアクセスできます。 そんなわけで、ローカル LLM を使ってコード補完する Vim プラグインを作ることにしました。

そうしてできたのが mikoto2000/ollama-codeassist.vim: ローカル LLM を用いたコード補完プラグイン です。 Vim でコード補完する際に、ローカル LLM を呼び出して補完候補を生成します。

mikoto2000/ollama-codeassist.vim

このプラグインは、Vim でコード補完する際に、ローカル LLM を呼び出して補完候補を生成します。 補完候補は、現在のカーソル行の前後のコードをコンテキストとして LLM に渡し、LLM が生成したコードを補完候補として挿入します。

基本的には、Vim meets Local LLM: Edit Text beyond the Speed of Thought - YouTube の前半部分を基に作成しました。

後半のバーチャルテキスト的なところは未実装です。

動作としてはこんな感じ。

とりあえず何らかのそれっぽいコードが挿入されているのがわかるかと思います。

主処理は 100 行にも満たない程度なので、どかっと現状のコードを張り付けておきます。

vim9script

var host = get(g:, 'ollama_codeassist_host', 'localhost')
var port = get(g:, 'ollama_codeassist_port', 11434)
var path = get(g:, 'ollama_codeassist_path', '/api/generate')
var model = get(g:, 'ollama_codeassist_model', 'qwen2.5-coder:14b')
# qwen2.5:14b-instruct
# codellama:13b

var endpoint_url = $"http://{host}:{port}{path}"

const AsyncHTTP = vital#ollamacodeassist#import("Web.AsyncHTTP")

const data_template = {
  "model": model,
  "prompt": null,
  "suffix": null,
  "stream": false,
  "options": {
    "stop": ["<|FIM_START|>", "<|FIM_STOP|>", "<|im_start|>", "<|im_end|>"],
  }
}
var data = copy(data_template)
var line = 0
var language = 'unknown'

def UserCb(response: any)
  if response.status == 200
    # コンテキストの行に、レスポンスの内容を挿入する
    var s = json_decode(response.content).response

    # もし \n が文字として入ってくる場合も吸収(保険)
    s = substitute(s, '\\n', "\n", 'g')

    # もし \u0000 が文字として入ってくる場合も吸収(保険)
    s = substitute(s, '\\u0000', "\%x00", 'g')

    # NUL を改行に変換
    s = substitute(s, "\%x00", "\n", 'g')

    # 改行で行分割して挿入
    var lines = split(s, '\r\?\n', 1)
    setline(line, lines[0])
    append(line, lines[1 : -1])
  else
    #echomsg response.status
  endif
enddef

# 現在のバッファからコンテキストを作成する関数
def CreateCurrentBufferContext()
  # 現在の行番号を取得
  line = line('.')

  # 現在のバッファから言語を推測
  if &filetype != ''
    language = &filetype
  endif

  # バッファ全体の内容を取得
  var buffer_prefix = $"// language: {language}\n" .. $"// Please only codes, and not output codeblock text.\n// Don't add closing brackets if they already exist in suffix.\n" .. join(getline(1, '.'), '\n')
  var buffer_suffix = '<|FIM_STOP|>' .. join(getline('.', '$'), '\n')

  data.prompt = buffer_prefix
  data.suffix = buffer_suffix
enddef

# コンテキストを基に、コード補完のリクエストを送る関数
def RequestInner()
  AsyncHTTP.request({
        \ 'method': 'POST',
        \ 'url': endpoint_url,
        \ 'data': json_encode(data),
        \ 'userCallback': function('UserCb'),
        \ })
enddef

export def Request()
  CreateCurrentBufferContext()
  RequestInner()
enddef

API リクエストは Vital.Web.AsyncHTTP に丸投げし、Ollama の API にリクエストを送っています。

ちょっと非同期リクエストを生かせない構造になっているのが課題ですが、まあとりあえず動いているのでいいかなと思っています。

以上。作ったモノの報告でした。

参考資料

2026年2月23日月曜日

Windows のターミナルで Vim の OSC52 プラグインのコピーのみを有効にする

環境

OS: Ubuntu24.04 on WSL2 on Windows 11 Pro Vim: 9.1.2011

なんでコピーのみを有効にするの?

Windows Terminal と wstty(putty) で試したんですが、これらターミナルは OSC52 のコピー機能をサポートしているのに対し、ペースト機能はサポートしていないようです。

そして、ペースト機能をサポートしていないターミナルでは、 Vim 内で完結するペースト操作も使用できなくなってしまいう用でした。

解決方法

Vimrc に以下の設定を追加します。

if !has('win32')
  if has("patch-9.1.1984")
    function! InitOsc52() abort
        let v:clipproviders["osc52"] = {
            \  "available": v:true,
            \  "copy": {
            \    "*": function('osc52#Copy'),
            \    "+": function('osc52#Copy')
            \  },
            \ }
    endfunction

    augroup init_osc52
      autocmd!
      autocmd VimEnter * call InitOsc52()
    augroup END

    packadd osc52
    let g:osc52_force_avail = v:true
    let g:osc52_disable_paste = v:true
    set clipmethod=osc52,x11,wayland

  endif
endif

v:clipproviders["osc52"] がキモなのですが、paste の設定を省略することで、ペースト機能を無効化しています。

ただし、 Vimrc のトップレベルにべた書きで定義すると、プラグインの初期設定で上書きされてしまうため、VimEnter イベントで初期化するようにしています。

こうすることで、何らかのタイミングがかみ合い、ペースト機能が有効になってしまうことを防いでいます。

Windows のターミナルで OSC52 のコピー機能のみを有効にしたい場合は、試してみてください。

2026年2月13日金曜日

Vim9 script 基礎文法最速マスターを AI に書かせようと頑張った

Vim9 script 基礎文法最速マスターを AI に書かせようと頑張った

本記事は次のソースを NotebookLM に取り込み生成したものを微調整した記事です。 Vim9 チートシート的なものが欲しかったのですが、思ったような品質まで上げるには結構な手間がかかりそうだったので諦めました。

その途中経過をおすそ分けです。

自分も Vim9 script 歴が浅いので、内容に誤りがある可能性があります。もし誤りを見つけた場合は、コメントやフィードバックをいただけると幸いです。

はじめに

2010年、Vim 7.2をベースに執筆された伝説的な記事「Vimスクリプト基礎文法最速マスター - 永遠に未完成」は、多くのVim使いにとってのバイブルでした。しかし、現在のVimは「Vim9 script」という劇的な進化を遂げています。

本稿の目的は、Vim 7.2時代のレガシーな記述を卒業し、より高速で厳格な現代的パラダイムへと最短で移行することです。

必須の宣言と基本構造

Vim9 scriptを記述するには、ファイルの先頭に必ず vim9script 宣言が必要です。

  • Vim9固有の仕様(ソース外情報):
vim9script

変数宣言(s: 接頭辞は不要になった)

var count = 0

専門テクニカルライターの視点: 従来のVim scriptでは、スクリプトローカルな変数や関数に s: 接頭辞を付けるのが通例でした。Vim9 scriptでは、この宣言があるファイル内ではデフォルトでスクリプトローカルとして扱われるため、煩雑な接頭辞から解放されます。

コメントの書き方

Vim9ではコメント形式が # に刷新され、構文上の曖昧さが解消されました。

  • 従来形式 : " から行末まで。ただし、:map 系コマンドや特定の引数内ではコメントとして機能しないという「キモい制約」がありました。
  • Vim9形式(ソース外情報): # を使用します。

専門テクニカルライターの視点: 従来の " は文字列リテラルと混同されやすく、特にマッピング定義などで予期せぬ挙動を招くリスクがありました。# の導入により、他のモダンなスクリプト言語と同様の安全性と直感的な記述が可能になっています。

変数宣言と代入(var, const vs let)

代入に :let コマンドを使用していた時代は終わり、varconst による厳格な宣言が導入されました。

  • 比較表
機能 従来 (Vim 7.2) Vim9 script(ソース外情報)
変数宣言 let a = 1 var a = 1
定数宣言 (なし) const b = 2
型変更 let a = 1 → let a = “s” (可能) 禁止 (コンパイルエラー)
変数削除 :unlet a 禁止 (var 変数は削除不可)

専門テクニカルライターの視点: Vim9では静的型チェックが行われるため、一度数値として宣言した変数に文字列を代入することはできません。また、var で宣言したローカル変数は :unlet で消す必要はなく(そもそも禁止)、メモリ管理と予測可能性が向上しています。

データ型とリテラル

基本的な型は継承されていますが、演算子や表記に重要な変更があります。

  • 数値: 整数と小数(Vim 7.2導入)をサポート。
  • 文字列:
    • ダブルクォート(特殊文字展開あり)とシングルクォート(展開なし)を使い分ける。
    • 結合: 従来は . でしたが、Vim9では .. を推奨(ソース外情報)。
  • リスト: var list = [1, 2, 3]
  • 辞書: var dict = {key: 1} (ソース外情報:キーが英数字・ハイフン・アンダースコアのみならクォート省略可)
  • 関数参照 (Funcref)

関数参照はこの記事では省略します。

専門テクニカルライターの視点: 結合演算子に .. が推奨されるのは、. を使用すると浮動小数点数(例: 1 . 'a')との判別が困難になるという曖昧さを排除するためです。

ソースに基づき、Vim9 scriptでの数値計算と計算時の注意点をまとめた「四則演算」の章を作成しました。

この内容は、ソースにあるVim 7.2当時の仕様 を踏まえつつ、Vim9 scriptの文法(var#)に適合させたものです。


数値・四則演算

基本演算子

一般的なプログラミング言語と同様の演算子が使用可能です。 * + (加算) * - (減算) * * (乗算) * / (除算) * % (剰余)

var a = 1 + 1  # 2
var b = 5 - 2  # 3
var c = 2 * 3  # 6
var d = 3 % 2  # 1

複合演算子

+- については、値を直接更新する複合演算子が利用可能です。

var i = 1
i += 2  # 3
i -= 1  # 2

※ソースによれば、*=/= は使用できないため注意してください。

整数と小数の割り算

Vimスクリプトの重要な仕様として、整数同士の割り算の結果は整数のまま(切り捨て)になります。 小数(Float)の結果を得たい場合は、少なくとも一方を小数として記述する必要があります。

var val1 = 3 / 2      # 1
var val2 = 3 / 2.0    # 1.5

型の暗黙的な変換

数値演算などの特定の箇所では値が暗黙的に変換されることがありますが、整数値と小数値の変換は暗黙的には行われません。変換が必要な場面で適切な型でない場合はエラーになるため、注意が必要です。

文字列操作

  • 結合: .. または join(list, sep)
  • 分割: split(str, sep)
  • 長さ: strlen(str)
  • 検索: stridx(haystack, needle)
  • スライス: str[start : end] で部分抽出が可能。
    • 'abcd'[1 : 2]'bc''abcd'[ : 1]'ab'

リスト操作

  • 基本操作: len() で長さ取得、add() で末尾追加、insert() で位置指定挿入 [3]。

  • 削除: remove(list, index) で要素を削除・取得 [3]。

  • 参照と代入: listlist[-1](末尾)で参照し、list = val で書き換え [3]。

  • 展開代入: 複数の変数に一括代入可能 [4, 5]。

    var [a, b] = [2, 6]
    var [first, second; rest] = [1-3, 6, 7] # rest は [1, 3, 7]

    ソース に基づき、Vim9 script の文法に合わせた「辞書(Dictionary)操作」の章を作成しました。


辞書 (Dictionary)

辞書は、他の言語でハッシュテーブルや連想配列と呼ばれているデータ構造です。キーには文字列のみ使用できます。

辞書の表現

var dict = {}                 # 空の辞書
var colors = {'red': '#ff0000', 'blue': '#0000ff'}

要素の参照と代入

インデックス(ブラケット)によるアクセスのほか、ドット記法も利用可能です。

var r = colors['red']         # インデックスで参照
var b = colors.blue           # ドット記法で参照

colors['red'] = 'RED'         # 代入
colors.green = '#00ff00'      # 新しいキーの追加代入

主要な操作関数

辞書を操作するための便利な組み込み関数が用意されています。

  • keys(dict): 全てのキーをリストで取得します。
  • values(dict): 全ての値をリストで取得します。
  • items(dict): 全てのキーと値を [[key, value], ...] の形式のリストで取得します。
  • has_key(dict, key): 指定したキーが存在するか確認します(真なら 1、偽なら 0)。
  • remove(dict, key): 指定したキーの要素を削除し、その値を返します。

辞書の走査 (for文)

items() を使うことで、キーと値を同時に取り出してループ処理ができます。

for [key, val] in items(colors)
  echo key .. ': ' .. val
endfor

高度な操作 (map, filter)

リストと同様、map()filter() を使って辞書の中身を一括操作できます。Vim9 script ではラムダ式を使うのが一般的です。

# 全ての値を大文字に変換 (map)
map(colors, (key, val) => toupper(val))

# 特定の条件に合う要素のみ残す (filter)
filter(colors, (key, val) => key =~ '^r') # キーが 'r' で始まるものだけ

map()filter()元の辞書を直接書き換える破壊的な操作である点に注意してください。

制御構文(if, for, while)

制御構文はより規律ある構造へと進化しました。

if 文

if condition
    # 処理
elseif other_condition  # else if ではなく elseif である点に注意
    # 処理
endif

while 文(比較)

  • 従来形式:
let i = 0
while i < 5
    let i += 1
endwhile
  • Vim9形式:
var i = 0
while i < 5
    i += 1  # varで宣言した変数は let なしで更新可能
endwhile

for 文

リストを走査します。

for e in range(3)  # range() は連番リストを生成
    echo e
endfor

関数定義(def vs function)

Vim9の白眉は、従来の :function を代替する :def です。

  • 比較コード
特徴 従来形式 (:function) Vim9形式 (:def)(ソース外情報)
引数アクセス a:var が必須 a: 接頭辞は不要
スコープ 関数ローカル ブロックスコープ
実行 逐次解釈(低速) コンパイル(高速)

Vim9の関数定義

def MyFunc(msg: string): number
    var len = strlen(msg)  # ブロックローカルな変数
    return len
enddef

専門テクニカルライターの視点: :def で定義された関数は、初回呼び出し時にバイトコードへコンパイルされ、静的型チェックが行われます。実行速度が劇的に向上するだけでなく、実行前にエラーを検知できる堅牢性を備えています。

Vim9特有の高度な機能

ソースの時代には存在しなかった、構造化プログラミングのための最新機能です。

  • クラス (class)(ソース外情報):
class Robot
    var name: string
    def Hello()
        echo "I am " .. this.name
    enddef
endclass
  • ラムダ式(ソース外情報):
var Filtered = filter([1, 2, 3], (k, v) => v > 1)
  • 可変長引数: 従来は ...a:000 でしたが、Vim9では ...name: list<type> と記述し、型安全なリストとして扱います(ソース外情報)。

実行と例外処理

  • 実行: :source script.vim または vim -S script.vim
  • 例外処理:
try
    # 処理
catch /pattern/  # 正規表現でエラーを捕捉
    # エラー処理
finally
    # 終了処理
endtry

まとめ

Vim9 scriptは、2010年当時の「コマンドの羅列」という側面を保ちつつ、レキシカルスコープや静的型付け、コンパイルといった現代的なプログラミング言語の恩恵をVimにもたらしました。

本稿で触れたのは基礎の入り口に過ぎません。Vim9の真価を理解するには、公式ドキュメントが唯一無二のガイドとなります。2010年当時からの変わらぬ鉄則を胸に、Vimをさらに深く使いこなしましょう。

「可能な限り :help を参照しましょう」

  • :help vim9
  • :help vim9-functions
  • :help vim9-classes

参考資料

2026年2月2日月曜日

ファイル名補完を「カレントディレクトリから」ではなく「開いているファイルのディレクトリから」にしたい

この記事はVim駅伝の2026-2-2 の記事です。

Vim では <C-x><C-f> で、「カレントディレクトリからのファイル名補完」ができます。

Markdown などで資料を書いていると、リンク先の記述をするために「そのファイルのディレクトリからのファイル補完」がしたくなることがあります。

ただ、資料のビルドなどがあるためカレントディレクトリは docroot から動かしたくありません。

そんな願いをかなえるスクリプトを作ったので共有します。

やり方としては、「補完開始直前に lcd でカレントディレクトリを動かして、補完完了後に元に戻す」という感じです。

同じ挙動が良いという方は以下スクリプトを使ってみてください。

" カレントディレクトリを開いているファイルのディレクトリへ移動
function! file_complete_extension#start_lcd_for_insert_completion() abort
  if exists('b:_saved_lcd')
    return ''
  endif

  " 現在のウィンドウの cwd を保存
  let b:_saved_lcd = getcwd(-1)

  " 開いているファイルのディレクトリへ移動
  let l:dir = expand('%:p:h')
  if !empty(l:dir)
    execute 'lcd' fnameescape(l:dir)
  endif
  return ''
endfunction

" CompleteDone で restore_lcd を発火させる
augroup restore_lcd_after_completion
  autocmd!
  autocmd CompleteDone * call file_complete_extension#restore_lcd()
augroup END

" b:_saved_lcd に戻る
function! file_complete_extension#restore_lcd() abort
  " b:_saved_lcd があればそこに戻る
  if exists('b:_saved_lcd') && !empty(b:_saved_lcd)
    execute 'lcd' fnameescape(b:_saved_lcd)
    " 後始末、 b:_saved_lcd を unlet
    unlet b:_saved_lcd
  endif
endfunction

inoremap <expr> <C-x><C-f>
      \ file_complete_extension#start_lcd_for_insert_completion() . "\<C-x>\<C-f>"

2025年12月8日月曜日

Vim のマクロで気持ち良くなった話 - Java POJO から SQL の INSERT 文を作る

SQL の INSERT 分の作成

こんな SQL を作りたい

MyBatis の書き方ですね。

INSERT INTO
TodoEntity
(
  id,
  title,
  done
)
VALUES
(
  #{id},
  #{title},
  #{done}
)

これに対応する Entity クラスが以下。

package dev.mikoto2000.todo.entity;

import lombok.AllArgsConstructor;
import lombok.Data;

/**
 * TodoEntity
 */
@Data
@AllArgsConstructor
public class TodoEntity {
  private long id;
  private String title;
  private boolean done;
}

フィールドとカラムが 1 対 1 で対応しているので、マクロでどうとでもできるはず!

やってみた

という事でやってみた。頑張って動画からキーストロークを読み取ってください…(キーストロークを文章でまとめる気力が無かった…)

第 1 フェーズ: カラムの作成

次のようにカーソルを配置したうえで、

^WWvey<C-w><C-w>pa,<C-w><C-w>j をマクロで記録、 @q で繰り返し、最終行のカンマを消す。

第 2 フェーズ: 値の作成

次のようにカーソルを配置したうえで、

^WWvey<C-w><C-w>a#{<C-r>"}<C-w><C-w>j をマクロで記録、 @q で繰り返し、最終行のカンマを消す。

第 3 フェーズ: フォーマット

後は ggVG= でフォーマットを書ければ終了。

以下、同じようなことをしている動画。

こんな形でウィンドウをまたいだマクロでファイルを作っていくのも便利ですよ!

以上!

2025年8月25日月曜日

Vim のマクロで気持ち良くなった話 - Java フィールドから SQL の SELECT 定義を作成編

Java Bean でエンティティ的なものを作って、そのフィールド値を(ほぼ)全部 SELECT で撮ってきたいと思う場面は多いと思います。

ざっくりやってみたら意外とうまくいったので記録。

User.java

public class User {
  /**
   * 氏名
   */
  private String name;
  /**
   * グループ ID
   */
  private Integer groupId;
  /**
   * Role ID
   */
  private Long roleId;
  /**
   * 住所
   */
  private String address;
  /**
   * 電話番号
   */
  private String phoneNumber;
}

最初のコメントの始まりにカーソルを合わせ、以下キーストロークをうつと、フィールド名だけを抽出できる。

これを q に保存して @q で全部フィールド名だけを抽出する。

VjjdvWWhx$r,j

すると、こうなる。

public class User {
  name,
  groupId,
  roleId,
  address,
  phoneNumber,
}

後は lower snake case にすればいいので、対象のフィールドの行で以下キーストロークを実行。

^ve:s/\C\([A-Z]\)/_\1/g<Enter>veuj

これも q に記録して、対象行で @q していく。

すると以下のようになる。

public class User {
  name,
  group_id,
  role_Id,
  address,
  phone_number,
}

後は先頭と末尾を SELECT 文ににしてケツカンマを削除。

select
  name,
  group_id,
  role_Id,
  address,
  phone_number
from
  "user";

OK, 以上。

以下、似たようなことをやっている動画です。

2025年8月22日金曜日

Vim のマクロで気持ち良くなった話 - Java のフィールド定義編

エクセルのインタフェース仕様書とかに、こんな形で入力や出力のパラメーター情報が書かれていたりする。

これをいちいち手作業で Java のフィールドとして記載していくのは骨が折れるため、 Vim のマクロで何とかした。

論理名 物理名 データ型 …(略)
氏名 name String
住所 address String
電話番号 phoneNumber String

論理名からデータ型までの列をコピーして Vim に張り付けると、以下のようなテキストになる。

氏名  name    String
住所  address String
電話番号    phoneNumber String

氏名の行の先頭にカーソルを置き、以下のように入力していく。

i/**<Enter><BS> * <ESC>Ea<Enter>*/<Enter>private <ESC>Elvx<ESC>vexbPa <ESC>A;<ESC>j0

すると、氏名の行が以下のようになる。

/**
 * 氏名
 */
private String name;

このストロークを qq で記録しておき、 @q を 2 回実行すれば他の行も以下のようになる。

/**
 * 住所
 */
private String address;
/**
 * 電話番号
 */
private String phoneNumber;

うーん、便利。

以下、似たようなことをやっている動画です。

2025年4月7日月曜日

devcontainer.vim で Claude Code を使う

AI コーディングというやつをやってみたかったのです。

前提

  • OS: Windows 11 Pro 23H2 ビルド 22631.5039
  • Docker Desktop: Version 4.37.1 (178610)
  • WSL2 の Ubuntu 24.04 から WSL Integration で Docker Desktop を利用
  • Java プロジェクトを作って、 Hello,World! するのが目標
  • Claude Code のアカウントを作って $5 入金済み

devcontainer 用のファイル作成

.devcontainer/devcontainer.json

  • Maven の Java プロジェクトを作りたいので、 devcontainer の features を利用してインストール
  • Claude Code に Node が必要なので、 devcontainer の features を利用してインストール
{
  "name": "app",
  "image": "mcr.microsoft.com/devcontainers/base",
  "features": {
    "ghcr.io/devcontainers/features/node:1": {},
    "ghcr.io/devcontainers/features/java:1": {
      "version": "21",
      "installGradle": "false",
      "installMaven": "true"
    }
  }
}

.devcontainer/devcontainer.vim.json

  • devcontainer.vim config -g > .devcontainer/devcontainer.vim.json で生成したものそのまま使う
{
  "remoteEnv": {
    "EDITOR": "~/squashfs-root/AppRun",
    "PAGER": "sed -r 's/\\x1B\\[[0-9;]*[mGKH]//g' | ~/squashfs-root/AppRun -R -",
    "LESSCHARSET": "utf-8",
    "SHELL": "bash",
    "TERM": "xterm-256color",
    "HISTCONTROL": "erasedups",
    // If use WSLG
    // "DISPLAY": "${localEnv:DISPLAY}",
    // "WAYLAND_DISPLAY": "${localEnv:WAYLAND_DISPLAY}",
    // "XDG_RUNTIME_DIR": "${localEnv:XDG_RUNTIME_DIR}",
    // "PULSE_SERVER": "${localEnv:PULSE_SERVER}",
  },
  // devcontainer/cli はまだ forwardPorts に対応していないため、
  // 必要に応じて forwardPorts の定義を appPort に転記する。
  // ※ コンテナ側で Listen する際は、 `127.0.0.1` **ではなく** `0.0.0.0` で Listen すること。
  // "appPort": [
  // ],
  // Linux で実行する場合には、 runArgs をコメントアウトし、コンテナからホストへの接続ができるようにしてください
  //"runArgs": [
  //  "--add-host=host.docker.internal:host-gateway"
  //],
  "mounts": [
    {
      "type": "bind",
      "source": "${localEnv:HOME}/.vim",
      "target": "/home/vscode/.vim"
    },
    {
      "type": "bind",
      "source": "${localEnv:HOME}/.gitconfig",
      "target": "/home/vscode/.gitconfig"
    },
    {
      "type": "bind",
      "source": "${localEnv:HOME}/.ssh",
      "target": "/home/vscode/.ssh"
    },
    // If use host's bashrc
    //{
    //  "type": "bind",
    //  "source": "${localEnv:HOME}/.bashrc",
    //  "target": "/home/vscode/.bashrc"
    //},
    // If use WSLG
    //{
    //  "type": "bind",
    //  "source": "/tmp/.X11-unix",
    //  "target": "/tmp/.X11-unix"
    //},
    //{
    //  "type": "bind",
    //  "source": "/mnt/wslg",
    //  "target": "/mnt/wslg"
    //},
  ],
  // denops など、別の実行環境が必要な場合や、
  // 後乗せで追加したいツールがある場合には以下の対象行をコメントアウトするか
  // https://containers.dev/features から必要な feature を探して追加してください。
  //"features": {
  //  "ghcr.io/devcontainers-community/features/deno:1": {}
  //  "ghcr.io/devcontainers/features/node:1": {}
  //  "ghcr.io/devcontainers/features/python:1": {}
  //  "ghcr.io/devcontainers/features/ruby:1": {}
  //  "ghcr.io/devcontainers-extra/features/fzf:1": {}
  //  "ghcr.io/jungaretti/features/ripgrep:1": {}
  //  "ghcr.io/devcontainers/features/docker-outside-of-docker:1": {},
  //  "ghcr.io/devcontainers/features/docker-in-docker:2": {}
  //}
}

開発環境の起動

devcontainer.vim start .

Claude Code のインストール・初期設定

インストール

Vim でターミナルを開き、以下コマンドを実行。

npm install -g @anthropic-ai/claude-code

初期設定

Vim でターミナルを開き、以下コマンドを実行。

cloude
  1. 表示のカラースタイルを聞かれるので答える(僕は Light text にした)
  2. ログイン処理 2.1 Enter 押下で表示される URL にアクセスし、ログインを行う 2.2 ログイン完了後に表示されるコードをターミナルにペーストし、 Enter
  3. Yes, proceed まで Enter を押し続ける
  4. プロンプト入力画面が表示される

あとはここにやって欲しいことを入力していくだけ。

実装

プロジェクトの作成

claude へ次の命令を入力する。

Maven の Java プロジェクトを作り、 Hello, World! を表示したい。 Java のバージョンは 21 とすること。

今回は、以下のような処理を行ってくれた。

  1. mkdir -p src/main/java/com/example

  2. pom.xml の作成

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.example</groupId>
        <artifactId>hello-world</artifactId>
        <version>1.0-SNAPSHOT</version>
    
        <properties>
            <maven.compiler.source>21</maven.compiler.source>
            <maven.compiler.target>21</maven.compiler.target>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        </properties>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-jar-plugin</artifactId>
                    <version>3.3.0</version>
                    <configuration>
                        <archive>
                            <manifest>
                                <mainClass>com.example.App</mainClass>
                            </manifest>
                        </archive>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </project>
  3. App.java の作成

    package com.example;
    
    public class App {
        public static void main(String[] args) {
            System.out.println("Hello, World!");
        }
    }
  4. コンパイル(mvn compile)

  5. 実行(mvn exec:java -Dexec.mainClass="com.example.App")

  6. 完了報告(Maven プロジェクトが正常に作成され、「Hello, World!」が表示されました。Java 21 を使用する Maven プロジェクトの基本構造を作成しました。)

良い感じ。

devcontainer.vim 特有のことが無かった気がするけれど、 Claude Code が使えたのでヨシ!

以上。

参考資料

2025年3月26日水曜日

Vim のマクロを使って気持ち良くなった話 - 表のデータを TS の enum にする

こんなデータから、こんなデータを作りたい

PDF に以下のような表がありました。

項目1 項目2
1
2
× 3

実際は行が 200 行くらいあります。

これを、以下のような TypeScript の enum にしたいのです。

enum Type {
= 1;
= 2;
= 3;
}

どうやったか?

まず、 PDF の表から文字列をコピペすると、以下のようなテキストデータになりました。



1


2

×
3

これを、前述の enum の形式にして、 200@q で気持ちよくなりたいわけです。

処理する順番としては以下のようにしました。

  1. 「項目2」 を消す
  2. 「項目1」の行末尾に = を追加
  3. 「項目1」と「値」 の行を結合
  4. 「3.」で結合した末尾に ; を追加
  5. 次の行の先頭にカーソルを配置

こうしてできた文字列を enum 内にコピペしてフォーマッタをかければ enum 定義の完成です。

ではやってみましょう。

<ここにカーソルがある>松

1


2

×
3

という状態から始め、 jddk で「項目2」の行を削除します。(1.)

<ここにカーソルがある>松
1


2

×
3

つづいて A = <Esc> で「項目1」の末尾に = を追加します。(2.)

すると以下の状態になりますになります。

松 = <ここにカーソルがある>
1


2

×
3

J で行を結合します。(3.)

松 = <ここにカーソルがある>1


2

×
3

A;<Esc> で末尾に ; を追加します。(4.)

松 = 1;<ここにカーソルがある>


2

×
3

マクロの連続実行ができるように、j^ で次の行の先頭にカーソルを持っていきます。(5.)

松 = 1;
<ここにカーソルがある>竹

2

×
3

この操作を記録して、 200@q とかすれば、他の項目も全て enum の形式になります。やったね。

2025年3月19日水曜日

Vim のマクロを使って気持ち良くなった話 - モックの JSON データ作成・編集編

モックデータの新規作成

こんなデータを作りたい

ホストにぶら下がっているエッジが大量にあり、そのエッジのステータスを取得する API がある。 その API のモックデータを作りたい。

{
    "type", "電球",
    "number", 1,
    "name", "電球1",
},
{
    "type", "電球",
    "number", 2,
    "name", "電球2",
},
{
    "type", "電球",
    "number", 3,
    "name", "電球3",
},
... これが 120 個くらい続く

どうしたか?

何個かデータを追加していくと、キー操作のパターンが見えてきました。

{
    "type", "電球",
    "number", 1,
    "name", "電球1",
},
<ここにカーソルがある>{
    "type", "電球",
    "number", 2,
    "name", "電球2",
},

上記状態で、qqVjjjjyjjjjpjj<C-a>j<C-a>kkkq としたあと、 117@q と打って気持ちよくなりました。 (実際は何個かやって感覚をつかんでからなので、 114@q くらいだった気がする…)

モックデータの修正

こんなデータを作りたい、既にデータはある

先ほど作ったデータが、実は間違いでした…。 本当は「電球60個に人感センサ60個」なのでした。

{
    "type", "電球",
    "number", 1,
    "name", "電球1",
},
{
    "type", "電球",
    "number", 2,
    "name", "電球2",
},
{
    "type", "電球",
    "number", 3,
    "name", "電球3",
},
... これが 60 個続く
{
    "type", "人感センサ",
    "number", 61,
    "name", "人感センサ1",
},
{
    "type", "人感センサ",
    "number", 62,
    "name", "人感センサ2",
},
{
    "type", "人感センサ",
    "number", 63,
    "name", "人感センサ3",
},
... これが 60 個続く

どうしたか?

このデータだけ見ると、「人感センサについてさっきと同じことすればいいじゃん」と思うでしょう。その通りです。

ただ、考えるのが面倒でこのデータには入れていませんが、既に使われているモックデータのため、「type, name 以外のパラメーターを変更したくない」という制約があったのです。

なんでそんな状況になったか覚えていませんが、そんな状況だったのです。

という事で、制約を守った編集ができるような手順を、マクロ含みで考えました。

まずは、61 個目以降のtype, name の数値部分以外を人感センサに一括置換します。

そうすると、以下のようなデータになります。

{
    "type", "電球",
    "number", 1,
    "name", "電球1",

},
{
    "type", "電球",
    "number", 2,
    "name", "電球2",
},
{
    "type", "電球",
    "number", 3,
    "name", "電球3",
},
... これが 60 個続く
{
    "type", "人感センサ",
    "number", 61,
    "name", "人感センサ61",
},
{
    "type", "人感センサ",
    "number", 62,
    "name", "人感センサ62",
},
{
    "type", "人感センサ",
    "number", 63,
    "name", "人感センサ63",
},
... これが 60 個続く

後は簡単ですね。

以下の位置にカーソルを移動し、 qq60<C-x>jjjjj^q59@q で完了です。

{
    "type", "電球",
    "number", 1,
    "name", "電球1",
},
{
    "type", "電球",
    "number", 2,
    "name", "電球2",
},
{
    "type", "電球",
    "number", 3,
    "name", "電球3",
},
... これが 60 個続く
{
    "type", "人感センサ",
    "number", 61,
<ここにカーソルがある>    "name", "人感センサ61",
},
{
    "type", "人感センサ",
    "number", 62,
    "name", "人感センサ62",
},
{
    "type", "人感センサ",
    "number", 63,
    "name", "人感センサ63",
},
... これが 60 個続く

これで気持ちよくなれました。

以上です。

2025年2月28日金曜日

Vim でカーソル下の文字コード取得と文字コードでの入力を行う

使いどころはパッと思いつかないけど、過去に何度かお世話になったので備忘録として記す。

カーソル下の文字コード取得

ga コマンドで取得できる。

例えば、 にカーソルを合わせて ga を押すと、以下のように情報が表示される。

<→ > 8594, Hex 2192, Oct 20622, Digr ->

文字コードでの入力

インサートモード中に <C-v> を押すことで、文字コードによる入力ができる。

例えば、前述の を入力したい場合には i<C-v>u2192 と入力する。

以上。

参考資料

2025年1月24日金曜日

ddt.vim を試す

Vim 上で動くターミナルに興味があるので、 denops の環境構築から試していく。

前提

今回使った devcontainer.json:
{
  "name": "ddt.vim study",
  "image": "ubuntu:24.04",
  "features": {
    "ghcr.io/devcontainers-community/features/deno:1": {}
  }
}

必要パッケージのインストール

プラグインインストール簡単化のために、 git が必要なので、インストールする。

apt update
apt install -y git

denops のインストール

Vim のパッケージ機能と git submodule を組み合わせてプラグインのインストールを行う。

なので今回は git submodulepack/denops/start/denops に配置する。

~/.vim に移動して、git リポジトリを初期化・サブモジュールの追加を行う。

cd ~
mkdir .vim
cd .vim
git init
git branch -m main
git submodule add https://github.com/vim-denops/denops.vim.git pack/denops/start/denops

ddt.vim のインストール

同様に `git submodule でインストールする。

git submodule add https://github.com/Shougo/ddt.vim.git pack/b/start/ddt.vim

ddt.vim の ui プラグインをインストール

git submodule add https://github.com/Shougo/ddt-ui-terminal.git pack/b/start/ddt-ui-terminal
git submodule add https://github.com/Shougo/ddt-ui-shell.git pack/b/start/ddt-ui-shell

設定

vimrc を作成し、プラグインの読み込みなど、最低限の設定を行う。

shougo-s-github/vim/rc/ddt.vim - Shougo/shougo-s-github を参考に、 deno の設定も追加。

~/ddt.vimrc:

packadd denops
packadd ddt.vim
packadd ddt-ui-terminal
packadd ddt-ui-shell

let g:denops#server#deno_args = [
    \   '-q',
    \   '-A',
    \ ]
let g:denops#server#deno_args += ['--unstable-ffi']

call ddt#custom#patch_global(#{
      \   uiParams: #{
      \     shell: #{
      \       nvimServer: '~/.cache/nvim/server.pipe',
      \       prompt: '=\\\>',
      \       promptPattern: '\w*=\\\> \?',
      \     },
      \     terminal: #{
      \       nvimServer: '~/.cache/nvim/server.pipe',
      \       command: ['bash'],
      \       promptPattern: has('win32') ? '\f\+>' : '\w*% \?',
      \     },
      \   },
      \ })

vimrc の作成が終わったら Vim を再起動し、以下コマンドで設定を読み込ませる。

:source ~/ddt.vimrc

ddt-ui-terminal の起動

こちらも shougo-s-github/vim/rc/ddt.vim - Shougo/shougo-s-github を参考に terminal ui を起動。

:call ddt#start(#{ name: t:->get('ddt_ui_terminal_last_name', 'terminal-' .. win_getid()), ui: 'terminal', })

Vim の :terminal が起動し、いつもと同じように操作できる。

ddt-ui-shell の起動

おなじく shougo-s-github/vim/rc/ddt.vim - Shougo/shougo-s-github を参考に shell ui を起動。

:call ddt#start(#{name: t:->get('ddt_ui_shell_last_name', 'shell-' .. win_getid()), ui: 'shell', })

空のバッファが表示されるが、一度エンターキーを押下すると、(前述の設定だと)以下のプロンプトが表示される。

/workspaces/TIL/vim/ddt
=\\\> 

ここからは癖強なので細かく説明していく。

  1. ここで、 uname -a を入力し、次の状態に持っていく
/workspaces/TIL/vim/ddt
=\\\> uname -a
  1. :call ddt#ui#do_action('executeLine') を実行する
  2. エンターキーを押下すると、以下のように uname -a の結果が表示される
/workspaces/TIL/vim/ddt
=\\\> uname -a
Linux 84223c761d6e 5.15.167.4-microsoft-standard-WSL2 #1 SMP Tue Nov 5 00:21:55 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux
/workspaces/TIL/vim/ddt
=\\\>

このように、コマンドの実行すら関数を呼び出さないと行けないので、使い勝手の良いシェルにするには自分でマッピング定義をする必要がある。

ddt-ui-shell の設定

以下のようなマッピングを行うと、「プロンプトの行でエンターを押すと、記載されたコマンドの実行と結果表示」ができるようになる。

""" {{{ for ddt-ui-shell
augroup ddt-shell
    autocmd!
    autocmd FileType ddt-shell nnoremap <buffer> <CR> <Cmd>call ddt#ui#do_action('executeLine')<CR>
    autocmd FileType ddt-shell inoremap <buffer> <CR> <Cmd>call ddt#ui#do_action('executeLine')<CR>
augroup END
""" }}} for ddt-ui-shell

あとは ddt や ddt-ui-shell 等のヘルプを観ながら自分の好きなようにマッピングを追加していけばよい。

以上。

参考資料

2024年12月16日月曜日

Vim 本体に evalfunc (Vim script 関数) を追加する

この記事は Vim 駅伝 の 2024/12/16 の記事です。 前回の記事は yuys13 さんによる、 2024/12/13 の「ソフトウェア技術者とコミュニティ活動~vim-jp radioに出演しました~」という記事でした。

次回は 2024/12/18 に投稿される予定です。

この記事はなに?

少し前に Vim script 関数を追加する Pull Request を出したので、その流れを忘れないように記録するための記事です。

VimConf 2024 の Kato さんのセッションとかぶってしまっていますが、実装の一例として見ていただくのがいいかと思います。 セッションの方がより深掘りされているので、視聴がおすすめです。

今回は、足し算をする関数を追加してみる。

前提

  • Vim のビルド環境が整った状態からスタート

手順概要

  1. 関数の実体を実装
  2. 関数のエントリーポイントを追加
  3. ドキュメントを追加
    1. 関数の説明を追加
    2. タグを追加
    3. 関数リストに追加
  4. 関数のテストを実装

実装

関数の実体を実装

evalfunc.c:

一番下に、実装する。

diff --git a/src/evalfunc.c b/src/evalfunc.c
index b2905da2a..99527029e 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -12125,3 +12125,9 @@ f_xor(typval_T *argvars, typval_T *rettv)
 }
-
 #endif // FEAT_EVAL
+
+    int
+add_num(int x, int y)
+{
+    return x + y;
+}

関数のエントリーポイントを追加

引数パターンの確認

今回追加するのは、ふたつの引数がそれぞれ「数値」、「数値」の引数を持つ関数。

Lists of functions that check the argument types of a builtin function. と コメントされている行から下に、引数のパターンごとに変数がつくられている。

今回追加したいパターンの引数の組み合わせは arg2_number[] となる。

エントリーポイント関数の追加

evalfunc.c:

今回は、さきほど実装した「関数の実体」の直後にエントリーポイントを実装し、プロトタイプ宣言も追加する。

diff --git a/src/evalfunc.c b/src/evalfunc.c
index 99527029e..8fcdaaf60 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -194,6 +194,7 @@ static void f_wildmenumode(typval_T *argvars, typval_T *rettv);
 static void f_windowsversion(typval_T *argvars, typval_T *rettv);
 static void f_wordcount(typval_T *argvars, typval_T *rettv);
 static void f_xor(typval_T *argvars, typval_T *rettv);
+static void f_add_num(typval_T *argvars, typval_T *rettv);
 
 
 /*
@@ -12131,3 +12131,26 @@ add_num(int x, int y)
 {
     return x + y;
 }
+
+/*
+ * "add_num(number, number)" function
+ */
+    static void
+f_add_num(typval_T *argvars, typval_T *rettv)
+{
+    // argvars[] の第一引数と第二引数の型チェック
+    if (in_vim9script()
+           && (check_for_number_arg(argvars, 0) == FAIL
+               || check_for_number_arg(argvars, 1) == FAIL))
+       return;
+
+    // argvars[] から第一引数と第二引数を取得
+    int x = tv_get_number_chk(&argvars[0], NULL);
+    int y = tv_get_number_chk(&argvars[1], NULL);
+
+    // 戻り値に計算結果を格納
+    rettv->vval.v_number = add_num(x, y);
+
+    return;
+}
+

エントリーポイント定義の追加

evalfunc.c:

1758 行目くらいからあるエントリーポイントの定義に、今回実装したエントリーポイントの定義を追加する。

diff --git a/src/evalfunc.c b/src/evalfunc.c
index 99527029e..d51b6ac1f 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -1758,6 +1758,8 @@ static funcentry_T global_functions[] =
                        ret_float,          f_acos},
     {"add",            2, 2, FEARG_1,      arg2_listblobmod_item,
                        ret_first_arg,      f_add},
+    {"add_num",        2, 2, 0,            arg2_number,
+                       ret_number,         f_add_num},
     {"and",            2, 2, FEARG_1,      arg2_number,
                        ret_number,         f_and},
     {"append",         2, 2, FEARG_2,      arg2_setline,

各定義は以下の通り。

  1. 関数名
  2. 最小引数数
  3. 最大引数数
  4. メソッドとして利用できる引数の位置
  5. 引数の組み合わせ
  6. 戻り値の型
  7. 実際に呼び出すエントリーポイント

ドキュメントを追加

関数の説明を追加

runtime/doc/builtin.txt に関数のドキュメントを辞書順になるように追加する。

diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt
index d0f0c7b03..7370091a0 100644
--- a/runtime/doc/builtin.txt
+++ b/runtime/doc/builtin.txt
@@ -26,6 +26,7 @@ USAGE                         RESULT  DESCRIPTION     ~
 abs({expr})                    Float or Number  absolute value of {expr}
 acos({expr})                   Float   arc cosine of {expr}
 add({object}, {item})          List/Blob   append {item} to {object}
+add_num({number}, {number})    Number  add two number
 and({expr}, {expr})            Number  bitwise AND
 append({lnum}, {text})         Number  append {text} below line {lnum}
 appendbufline({buf}, {lnum}, {text})
@@ -833,6 +834,12 @@ add({object}, {expr})                                      *add()*
                |Blob|
 
 
+add_num({number}, {number})                            *add_num()*
+               Add two {number}.
+
+               Return type: |Number|
+
+
 and({expr}, {expr})                                    *and()*
                Bitwise AND on the two arguments.  The arguments are converted
                to a number.  A List, Dict or Float argument causes an error.

タグを追加

runtime/doc/tags に関数のドキュメントのタグを辞書順になるように追加する。

diff --git a/runtime/doc/tags b/runtime/doc/tags
index f33988020..f92a144df 100644
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -5995,6 +5995,7 @@ ada-extra-plugins ft_ada.txt      /*ada-extra-plugins*
 ada-reference  ft_ada.txt      /*ada-reference*
 ada.vim        ft_ada.txt      /*ada.vim*
 add()  builtin.txt     /*add()*
+add_num()      builtin.txt     /*add_num()*
 add-filetype-plugin    usr_05.txt      /*add-filetype-plugin*
 add-global-plugin      usr_05.txt      /*add-global-plugin*
 add-local-help usr_05.txt      /*add-local-help*

関数リストに追加

runtime/doc/usr_41.tx に関数定義を辞書順になるように追加。

diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt
index 36907d249..29e6d2ffc 100644
--- a/runtime/doc/usr_41.txt
+++ b/runtime/doc/usr_41.txt
@@ -808,6 +808,7 @@ List manipulation:                                  *list-functions*
        empty()                 check if List is empty
        insert()                insert an item somewhere in a List
        add()                   append an item to a List
+       add_num()               add two number
        extend()                append a List to a List
        extendnew()             make a new List and append items
        remove()                remove one or more items from a List

関数のテストを実装

今回は、ただの関数なので src/testdir/test_functions.vim にテスト関数を追加する。

diff --git a/src/testdir/test_functions.vim b/src/testdir/test_functions.vim
index 8b2518f2b..9167b2952 100644
--- a/src/testdir/test_functions.vim
+++ b/src/testdir/test_functions.vim
@@ -4206,4 +4206,9 @@ func Test_getcellpixels_gui()
   endif
 endfunc
 
+func Test_add_num()
+  let result = add_num(1, 2)
+  call assert_equal(3, result)
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab

make test_functions で、このファイル内のテストのみを実行できる。

参考資料