Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SQLLINE-186] Autocompletion of MODE property values #223

Closed
4 changes: 1 addition & 3 deletions src/main/java/sqlline/AbstractCommandHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,7 @@ public AbstractCommandHandler(SqlLine sqlLine, String[] names,
this.parameterCompleters =
Collections.singletonList(new NullCompleter());
} else {
List<Completer> c = new ArrayList<>(completers);
c.add(new NullCompleter());
this.parameterCompleters = c;
this.parameterCompleters = new ArrayList<>(completers);
}
}

Expand Down
11 changes: 8 additions & 3 deletions src/main/java/sqlline/Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,10 @@ public Collection<CommandHandler> getCommandHandlers(SqlLine sqlLine) {
TableNameCompleter tableCompleter = new TableNameCompleter(sqlLine);
List<Completer> empty = Collections.emptyList();
final Map<String, OutputFormat> outputFormats = getOutputFormats(sqlLine);
final Map<BuiltInProperty, Collection<String>> customPropertyCompletions =
new HashMap<>();
customPropertyCompletions
.put(BuiltInProperty.OUTPUT_FORMAT, outputFormats.keySet());
final CommandHandler[] handlers = {
new ReflectiveCommandHandler(sqlLine, empty, "quit", "done", "exit"),
new ReflectiveCommandHandler(sqlLine,
Expand Down Expand Up @@ -314,9 +318,10 @@ public Collection<CommandHandler> getCommandHandlers(SqlLine sqlLine) {
new ReflectiveCommandHandler(sqlLine, empty, "rollback"),
new ReflectiveCommandHandler(sqlLine, empty, "help", "?"),
new ReflectiveCommandHandler(sqlLine,
getOpts(sqlLine).optionCompleters(), "set"),
getOpts(sqlLine).setOptionCompleters(customPropertyCompletions),
"set"),
new ReflectiveCommandHandler(sqlLine,
getOpts(sqlLine).optionCompleters(), "reset"),
getOpts(sqlLine).resetOptionCompleters(), "reset"),
new ReflectiveCommandHandler(sqlLine, empty, "save"),
new ReflectiveCommandHandler(sqlLine, empty, "scan"),
new ReflectiveCommandHandler(sqlLine, empty, "sql"),
Expand Down Expand Up @@ -357,7 +362,7 @@ private Set<String> getMetadataMethodNames() {
}
}

private List<String> getIsolationLevels() {
List<String> getIsolationLevels() {
return Arrays.asList(
"TRANSACTION_NONE",
"TRANSACTION_READ_COMMITTED",
Expand Down
34 changes: 25 additions & 9 deletions src/main/java/sqlline/BuiltInProperty.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
package sqlline;

import java.io.File;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import org.jline.reader.LineReader;
import org.jline.reader.impl.history.DefaultHistory;
Expand All @@ -26,7 +30,8 @@ public enum BuiltInProperty implements SqlLineProperty {

AUTO_COMMIT("autoCommit", Type.BOOLEAN, true),
AUTO_SAVE("autoSave", Type.BOOLEAN, false),
COLOR_SCHEME("colorScheme", Type.STRING, DEFAULT),
COLOR_SCHEME("colorScheme", Type.STRING, DEFAULT,
true, false, new Application().getName2HighlightStyle().keySet()),
COLOR("color", Type.BOOLEAN, false),
CSV_DELIMITER("csvDelimiter", Type.STRING, ","),

Expand All @@ -41,19 +46,21 @@ public enum BuiltInProperty implements SqlLineProperty {
HISTORY_FILE("historyFile", Type.STRING,
new File(SqlLineOpts.saveDir(), "history").getAbsolutePath()),
INCREMENTAL("incremental", Type.BOOLEAN, true),
ISOLATION("isolation", Type.STRING, "TRANSACTION_REPEATABLE_READ"),
MAX_COLUMN_WIDTH("maxColumnWidth", Type.INTEGER, 15),
ISOLATION("isolation", Type.STRING, "TRANSACTION_REPEATABLE_READ",
true, false, new HashSet<>(new Application().getIsolationLevels())),
MAX_COLUMN_WIDTH("maxColumnWidth", Type.INTEGER, -1),
// don't save maxheight, maxwidth: it is automatically set based on
// the terminal configuration
MAX_HEIGHT("maxHeight", Type.INTEGER, 80, false, false),
MAX_WIDTH("maxWidth", Type.INTEGER, 80, false, false),
MAX_HEIGHT("maxHeight", Type.INTEGER, 80, false, false, null),
MAX_WIDTH("maxWidth", Type.INTEGER, 80, false, false, null),

MAX_HISTORY_ROWS("maxHistoryRows",
Type.INTEGER, DefaultHistory.DEFAULT_HISTORY_SIZE),
MAX_HISTORY_FILE_ROWS("maxHistoryFileRows",
Type.INTEGER, DefaultHistory.DEFAULT_HISTORY_FILE_SIZE),

MODE("mode", Type.STRING, LineReader.EMACS),
MODE("mode", Type.STRING, LineReader.EMACS, true,
false, new HashSet<>(Arrays.asList(LineReader.EMACS, "vi"))),
NUMBER_FORMAT("numberFormat", Type.STRING, DEFAULT),
NULL_VALUE("nullValue", Type.STRING, DEFAULT),
SILENT("silent", Type.BOOLEAN, false),
Expand All @@ -72,29 +79,34 @@ public enum BuiltInProperty implements SqlLineProperty {
TRIM_SCRIPTS("trimScripts", Type.BOOLEAN, true),
USE_LINE_CONTINUATION("useLineContinuation", Type.BOOLEAN, true),
VERBOSE("verbose", Type.BOOLEAN, false),
VERSION("version", Type.STRING, new Application().getVersion(), false, true);
VERSION("version",
Type.STRING, new Application().getVersion(), false, true, null);

private final String propertyName;
private final Type type;
private final Object defaultValue;
private final boolean couldBeStored;
private final boolean isReadOnly;
private final Set<String> availableValues;

BuiltInProperty(String propertyName, Type type, Object defaultValue) {
this(propertyName, type, defaultValue, true, false);
this(propertyName, type, defaultValue, true, false, null);
}

BuiltInProperty(
String propertyName,
Type type,
Object defaultValue,
boolean couldBeStored,
boolean isReadOnly) {
boolean isReadOnly,
Set availableValues) {
this.propertyName = propertyName;
this.type = type;
this.defaultValue = defaultValue;
this.isReadOnly = isReadOnly;
this.couldBeStored = couldBeStored;
this.availableValues = availableValues == null
? Collections.emptySet() : Collections.unmodifiableSet(availableValues);
}

@Override public String propertyName() {
Expand All @@ -117,6 +129,10 @@ public enum BuiltInProperty implements SqlLineProperty {
return type;
}

@Override public Set<String> getAvailableValues() {
return availableValues;
}

/** Returns the built-in property with the given name, or null if not found.
*
* @param propertyName Property name
Expand Down
20 changes: 14 additions & 6 deletions src/main/java/sqlline/SqlLineCommandCompleter.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import java.util.LinkedList;
import java.util.List;

import org.jline.builtins.Completers;
import org.jline.reader.Completer;
import org.jline.reader.impl.completer.AggregateCompleter;
import org.jline.reader.impl.completer.ArgumentCompleter;
Expand All @@ -26,16 +27,23 @@
class SqlLineCommandCompleter extends AggregateCompleter {
SqlLineCommandCompleter(SqlLine sqlLine) {
super(new LinkedList<>());
List<ArgumentCompleter> completers = new LinkedList<>();
List<Completer> completers = new LinkedList<>();

for (CommandHandler commandHandler : sqlLine.getCommandHandlers()) {
for (String cmd : commandHandler.getNames()) {
List<Completer> compl = new LinkedList<>();
compl.add(new StringsCompleter(SqlLine.COMMAND_PREFIX + cmd));
compl.addAll(commandHandler.getParameterCompleters());
compl.add(new NullCompleter()); // last param no complete

completers.add(new ArgumentCompleter(compl));
final List<Completer> parameterCompleters =
commandHandler.getParameterCompleters();
if (parameterCompleters.size() == 1
&& parameterCompleters.iterator().next()
instanceof Completers.RegexCompleter) {
completers.add(parameterCompleters.iterator().next());
} else {
compl.add(new StringsCompleter(SqlLine.COMMAND_PREFIX + cmd));
compl.addAll(parameterCompleters);
compl.add(new NullCompleter()); // last param no complete
completers.add(new ArgumentCompleter(compl));
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/main/java/sqlline/SqlLineCompleter.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class SqlLineCompleter

@Override public void complete(LineReader reader, ParsedLine line,
List<Candidate> candidates) {
String bufferStr = reader.getBuffer().substring(0);
final String bufferStr = reader.getBuffer().substring(0).trim();
if (bufferStr.startsWith(SqlLine.COMMAND_PREFIX)
&& !bufferStr.startsWith(SqlLine.COMMAND_PREFIX + "all")
&& !bufferStr.startsWith(SqlLine.COMMAND_PREFIX + "sql")) {
Expand Down
77 changes: 76 additions & 1 deletion src/main/java/sqlline/SqlLineOpts.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.util.*;
import java.util.stream.Collectors;

import org.jline.builtins.Completers;
import org.jline.keymap.KeyMap;
import org.jline.reader.Binding;
import org.jline.reader.Candidate;
Expand Down Expand Up @@ -115,10 +116,84 @@ public SqlLineOpts(SqlLine sqlLine, Properties props) {
loadProperties(props);
}

public List<Completer> optionCompleters() {
public List<Completer> resetOptionCompleters() {
return Collections.singletonList(this);
}

/**
* Builds and returns {@link org.jline.builtins.Completers.RegexCompleter} for
* <code>!set</code> command based on
* (in decreasing order of priority)
* <ul>
* <li>Customizations via {@code customCompletions}</li>
* <li>Available values defined in {@link BuiltInProperty}</li>
* <li>{@link Type} of property.
* Currently there is completion only for boolean type</li>
* </ul>
*
* @param customCompletions defines custom completions values per property
* @return a singleton list with a built RegexCompleter */
public List<Completer> setOptionCompleters(
Map<BuiltInProperty, Collection<String>> customCompletions) {
Map<String, Completer> comp = new HashMap<>();
final String start = "START";
comp.put(start, new StringsCompleter("!set"));
Collection<BuiltInProperty> booleanProperties = new ArrayList<>();
Collection<BuiltInProperty> withDefinedAvailableValues = new ArrayList<>();
StringBuilder sb = new StringBuilder(start + " (");
for (BuiltInProperty property : BuiltInProperty.values()) {
if (customCompletions.containsKey(property)) {
continue;
} else if (!property.getAvailableValues().isEmpty()) {
withDefinedAvailableValues.add(property);
} else if (property.type() == Type.BOOLEAN) {
booleanProperties.add(property);
} else {
sb.append(property.name()).append(" | ");
comp.put(property.name(),
new StringsCompleter(property.propertyName()));
}
}
// all boolean properties without defined available values and
// not customized via {@code customCompletions} have values
// for autocompletion specified in SqlLineProperty.BOOLEAN_VALUES
final String booleanTypeString = Type.BOOLEAN.toString();
sb.append(booleanTypeString);
comp.put(booleanTypeString,
new StringsCompleter(booleanProperties
.stream()
.map(BuiltInProperty::propertyName)
.toArray(String[]::new)));
final String booleanPropertyValueKey = booleanTypeString + "_value";
comp.put(booleanPropertyValueKey,
new StringsCompleter(BuiltInProperty.BOOLEAN_VALUES));
sb.append(" ").append(booleanPropertyValueKey);
// If a property has defined values they will be used for autocompletion
for (BuiltInProperty property : withDefinedAvailableValues) {
final String propertyName = property.propertyName();
sb.append(" | ").append(propertyName);
comp.put(propertyName, new StringsCompleter(propertyName));
final String propertyValueKey = propertyName + "_value";
comp.put(propertyValueKey,
new StringsCompleter(
property.getAvailableValues().toArray(new String[0])));
sb.append(" ").append(propertyValueKey);
}
for (Map.Entry<BuiltInProperty, Collection<String>> mapEntry
: customCompletions.entrySet()) {
final String propertyName = mapEntry.getKey().propertyName();
comp.put(propertyName, new StringsCompleter(propertyName));
final String propertyValueKey = propertyName + "_value";
comp.put(propertyValueKey,
new StringsCompleter(mapEntry.getValue().toArray(new String[0])));
sb.append("| ").append(propertyName).append(" ")
.append(propertyValueKey);
}
sb.append(") ");
return Collections.singletonList(
new Completers.RegexCompleter(sb.toString(), comp::get));
}

/**
* The save directory if HOME/.sqlline/ on UNIX, and HOME/sqlline/ on
* Windows.
Expand Down
11 changes: 9 additions & 2 deletions src/main/java/sqlline/SqlLineProperty.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,18 @@
*/
package sqlline;

import java.util.Set;

/**
* Definition of property that may be specified for SqlLine.
*
* @see BuiltInProperty
*/
public interface SqlLineProperty {
String DEFAULT = "default";
String[] BOOLEAN_VALUES = {
Boolean.TRUE.toString(), Boolean.FALSE.toString()};

String propertyName();

Object defaultValue();
Expand All @@ -28,6 +33,8 @@ public interface SqlLineProperty {

Type type();

Set<String> getAvailableValues();

/** Property writer. */
@FunctionalInterface
interface Writer {
Expand All @@ -38,8 +45,8 @@ interface Writer {
enum Type {
BOOLEAN,
CHAR,
STRING,
INTEGER;
INTEGER,
STRING;
}

}
Expand Down
Loading