S-JIS[2022-09-03/2022-09-05] 変更履歴

JLine3 DefaultParser

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);                    // エスケープしない

LineReaderでエスケープ処理を抑止する例


行継続の例

行継続を有効にすると、行継続文字を入力した後に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;

JLine3へ戻る / JLineへ戻る / Javaへ戻る / 技術メモへ戻る
メールの送信先:ひしだま