MyHLは、haribote HL-8cを改造したバージョンです。
historyコマンドを追加
history
コマンドを実装しました。
haribote HL-4aで実装したコマンド履歴は、上下矢印キーで履歴をたどる機能のみをサポートしていました。
上下矢印キーで履歴をたどれるのは便利ですが、古いものにたどりつくには何度もキーを押さないといけません。
MyHLの開発を進める中で、もっと速く履歴からコードを呼び出したいと感じることが増えたため実装しました。
historyコマンド
history
コマンド: 以前に実行したコマンドやコードを古い順に、引数に指定した行数だけ表示する
結果の左側に表示されている数値は、古いものから順に割り当てたid番号です。
確認したid番号や相対位置を使って履歴からコマンドやコードを検索し実行できます。
$ haribote [1]> history 10 9 print 1+2*3 10 print (1+2)*3 11 a = 0; print ++a; print a 12 a = 0; print a++; print a 13 int a[30] = { 1, 1 } 14 for (i = 2; i < 30; i++) { a[i] = a[i - 2] + a[i - 1]; } 15 for (i = 0; i < 10; i++) { print a[i]; } 16 for (i = 0; i < 20; i++) { print a[i]; } 17 exit 18 history 10
!コマンドを使って履歴からコードを実行する
!
(Bang) コマンド: id番号または相対位置を表す負数を指定して、その位置にあるコマンドやコードを実行する
!
に続けて、数値または感嘆符を入力することで、実行するコマンドやコードを指定します。
!n
!n
は、id番号(n番)の位置にあるコマンドやコードを実行します。
[2]> !13 int a[30] = { 1, 1 } [3]> !14 for (i = 2; i < 30; i++) { a[i] = a[i - 2] + a[i - 1]; } [4]> !15 for (i = 0; i < 10; i++) { print a[i]; } 1 1 2 3 5 8 13 21 34 55
!-n
!-n
は、直近のものから数えてnつ前に実行したコマンドやコードを実行します。
[5]> prints "Riff Raff" Riff Raff [6]> prints "Jailbreak" Jailbreak [7]> prints "Beating Around The Bush" Beating Around The Bush [8]> history 5 21 for (i = 0; i < 10; i++) { print a[i]; } 22 prints "Riff Raff" 23 prints "Jailbreak" 24 prints "Beating Around The Bush" 25 history 5 [9]> !-3 prints "Jailbreak" Jailbreak
!!
!!
は、直前に実行したコマンドやコードを再実行するための記法です。
結果は!-1
と同じになります。
[10]> !! prints "Jailbreak" Jailbreak
:eサフィックス
:e
サフィックスで!n
、!-n
、!!
を修飾すると、履歴から検索したコマンドやコードが、入力が確定していない状態でコマンドラインにセットされます。
[11]> !9 print 1+2*3 7 [12]> !!:e [13]> print 1+2*3
コマンドラインを編集して入力を確定すると、コマンドやコードが実行されます。
[13]> print (1+2)*3 9
!n
、!-n
、!!
、:e
は直接履歴に追加されず、実行したコマンドやコードが追加されます。
特に以前のコマンドやコードを編集して実行したいときに、実装済みのコマンドライン編集とあわせて使うと便利な機能です。
実装メモ
自分が使いやすいようにbashの履歴展開に似せて作っていますが、hariboteの「!」は単なるREPLのコマンドです。
else if (text[0] == '!' && (text[1] == '!' || text[1] == '-' || isNumber(text[1]))) { int reedit = endsWithSuffix('e', text), i; char *end; if (reedit) text[inputLen - 2] = 0; if (text[1] == '!') i = -1; else if (i = strtol(&text[1], &end, 10), *end != 0) { printf("!: %s: numeric argument required\n", &text[1]); continue; } Command *cmd = text[1] == '!' || text[1] == '-' ? getPrevCmd(i - 1, text) // -1は"!"コマンド : getPrevCmdById(i, text); if (reedit) { ignorePrevHistory(); eraseLine(); setCursorX(cmd->len); printf("[%d]> %s", ++nLines, cmd->str); next = 0; } else { updatePrevHistory(cmd->str, cmd->len); resetCmdHistoryPos(); setCursorX(0); printf("%s\n", cmd->str); reexecute = 1; } } else if (strncmp(text, "history ", 8) == 0) { int num = strtol(&text[8], NULL, 10); listPrevCmds(num, text); }
history
コマンドや!
コマンドで履歴を検索する処理は、上下矢印キーでコマンド履歴をたどる処理で使っているshowHistory()のラッパーを使って書いています。
int rewindHistory(int num) { assert(num < 0); num = -num; int id = cmdHist.count - num + 1; if (id <= 0) return 0; int end = num; for (int i = 0; i < end; ++i) showHistory(Prev, NULL); return id; }
showHistory()
は、第二引数にNULL
を渡すと、早期リターンしてコマンドやコードを表示しないように変更しました。履歴をたどる処理は
haribote HL-4aと同じです。
history
コマンドの結果の左側に表示されている数値は、cmdHist.head
に登録されているコマンドやコードの位置を1として、順に割り当てたid番号です。
id番号は内部的に保持せず、history
コマンドが実行される都度、int id = cmdHist.count - num + 1;
で計算しています。
※ 変数num
は、直近のものから数えて履歴をいくつたどるかを表す数値です。
haribote HL-4aに係る変更
HISTORY_SIZEを変更
-#define HISTORY_SIZE 100 +#define HISTORY_SIZE 500
不要なcontinue文を削除
@@ -1664,14 +1664,12 @@ int main(int argc, const char **argv) printf("[%d]> ", nLines); showHistory(Prev, text); next = 0; - continue; } else if (strcmp(text, "nexthist") == 0) { eraseLine(); printf("[%d]> ", nLines); showHistory(Next, text); next = 0; - continue; } #endif else if (strncmp(text, "run ", 4) == 0) { @@ -1684,7 +1682,6 @@ int main(int argc, const char **argv) eraseAll(); printf("[%d]> ", nLines); next = 0; - continue; } #endif else {
prevhistとnexthistをリネーム
@@ -1537,8 +1676,8 @@ char *readLine(char *str, int size, FILE *stream) switch (ch) { case 67: if (cursorX < i) { write(0, "\e[C", 3); ++cursorX; } continue; // RightArrow case 68: if (cursorX > 0) { write(0, "\e[D", 3); --cursorX; } continue; // LeftArrow - case 65: strncpy(str, "prevhist", 9); break; // UpArrow - case 66: strncpy(str, "nexthist", 9); break; // DownArrow + case 65: strncpy(str, "__PREV_HIST", 12); break; // UpArrow + case 66: strncpy(str, "__NEXT_HIST", 12); break; // DownArrow } setCanonicalMode(); return str;
@@ -1659,13 +1659,13 @@ int main(int argc, const char **argv) if (strcmp(text, "exit") == 0) goto exit; #if defined(__APPLE__) || defined(__linux__) - else if (strcmp(text, "prevhist") == 0) { + else if (strcmp(text, "__PREV_HIST") == 0) { eraseLine(); printf("[%d]> ", nLines); showHistory(Prev, text); next = 0; } - else if (strcmp(text, "nexthist") == 0) { + else if (strcmp(text, "__NEXT_HIST") == 0) { eraseLine(); printf("[%d]> ", nLines); showHistory(Next, text);