JLine3のDefaultParserのメモ。
DefaultParserは、JLine3でユーザーからの入力を解析するデフォルトのクラス。
LineReaderにparserを指定しない場合、DefaultParserが使われる。
import java.io.IOException; import org.jline.reader.LineReader; import org.jline.reader.LineReaderBuilder; import org.jline.reader.impl.DefaultParser; import org.jline.terminal.Terminal; import org.jline.terminal.TerminalBuilder;
public class JLineExample {
public static void main(String[] args) throws IOException {
try (Terminal terminal = TerminalBuilder.terminal()) {
DefaultParser parser = new DefaultParser();
LineReader lineReader = LineReaderBuilder.builder() //
.terminal(terminal) //
.parser(parser) //
.build();
for (;;) {
String line = lineReader.readLine("jline> ");
terminal.writer().println(line);
}
}
}
}
デフォルトでは「\」がエスケープ文字になっている。[2022-09-05]
つまり、「\」が来た後の文字はそのまま入力文字となり、「\」は消される。
また、行継続が有効になっている場合は、エスケープ文字を行末に書くと、次の行が継続する。
エスケープ文字はsetEscapeChars()で指定する。
引数はchar配列なので、複数の文字をエスケープ文字に指定できる。
nullや空配列を渡すと、エスケープしなくなる。
parser.setEscapeChars(new char[]{ '\\', '-' }); // \と-をエスケープ文字とする
parser.setEscapeChars(null); // エスケープしない
行継続を有効にすると、行継続文字を入力した後にENTERキーを押しても、元の行から続く入力が出来る。
デフォルトでは行継続機能は無効になっている。
デフォルトの行継続文字は「\」。
(行継続専用の文字というわけではなく、エスケープ用の文字がそのまま使われる)
parser.setEofOnEscapedNewLine(true); // 行継続を有効にする
// parser.setEscapeChars(new char[] { '\\' }); // エスケープ文字(行継続文字)
| 行継続が有効な場合 | 行継続が無効な場合 |
|---|---|
jline> a\ >b\ >c abc |
jline> a\ a jline> b\ b jline> c c |
※行継続が無効な場合に末尾の「\」が消えるのは、エスケープ文字の処理が実施されるから。[2022-09-05]
エスケープ文字の処理を無効化すると、行継続機能を有効にしても行継続されない。
デフォルトのLineReader.readLine()は、1行入力する毎に文字列が返ってくる。
Parser.parse()の中で、続きの入力がある場合(文が終わっていない場合)にEOFError(JLine3の例外)をスローすると、LineReader.readLine()で複数行の文字列が返るようになる。
参考: takemikamiさんのJavaでのCUI対話型ツールの実装、JLineによる複数行コマンド受付の対応
import org.jline.reader.EOFError; import org.jline.reader.ParsedLine;
class MyParser extends DefaultParser {
@Override
public ParsedLine parse(String line, int cursor, ParseContext context) {
if (context == ParseContext.ACCEPT_LINE) {
if (!line.trim().endsWith(";")) {
throw new EOFError(-1, -1, "not end of statement");
}
}
return super.parse(line, cursor, context);
}
}
入力1行につき1回parseが呼ばれるが、引数lineには、それまでに入力された複数行の文字列(結合された状態)が渡ってくる。
文が終了したかどうかのチェックはcontext=ACCEPT_LINEのときだけ行う。
タブキーを押すと補完機能が働き、context=COMPLETEで呼ばれるが、そのときにEOFErrorを返してはいけない。
LineReader lineReader = LineReaderBuilder.builder() //
.terminal(terminal) //
.parser(new MyParser()) //
.variable(LineReader.SECONDARY_PROMPT_PATTERN, " | ") //
.build();
SECONDARY_PROMPT_PATTERNを指定すると、2行目以降のプロンプトがそれに変わる。
デフォルトのパターンは「%M> 」。
「%M」には、EOFErrorの第4引数missingの内容が表示される。
(どのようなパターンが使えるかは、LineReaderのclassのJavadocに書かれている)
ただし、DumbTerminalではSECONDARY_PROMPT_PATTERNは機能しない。
(SECONDARY_PROMPT_PATTERNに「%P」を指定すると、DumbTerminalではバグる(最初のプロンプトすら表示されずに無限ループに陥る)ので注意。(jline
3.21.0))
| 正常に動作する場合 | DumbTerminalの場合 |
|---|---|
jline> aaa;
aaa;
jline> aaa
| bbb
| ccc;
aaa
bbb
ccc;
|
jline> aaa; aaa; jline> aaa bbb ccc; aaa bbb ccc; |