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

fixes shell completion #454

Merged
merged 1 commit into from
May 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions modules/ringo/shell.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const styles = {
'function': term.CYAN,
'boolean': term.YELLOW,
'null': term.BOLD,
'undefined': term.LIGHT_GREY,
'date': term.MAGENTA,
'java': term.MAGENTA,
'custom': term.RED
Expand Down Expand Up @@ -187,7 +188,7 @@ const convert = (value, nesting, visited) => {
}
break;
case 'undefined':
retval = {};
retval.type = retval.string = 'undefined';
break;
default:
retval.string = String(value);
Expand All @@ -201,11 +202,9 @@ const convert = (value, nesting, visited) => {
* @param {Stream} writer
*/
const printResult = exports.printResult = (value, writer) => {
if (typeof value !== "undefined") {
writer = writer || term;
printValue(convert(value, 0, []), writer, 0);
writer.writeln();
}
writer = writer || term;
printValue(convert(value, 0, []), writer, 0);
writer.writeln();
};

const printValue = (value, writer, nesting) => {
Expand Down
2 changes: 2 additions & 0 deletions modules/ringo/term.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ exports.ONMAGENTA = "\u001B[45m";
exports.ONCYAN = "\u001B[46m";
exports.ONWHITE = "\u001B[47m";

exports.LIGHT_GREY = "\u001B[38;5;242;1m";

// used to remove ANSI control sequences if disabled
const cleaner = /\u001B\[\d*(?:;\d+)?[a-zA-Z]/g;
// used to check if a string consists only of ANSI control sequences
Expand Down
6 changes: 1 addition & 5 deletions src/org/ringojs/engine/ModuleScope.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,7 @@ public ModuleScope(String moduleId, Trackable source,
delete("constructor");
// for activating the ImporterTopLevel import* functions
activatePrototypeMap(3);
try {
cacheBuiltins(this, false);
} catch (NoSuchMethodError e) {
// allows us to run with older versions of Rhino
}
cacheBuiltins(this, false);
this.source = source;
this.repository = source instanceof Repository ?
(Repository) source : source.getParentRepository();
Expand Down
19 changes: 3 additions & 16 deletions src/org/ringojs/engine/RhinoEngine.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,7 @@

package org.ringojs.engine;

import org.mozilla.javascript.Context;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.JavaScriptException;
import org.mozilla.javascript.ScriptRuntime;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.Undefined;
import org.mozilla.javascript.WrapFactory;
import org.mozilla.javascript.Wrapper;
import org.mozilla.javascript.*;
import org.mozilla.javascript.json.JsonParser;
import org.ringojs.repository.*;
import org.ringojs.tools.RingoDebugger;
Expand Down Expand Up @@ -262,7 +254,7 @@ public Object invoke(Object module, String method, Object... args)
}

/**
* Associate a worker with the current worker and return the worker
* Associate a worker with the current thread and return the worker
* that was previously associated with it, or null.
*
* @param worker the new worker associated with the current thread
Expand Down Expand Up @@ -385,12 +377,7 @@ public Scriptable getShellScope(RingoWorker worker) throws IOException {
Repository repository = new FileRepository("");
repository.setAbsolute(true);
Scriptable protoScope = mainScope != null ? mainScope : globalScope;
contextFactory.enterContext();
try {
return new ModuleScope("<shell>", repository, protoScope, worker);
} finally {
Context.exit();
}
return contextFactory.call(cx -> new ModuleScope("<shell>", repository, protoScope, worker));
}

/**
Expand Down
9 changes: 9 additions & 0 deletions src/org/ringojs/engine/RingoWorker.java
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,15 @@ public Object invoke(Object module, Object function, Object... args)
}
}

public Object getProperty(Scriptable obj, String name) {
RingoWorker previous = acquireWorker();
try {
return ScriptableObject.getProperty(obj, name);
} finally {
releaseWorker(previous);
}
}

/**
* <p>Submit a function to be invoked on the worker's event loop thread and
* return a future for the result.</p>
Expand Down
104 changes: 47 additions & 57 deletions src/org/ringojs/tools/RingoShell.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import org.jline.reader.impl.history.DefaultHistory;
import org.jline.terminal.Terminal;
import org.jline.terminal.TerminalBuilder;
import org.ringojs.engine.ModuleScope;
import org.ringojs.engine.ReloadableScript;
import org.ringojs.engine.RhinoEngine;
import org.ringojs.engine.RingoConfig;
Expand Down Expand Up @@ -98,10 +97,11 @@ public void run() throws IOException {
.variable(LineReader.INDENTATION, 4)
.history(new DefaultHistory())
.completer(new AggregateCompleter(
new JSCompleter(terminal),
new JSCompleter(),
new StringsCompleter(getJsKeywordCandidates())
))
.option(LineReader.Option.HISTORY_TIMESTAMPED, false)
.option(LineReader.Option.DISABLE_EVENT_EXPANSION, true)
.build();
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try {
Expand All @@ -123,6 +123,9 @@ public void run() throws IOException {
} catch (EndOfFileException e) {
break;
}
if (source.trim().length() == 0) {
continue;
}
try {
Resource res = new StringResource("<stdin>", source, lineno);
ReloadableScript script = new ReloadableScript(res, engine);
Expand Down Expand Up @@ -201,18 +204,14 @@ private void runSilently() throws IOException {

// preload ringo/shell in separate thread
private void preloadShellModule() {
Thread t = new Thread() {
public void run() {
Context cx = engine.getContextFactory().enterContext(null);
try {
worker.loadModule(cx, "ringo/shell", null);
} catch (Exception ignore) {
// ignore
} finally {
Context.exit();
}
Thread t = new Thread(() -> engine.getContextFactory().call(cx -> {
try {
worker.loadModule(cx, "ringo/shell", null);
} catch (Exception ignore) {
// ignore
}
};
return null;
}));
t.setPriority(Thread.MIN_PRIORITY);
t.setDaemon(true);
t.start();
Expand All @@ -227,65 +226,56 @@ private Candidate[] getJsKeywordCandidates() {
class JSCompleter implements Completer {

final Pattern variables = Pattern.compile("(^|\\s|[^\\w.'\"])([\\w.]+)$");
Terminal terminal;

JSCompleter(Terminal terminal) {
this.terminal = terminal;
}

@Override
public void complete(LineReader lineReader, ParsedLine parsedLine, List<Candidate> list) {
try {
String word = parsedLine.word();
Matcher match = variables.matcher(word);
if (match.find()) {
String path = match.group(2);
Scriptable obj = scope;
String[] parts = path.split("\\.", -1);
for (int k = 0; k < parts.length - 1; k++) {
Object o = ScriptableObject.getProperty(obj, parts[k]);
if (o == null || o == ScriptableObject.NOT_FOUND) {
return;
}
obj = ScriptRuntime.toObject(scope, o);
}
String lastPart = parts[parts.length - 1];
String value = word.substring(0, word.length() - lastPart.length());
String display = path.substring(0, path.length() - lastPart.length());
while (obj != null) {
Object[] ids = obj.getIds();
collectIds(ids, obj, value, display, list);
if (list.size() <= 3 && obj instanceof ScriptableObject) {
ids = ((ScriptableObject) obj).getAllIds();
collectIds(ids, obj, value, display, list);
}
if (word.endsWith(".") && obj instanceof ModuleScope) {
// don't walk scope prototype chain if nothing to compare yet -
// the list is just too long.
break;
}
obj = obj.getPrototype();
Scriptable obj = scope;
String word = parsedLine.word();
Matcher match = variables.matcher(word);
String value = "";
String display = "";
if (match.find()) {
String path = match.group(2);
String[] parts = path.split("\\.", -1);
for (int k = 0; k < parts.length - 1; k++) {
Object o = worker.getProperty(obj, parts[k]);
if (o == null || o == ScriptableObject.NOT_FOUND) {
return;
}
obj = ScriptRuntime.toObject(scope, o);
}
} catch (Exception ignore) {
// ignore.printStackTrace();
String lastPart = parts[parts.length - 1];
value = word.substring(0, word.length() - lastPart.length());
display = path.substring(0, path.length() - lastPart.length());
}
while (obj != null) {
Object[] ids = obj.getIds();
collectIds(ids, obj, value, display, list);
if (obj instanceof ScriptableObject) {
ids = ((ScriptableObject) obj).getAllIds();
collectIds(ids, obj, value, display, list);
}
obj = obj.getPrototype();
}
}

private void collectIds(Object[] ids, Scriptable obj, String value, String display, List<Candidate> list) {
private void collectIds(Object[] ids, Scriptable obj, String value, String display, List<Candidate> candidates) {
for (Object id: ids) {
if (!(id instanceof String)) {
continue;
}
String str = (String) id;
if (ScriptableObject.getProperty(obj, str) instanceof Callable) {
str += "(";
String idStr = (String) id;
// use the thread worker to retrieve properties: JS getters in global
// scope using `require()` need a worker associated with the current
// thread (i.e. console getter)
Object property = worker.getProperty(obj, idStr);
if (property instanceof Callable) {
idStr += "(";
}
Candidate candidate = new Candidate(value + str, display + str, null, null, null, null, false);
list.add(candidate);
Candidate candidate = new Candidate(value + idStr, display + idStr, null, null, null, null, false);
candidates.add(candidate);
}
}

}

private static final String[] jsKeywords =
Expand Down