※一部修正
ヒストリのリストの範囲外を参照してしまっている所がありましたので修正しました
- for i = 0, hist_len do
+ for i = 1, hist_len do
nyagosには実装されていません
nyaos3000ではvzlike-previous-history
とvzlike-next-history
のヒストリ補完がありましたが、
nyagosは「本体はコンパクトに、機能拡張はLuaで!」という設計思想のもと、これらのファンクションは実装されていません。
しかし、nyagosにはLuaで書いた機能をキーバインドに設定ためのインターフェイスnyagos.bindkey("キー名",function(this) ... end)
があります。この機能を使うことでLuaスクリプトで実装しましょう。
nyaos3000のそれとの違い
nyaos3000のvzlike-previous-history
, vzlike-next-history
は単語単位の補間という感じでしたが、
今回の実装はtcshの補完に近く、行単位の補完となっています。
また、補完元の検索ワードを判別するため、カーソル位置を元あった場所に戻しているので慣れるまで違和感があるかもしれません。
なお、補完元の検索ワードに日本語などのマルチバイト文字が入っているとズレていきますが、
そこは仕様ということでお願いします(補完先の文字列にマルチバイト文字が混じっている場合は大丈夫です)。
実装例
nyagos.bindkey("C_N",
function(this)
search_history(this, false)
end
)
nyagos.bindkey("C_P",
function(this)
search_history(this, true)
end
)
function search_history(this, is_prev)
-- カーソル位置が一番左の場合は通常のnext/prev
if this.pos == 1 then
if is_prev == true then
this:call("PREVIOUS_HISTORY")
else
this:call("NEXT_HISTORY")
end
this:call("BEGINNING_OF_LINE")
return nil
end
-- 検索キーワード
local search_string = this.text:sub(1, this.pos - 1)
-- 重複を除いたhistoryリストの取得
local history_uniq = {}
local is_duplicated = false
local hist_len = nyagos.gethistory()
for i = 1, hist_len do
local history
-- 新しい履歴がリスト後ろに残るよう末尾からサーチ
history = nyagos.gethistory(hist_len - i)
for i, e in ipairs(history_uniq) do
if history == e or history == search_string then
is_duplicated = true
end
end
if is_duplicated == false then
if is_prev == true then
table.insert(history_uniq, history)
else
table.insert(history_uniq, 1, history)
end
end
is_duplicated = false
end
-- 入力と完全一致する履歴を探す
-- 完全一致する履歴を起点にすることで
-- (見かけ上)インクリメンタルな検索にする
local hist_pos = 0
for i, e in ipairs(history_uniq) do
if e == this.text then
hist_pos = i
break
end
end
-- 前方一致する履歴を探す
local matched_string = nil
for i = hist_pos + 1, #history_uniq do
if history_uniq[i]:match('^' .. search_string .. '.*') then
matched_string = history_uniq[i]
break
end
end
-- 見つかった履歴を出力
-- 見つからなければ、検索キーワードを出力
this:call("KILL_WHOLE_LINE")
if (matched_string ~= nil) then
this:insert(matched_string)
else
this:insert(search_string)
end
this:call("BEGINNING_OF_LINE")
for i = 1, this.pos - 1 do
this:call("FORWARD_CHAR")
end
end