MyHLは、haribote HL-8cを改造したバージョンです。
history
コマンドを実装しました。
haribote HL-4aで実装したコマンド履歴は、上下矢印キーで履歴をたどる機能のみをサポートしていました。
上下矢印キーで履歴をたどれるのは便利ですが、古いものにたどりつくには何度もキーを押さないといけません。
MyHLの開発を進める中で、もっと速く履歴からコードを呼び出したいと感じることが増えたため実装しました。
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
サフィックスで!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)
: 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に係る変更
-#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);