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; |