From 8da980b39d140a1012c9deabf6b710f3faccdf6c Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Tue, 24 Oct 2023 16:51:25 +0200 Subject: [PATCH] Support automatic parsing of an inputrc file in jline reader (#821) --- .../main/java/org/jline/builtins/InputRC.java | 365 +--------------- .../java/org/jline/reader/LineReader.java | 5 + .../java/org/jline/reader/impl/InputRC.java | 388 ++++++++++++++++++ .../org/jline/reader/impl/LineReaderImpl.java | 18 +- 4 files changed, 416 insertions(+), 360 deletions(-) create mode 100644 reader/src/main/java/org/jline/reader/impl/InputRC.java diff --git a/builtins/src/main/java/org/jline/builtins/InputRC.java b/builtins/src/main/java/org/jline/builtins/InputRC.java index cffbba2ef..ebb63b697 100644 --- a/builtins/src/main/java/org/jline/builtins/InputRC.java +++ b/builtins/src/main/java/org/jline/builtins/InputRC.java @@ -8,377 +8,24 @@ */ package org.jline.builtins; -import java.io.*; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; import java.net.URL; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; import org.jline.reader.LineReader; -import org.jline.reader.Macro; -import org.jline.reader.Reference; -import org.jline.utils.Log; public final class InputRC { public static void configure(LineReader reader, URL url) throws IOException { - try (InputStream is = url.openStream()) { - configure(reader, is); - } + org.jline.reader.impl.InputRC.configure(reader, url); } public static void configure(LineReader reader, InputStream is) throws IOException { - try (InputStreamReader r = new InputStreamReader(is)) { - configure(reader, r); - } + org.jline.reader.impl.InputRC.configure(reader, is); } public static void configure(LineReader reader, Reader r) throws IOException { - BufferedReader br; - if (r instanceof BufferedReader) { - br = (BufferedReader) r; - } else { - br = new BufferedReader(r); - } - reader.getVariables().putIfAbsent(LineReader.EDITING_MODE, "emacs"); - reader.setKeyMap(LineReader.MAIN); - if ("vi".equals(reader.getVariable(LineReader.EDITING_MODE))) { - reader.getKeyMaps().put(LineReader.MAIN, reader.getKeyMaps().get(LineReader.VIINS)); - } else if ("emacs".equals(reader.getVariable(LineReader.EDITING_MODE))) { - reader.getKeyMaps().put(LineReader.MAIN, reader.getKeyMaps().get(LineReader.EMACS)); - } - new InputRC(reader).parse(br); - if ("vi".equals(reader.getVariable(LineReader.EDITING_MODE))) { - reader.getKeyMaps().put(LineReader.MAIN, reader.getKeyMaps().get(LineReader.VIINS)); - } else if ("emacs".equals(reader.getVariable(LineReader.EDITING_MODE))) { - reader.getKeyMaps().put(LineReader.MAIN, reader.getKeyMaps().get(LineReader.EMACS)); - } - } - - private final LineReader reader; - - private InputRC(LineReader reader) { - this.reader = reader; - } - - private void parse(BufferedReader br) throws IOException, IllegalArgumentException { - String line; - boolean parsing = true; - List ifsStack = new ArrayList<>(); - while ((line = br.readLine()) != null) { - try { - line = line.trim(); - if (line.length() == 0) { - continue; - } - if (line.charAt(0) == '#') { - continue; - } - int i = 0; - if (line.charAt(i) == '$') { - String cmd; - String args; - ++i; - while (i < line.length() && (line.charAt(i) == ' ' || line.charAt(i) == '\t')) { - i++; - } - int s = i; - while (i < line.length() && (line.charAt(i) != ' ' && line.charAt(i) != '\t')) { - i++; - } - cmd = line.substring(s, i); - while (i < line.length() && (line.charAt(i) == ' ' || line.charAt(i) == '\t')) { - i++; - } - s = i; - while (i < line.length() && (line.charAt(i) != ' ' && line.charAt(i) != '\t')) { - i++; - } - args = line.substring(s, i); - if ("if".equalsIgnoreCase(cmd)) { - ifsStack.add(parsing); - if (!parsing) { - continue; - } - if (args.startsWith("term=")) { - // TODO - } else if (args.startsWith("mode=")) { - String mode = (String) reader.getVariable(LineReader.EDITING_MODE); - parsing = args.substring("mode=".length()).equalsIgnoreCase(mode); - } else { - parsing = args.equalsIgnoreCase(reader.getAppName()); - } - } else if ("else".equalsIgnoreCase(cmd)) { - if (ifsStack.isEmpty()) { - throw new IllegalArgumentException("$else found without matching $if"); - } - boolean invert = true; - for (boolean b : ifsStack) { - if (!b) { - invert = false; - break; - } - } - if (invert) { - parsing = !parsing; - } - } else if ("endif".equalsIgnoreCase(cmd)) { - if (ifsStack.isEmpty()) { - throw new IllegalArgumentException("endif found without matching $if"); - } - parsing = ifsStack.remove(ifsStack.size() - 1); - } else if ("include".equalsIgnoreCase(cmd)) { - // TODO - } - continue; - } - if (!parsing) { - continue; - } - if (line.charAt(i++) == '"') { - boolean esc = false; - for (; ; i++) { - if (i >= line.length()) { - throw new IllegalArgumentException("Missing closing quote on line '" + line + "'"); - } - if (esc) { - esc = false; - } else if (line.charAt(i) == '\\') { - esc = true; - } else if (line.charAt(i) == '"') { - break; - } - } - } - while (i < line.length() && line.charAt(i) != ':' && line.charAt(i) != ' ' && line.charAt(i) != '\t') { - i++; - } - String keySeq = line.substring(0, i); - boolean equivalency = i + 1 < line.length() && line.charAt(i) == ':' && line.charAt(i + 1) == '='; - i++; - if (equivalency) { - i++; - } - if (keySeq.equalsIgnoreCase("set")) { - String key; - String val; - while (i < line.length() && (line.charAt(i) == ' ' || line.charAt(i) == '\t')) { - i++; - } - int s = i; - while (i < line.length() && (line.charAt(i) != ' ' && line.charAt(i) != '\t')) { - i++; - } - key = line.substring(s, i); - while (i < line.length() && (line.charAt(i) == ' ' || line.charAt(i) == '\t')) { - i++; - } - s = i; - while (i < line.length() && (line.charAt(i) != ' ' && line.charAt(i) != '\t')) { - i++; - } - val = line.substring(s, i); - setVar(reader, key, val); - } else { - while (i < line.length() && (line.charAt(i) == ' ' || line.charAt(i) == '\t')) { - i++; - } - int start = i; - if (i < line.length() && (line.charAt(i) == '\'' || line.charAt(i) == '\"')) { - char delim = line.charAt(i++); - boolean esc = false; - for (; ; i++) { - if (i >= line.length()) { - break; - } - if (esc) { - esc = false; - } else if (line.charAt(i) == '\\') { - esc = true; - } else if (line.charAt(i) == delim) { - break; - } - } - } - for (; i < line.length() && line.charAt(i) != ' ' && line.charAt(i) != '\t'; i++) - ; - String val = line.substring(Math.min(start, line.length()), Math.min(i, line.length())); - if (keySeq.charAt(0) == '"') { - keySeq = translateQuoted(keySeq); - } else { - // Bind key name - String keyName = - keySeq.lastIndexOf('-') > 0 ? keySeq.substring(keySeq.lastIndexOf('-') + 1) : keySeq; - char key = getKeyFromName(keyName); - keyName = keySeq.toLowerCase(); - keySeq = ""; - if (keyName.contains("meta-") || keyName.contains("m-")) { - keySeq += "\u001b"; - } - if (keyName.contains("control-") || keyName.contains("c-") || keyName.contains("ctrl-")) { - key = (char) (Character.toUpperCase(key) & 0x1f); - } - keySeq += key; - } - if (val.length() > 0 && (val.charAt(0) == '\'' || val.charAt(0) == '\"')) { - reader.getKeys().bind(new Macro(translateQuoted(val)), keySeq); - } else { - reader.getKeys().bind(new Reference(val), keySeq); - } - } - } catch (IllegalArgumentException e) { - Log.warn("Unable to parse user configuration: ", e); - } - } - } - - private static String translateQuoted(String keySeq) { - int i; - String str = keySeq.substring(1, keySeq.length() - 1); - StringBuilder sb = new StringBuilder(); - for (i = 0; i < str.length(); i++) { - char c = str.charAt(i); - if (c == '\\') { - boolean ctrl = str.regionMatches(i, "\\C-", 0, 3) || str.regionMatches(i, "\\M-\\C-", 0, 6); - boolean meta = str.regionMatches(i, "\\M-", 0, 3) || str.regionMatches(i, "\\C-\\M-", 0, 6); - i += (meta ? 3 : 0) + (ctrl ? 3 : 0) + (!meta && !ctrl ? 1 : 0); - if (i >= str.length()) { - break; - } - c = str.charAt(i); - if (meta) { - sb.append("\u001b"); - } - if (ctrl) { - c = c == '?' ? 0x7f : (char) (Character.toUpperCase(c) & 0x1f); - } - if (!meta && !ctrl) { - switch (c) { - case 'a': - c = 0x07; - break; - case 'b': - c = '\b'; - break; - case 'd': - c = 0x7f; - break; - case 'e': - c = 0x1b; - break; - case 'f': - c = '\f'; - break; - case 'n': - c = '\n'; - break; - case 'r': - c = '\r'; - break; - case 't': - c = '\t'; - break; - case 'v': - c = 0x0b; - break; - case '\\': - c = '\\'; - break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - c = 0; - for (int j = 0; j < 3; j++, i++) { - if (i >= str.length()) { - break; - } - int k = Character.digit(str.charAt(i), 8); - if (k < 0) { - break; - } - c = (char) (c * 8 + k); - } - c &= 0xFF; - break; - case 'x': - i++; - c = 0; - for (int j = 0; j < 2; j++, i++) { - if (i >= str.length()) { - break; - } - int k = Character.digit(str.charAt(i), 16); - if (k < 0) { - break; - } - c = (char) (c * 16 + k); - } - c &= 0xFF; - break; - case 'u': - i++; - c = 0; - for (int j = 0; j < 4; j++, i++) { - if (i >= str.length()) { - break; - } - int k = Character.digit(str.charAt(i), 16); - if (k < 0) { - break; - } - c = (char) (c * 16 + k); - } - break; - } - } - sb.append(c); - } else { - sb.append(c); - } - } - return sb.toString(); - } - - private static char getKeyFromName(String name) { - if ("DEL".equalsIgnoreCase(name) || "Rubout".equalsIgnoreCase(name)) { - return 0x7f; - } else if ("ESC".equalsIgnoreCase(name) || "Escape".equalsIgnoreCase(name)) { - return '\033'; - } else if ("LFD".equalsIgnoreCase(name) || "NewLine".equalsIgnoreCase(name)) { - return '\n'; - } else if ("RET".equalsIgnoreCase(name) || "Return".equalsIgnoreCase(name)) { - return '\r'; - } else if ("SPC".equalsIgnoreCase(name) || "Space".equalsIgnoreCase(name)) { - return ' '; - } else if ("Tab".equalsIgnoreCase(name)) { - return '\t'; - } else { - return name.charAt(0); - } - } - - private static void setVar(LineReader reader, String key, String val) { - if (LineReader.KEYMAP.equalsIgnoreCase(key)) { - reader.setKeyMap(val); - return; - } - - for (LineReader.Option option : LineReader.Option.values()) { - if (option.name().toLowerCase(Locale.ENGLISH).replace('_', '-').equals(val)) { - if ("on".equalsIgnoreCase(val)) { - reader.setOpt(option); - } else if ("off".equalsIgnoreCase(val)) { - reader.unsetOpt(option); - } - return; - } - } - - reader.setVariable(key, val); + org.jline.reader.impl.InputRC.configure(reader, r); } } diff --git a/reader/src/main/java/org/jline/reader/LineReader.java b/reader/src/main/java/org/jline/reader/LineReader.java index 806937df8..fa7451e35 100644 --- a/reader/src/main/java/org/jline/reader/LineReader.java +++ b/reader/src/main/java/org/jline/reader/LineReader.java @@ -406,6 +406,11 @@ public interface LineReader { */ String TAB_WIDTH = "tab-width"; + /** + * Name of inputrc to read at line reader creation time. + */ + String INPUT_RC_FILE_NAME = "input-rc-file-name"; + Map> defaultKeyMaps(); enum Option { diff --git a/reader/src/main/java/org/jline/reader/impl/InputRC.java b/reader/src/main/java/org/jline/reader/impl/InputRC.java new file mode 100644 index 000000000..aaede8945 --- /dev/null +++ b/reader/src/main/java/org/jline/reader/impl/InputRC.java @@ -0,0 +1,388 @@ +/* + * Copyright (c) 2002-2023, the original author(s). + * + * This software is distributable under the BSD license. See the terms of the + * BSD license in the documentation provided with this software. + * + * https://opensource.org/licenses/BSD-3-Clause + */ +package org.jline.reader.impl; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import org.jline.reader.LineReader; +import org.jline.reader.Macro; +import org.jline.reader.Reference; +import org.jline.utils.Log; + +public final class InputRC { + + public static void configure(LineReader reader, URL url) throws IOException { + try (InputStream is = url.openStream()) { + configure(reader, is); + } + } + + public static void configure(LineReader reader, InputStream is) throws IOException { + try (InputStreamReader r = new InputStreamReader(is)) { + configure(reader, r); + } + } + + public static void configure(LineReader reader, Reader r) throws IOException { + BufferedReader br; + if (r instanceof BufferedReader) { + br = (BufferedReader) r; + } else { + br = new BufferedReader(r); + } + reader.getVariables().putIfAbsent(LineReader.EDITING_MODE, "emacs"); + reader.setKeyMap(LineReader.MAIN); + if ("vi".equals(reader.getVariable(LineReader.EDITING_MODE))) { + reader.getKeyMaps().put(LineReader.MAIN, reader.getKeyMaps().get(LineReader.VIINS)); + } else if ("emacs".equals(reader.getVariable(LineReader.EDITING_MODE))) { + reader.getKeyMaps().put(LineReader.MAIN, reader.getKeyMaps().get(LineReader.EMACS)); + } + new InputRC(reader).parse(br); + if ("vi".equals(reader.getVariable(LineReader.EDITING_MODE))) { + reader.getKeyMaps().put(LineReader.MAIN, reader.getKeyMaps().get(LineReader.VIINS)); + } else if ("emacs".equals(reader.getVariable(LineReader.EDITING_MODE))) { + reader.getKeyMaps().put(LineReader.MAIN, reader.getKeyMaps().get(LineReader.EMACS)); + } + } + + private final LineReader reader; + + private InputRC(LineReader reader) { + this.reader = reader; + } + + private void parse(BufferedReader br) throws IOException, IllegalArgumentException { + String line; + boolean parsing = true; + List ifsStack = new ArrayList<>(); + while ((line = br.readLine()) != null) { + try { + line = line.trim(); + if (line.length() == 0) { + continue; + } + if (line.charAt(0) == '#') { + continue; + } + int i = 0; + if (line.charAt(i) == '$') { + String cmd; + String args; + ++i; + while (i < line.length() && (line.charAt(i) == ' ' || line.charAt(i) == '\t')) { + i++; + } + int s = i; + while (i < line.length() && (line.charAt(i) != ' ' && line.charAt(i) != '\t')) { + i++; + } + cmd = line.substring(s, i); + while (i < line.length() && (line.charAt(i) == ' ' || line.charAt(i) == '\t')) { + i++; + } + s = i; + while (i < line.length() && (line.charAt(i) != ' ' && line.charAt(i) != '\t')) { + i++; + } + args = line.substring(s, i); + if ("if".equalsIgnoreCase(cmd)) { + ifsStack.add(parsing); + if (!parsing) { + continue; + } + if (args.startsWith("term=")) { + // TODO + } else if (args.startsWith("mode=")) { + String mode = (String) reader.getVariable(LineReader.EDITING_MODE); + parsing = args.substring("mode=".length()).equalsIgnoreCase(mode); + } else { + parsing = args.equalsIgnoreCase(reader.getAppName()); + } + } else if ("else".equalsIgnoreCase(cmd)) { + if (ifsStack.isEmpty()) { + throw new IllegalArgumentException("$else found without matching $if"); + } + boolean invert = true; + for (boolean b : ifsStack) { + if (!b) { + invert = false; + break; + } + } + if (invert) { + parsing = !parsing; + } + } else if ("endif".equalsIgnoreCase(cmd)) { + if (ifsStack.isEmpty()) { + throw new IllegalArgumentException("endif found without matching $if"); + } + parsing = ifsStack.remove(ifsStack.size() - 1); + } else if ("include".equalsIgnoreCase(cmd)) { + // TODO + } + continue; + } + if (!parsing) { + continue; + } + if (line.charAt(i++) == '"') { + boolean esc = false; + for (; ; i++) { + if (i >= line.length()) { + throw new IllegalArgumentException("Missing closing quote on line '" + line + "'"); + } + if (esc) { + esc = false; + } else if (line.charAt(i) == '\\') { + esc = true; + } else if (line.charAt(i) == '"') { + break; + } + } + } + while (i < line.length() && line.charAt(i) != ':' && line.charAt(i) != ' ' && line.charAt(i) != '\t') { + i++; + } + String keySeq = line.substring(0, i); + boolean equivalency = i + 1 < line.length() && line.charAt(i) == ':' && line.charAt(i + 1) == '='; + i++; + if (equivalency) { + i++; + } + if (keySeq.equalsIgnoreCase("set")) { + String key; + String val; + while (i < line.length() && (line.charAt(i) == ' ' || line.charAt(i) == '\t')) { + i++; + } + int s = i; + while (i < line.length() && (line.charAt(i) != ' ' && line.charAt(i) != '\t')) { + i++; + } + key = line.substring(s, i); + while (i < line.length() && (line.charAt(i) == ' ' || line.charAt(i) == '\t')) { + i++; + } + s = i; + while (i < line.length() && (line.charAt(i) != ' ' && line.charAt(i) != '\t')) { + i++; + } + val = line.substring(s, i); + setVar(reader, key, val); + } else { + while (i < line.length() && (line.charAt(i) == ' ' || line.charAt(i) == '\t')) { + i++; + } + int start = i; + if (i < line.length() && (line.charAt(i) == '\'' || line.charAt(i) == '\"')) { + char delim = line.charAt(i++); + boolean esc = false; + for (; ; i++) { + if (i >= line.length()) { + break; + } + if (esc) { + esc = false; + } else if (line.charAt(i) == '\\') { + esc = true; + } else if (line.charAt(i) == delim) { + break; + } + } + } + for (; i < line.length() && line.charAt(i) != ' ' && line.charAt(i) != '\t'; i++) + ; + String val = line.substring(Math.min(start, line.length()), Math.min(i, line.length())); + if (keySeq.charAt(0) == '"') { + keySeq = translateQuoted(keySeq); + } else { + // Bind key name + String keyName = + keySeq.lastIndexOf('-') > 0 ? keySeq.substring(keySeq.lastIndexOf('-') + 1) : keySeq; + char key = getKeyFromName(keyName); + keyName = keySeq.toLowerCase(); + keySeq = ""; + if (keyName.contains("meta-") || keyName.contains("m-")) { + keySeq += "\u001b"; + } + if (keyName.contains("control-") || keyName.contains("c-") || keyName.contains("ctrl-")) { + key = (char) (Character.toUpperCase(key) & 0x1f); + } + keySeq += key; + } + if (val.length() > 0 && (val.charAt(0) == '\'' || val.charAt(0) == '\"')) { + reader.getKeys().bind(new Macro(translateQuoted(val)), keySeq); + } else { + reader.getKeys().bind(new Reference(val), keySeq); + } + } + } catch (IllegalArgumentException e) { + Log.warn("Unable to parse user configuration: ", e); + } + } + } + + private static String translateQuoted(String keySeq) { + int i; + String str = keySeq.substring(1, keySeq.length() - 1); + StringBuilder sb = new StringBuilder(); + for (i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (c == '\\') { + boolean ctrl = str.regionMatches(i, "\\C-", 0, 3) || str.regionMatches(i, "\\M-\\C-", 0, 6); + boolean meta = str.regionMatches(i, "\\M-", 0, 3) || str.regionMatches(i, "\\C-\\M-", 0, 6); + i += (meta ? 3 : 0) + (ctrl ? 3 : 0) + (!meta && !ctrl ? 1 : 0); + if (i >= str.length()) { + break; + } + c = str.charAt(i); + if (meta) { + sb.append("\u001b"); + } + if (ctrl) { + c = c == '?' ? 0x7f : (char) (Character.toUpperCase(c) & 0x1f); + } + if (!meta && !ctrl) { + switch (c) { + case 'a': + c = 0x07; + break; + case 'b': + c = '\b'; + break; + case 'd': + c = 0x7f; + break; + case 'e': + c = 0x1b; + break; + case 'f': + c = '\f'; + break; + case 'n': + c = '\n'; + break; + case 'r': + c = '\r'; + break; + case 't': + c = '\t'; + break; + case 'v': + c = 0x0b; + break; + case '\\': + c = '\\'; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + c = 0; + for (int j = 0; j < 3; j++, i++) { + if (i >= str.length()) { + break; + } + int k = Character.digit(str.charAt(i), 8); + if (k < 0) { + break; + } + c = (char) (c * 8 + k); + } + c &= 0xFF; + break; + case 'x': + i++; + c = 0; + for (int j = 0; j < 2; j++, i++) { + if (i >= str.length()) { + break; + } + int k = Character.digit(str.charAt(i), 16); + if (k < 0) { + break; + } + c = (char) (c * 16 + k); + } + c &= 0xFF; + break; + case 'u': + i++; + c = 0; + for (int j = 0; j < 4; j++, i++) { + if (i >= str.length()) { + break; + } + int k = Character.digit(str.charAt(i), 16); + if (k < 0) { + break; + } + c = (char) (c * 16 + k); + } + break; + } + } + sb.append(c); + } else { + sb.append(c); + } + } + return sb.toString(); + } + + private static char getKeyFromName(String name) { + if ("DEL".equalsIgnoreCase(name) || "Rubout".equalsIgnoreCase(name)) { + return 0x7f; + } else if ("ESC".equalsIgnoreCase(name) || "Escape".equalsIgnoreCase(name)) { + return '\033'; + } else if ("LFD".equalsIgnoreCase(name) || "NewLine".equalsIgnoreCase(name)) { + return '\n'; + } else if ("RET".equalsIgnoreCase(name) || "Return".equalsIgnoreCase(name)) { + return '\r'; + } else if ("SPC".equalsIgnoreCase(name) || "Space".equalsIgnoreCase(name)) { + return ' '; + } else if ("Tab".equalsIgnoreCase(name)) { + return '\t'; + } else { + return name.charAt(0); + } + } + + static void setVar(LineReader reader, String key, String val) { + if (LineReader.KEYMAP.equalsIgnoreCase(key)) { + reader.setKeyMap(val); + return; + } + + for (LineReader.Option option : LineReader.Option.values()) { + if (option.name().toLowerCase(Locale.ENGLISH).replace('_', '-').equals(val)) { + if ("on".equalsIgnoreCase(val)) { + reader.setOpt(option); + } else if ("off".equalsIgnoreCase(val)) { + reader.unsetOpt(option); + } + return; + } + } + + reader.setVariable(key, val); + } +} diff --git a/reader/src/main/java/org/jline/reader/impl/LineReaderImpl.java b/reader/src/main/java/org/jline/reader/impl/LineReaderImpl.java index 2a46855cf..eb7da9d26 100644 --- a/reader/src/main/java/org/jline/reader/impl/LineReaderImpl.java +++ b/reader/src/main/java/org/jline/reader/impl/LineReaderImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2022, the original author(s). + * Copyright (c) 2002-2023, the original author(s). * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -18,6 +18,9 @@ import java.io.InputStream; import java.io.InterruptedIOException; import java.lang.reflect.Constructor; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.time.Instant; import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; @@ -321,6 +324,19 @@ public LineReaderImpl(Terminal terminal, String appName, Map var builtinWidgets = builtinWidgets(); widgets = new HashMap<>(builtinWidgets); bindingReader = new BindingReader(terminal.reader()); + + String inputRc = getString(INPUT_RC_FILE_NAME, null); + if (inputRc != null) { + Path inputRcPath = Paths.get(inputRc); + if (Files.exists(inputRcPath)) { + try (InputStream is = Files.newInputStream(inputRcPath)) { + InputRC.configure(this, is); + } catch (Exception e) { + Log.debug("Error reading inputRc config file: ", inputRc, e); + } + } + } + doDisplay(); }