-
-
Notifications
You must be signed in to change notification settings - Fork 925
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Revert "Revert "Merge pull request #3917 from jruby/truffle_readline""
This reverts commit 005ec3d.
- 9.4.12.0
- 9.4.11.0
- 9.4.10.0
- 9.4.9.0
- 9.4.8.0
- 9.4.7.0
- 9.4.6.0
- 9.4.5.0
- 9.4.4.0
- 9.4.3.0
- 9.4.2.0
- 9.4.1.0
- 9.4.0.0
- 9.3.15.0
- 9.3.14.0
- 9.3.13.0
- 9.3.12.0
- 9.3.11.0
- 9.3.10.0
- 9.3.9.0
- 9.3.8.0
- 9.3.7.0
- 9.3.6.0
- 9.3.5.0
- 9.3.4.0
- 9.3.3.0
- 9.3.2.0
- 9.3.1.0
- 9.3.0.0
- 9.2.21.0
- 9.2.20.1
- 9.2.20.0
- 9.2.19.0
- 9.2.18.0
- 9.2.17.0
- 9.2.16.0
- 9.2.15.0
- 9.2.14.0
- 9.2.13.0
- 9.2.12.0
- 9.2.11.1
- 9.2.11.0
- 9.2.10.0
- 9.2.9.0
- 9.2.8.0
- 9.2.7.0
- 9.2.6.0
- 9.2.5.0
- 9.2.4.1
- 9.2.4.0
- 9.2.3.0
- 9.2.2.0
- 9.2.1.0
- 9.2.0.0
- 9.1.17.0
- 9.1.16.0
- 9.1.15.0
- 9.1.14.0
- 9.1.13.0
- 9.1.12.0
- 9.1.11.0
- 9.1.10.0
- 9.1.9.0
- 9.1.8.0
- 9.1.7.0
Showing
21 changed files
with
892 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
# Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved. This | ||
# code is released under a tri EPL/GPL/LGPL license. You can use it, | ||
# redistribute it and/or modify it under the terms of the: | ||
# | ||
# Eclipse Public License version 1.0 | ||
# GNU General Public License version 2 | ||
# GNU Lesser General Public License version 2.1 | ||
|
||
Readline = Truffle::Readline | ||
|
||
module Readline | ||
|
||
HISTORY = Object.new | ||
VERSION = 'JLine wrapper' | ||
|
||
module_function | ||
|
||
%i[ | ||
basic_quote_characters | ||
basic_quote_characters= | ||
completer_quote_characters | ||
completer_quote_characters= | ||
completer_word_break_characters | ||
completer_word_break_characters= | ||
completion_append_character | ||
completion_append_character= | ||
completion_case_fold | ||
completion_case_fold= | ||
completion_proc | ||
completion_proc= | ||
emacs_editing_mode | ||
emacs_editing_mode? | ||
filename_quote_characters | ||
filename_quote_characters= | ||
point= | ||
pre_input_hook | ||
pre_input_hook= | ||
redisplay | ||
set_screen_size | ||
special_prefixes | ||
special_prefixes= | ||
vi_editing_mode | ||
vi_editing_mode? | ||
set_screen_size | ||
].each do |method_name| | ||
define_method(method_name) do | ||
raise NotImplementedError.new("#{method_name}() function is unimplemented on this machine") | ||
end | ||
end | ||
|
||
def input=(input) | ||
# TODO (nirvdrum 20-May-16): This should do something functional. | ||
nil | ||
end | ||
|
||
def output=(output) | ||
# TODO (nirvdrum 20-May-16): This should do something functional. | ||
nil | ||
end | ||
|
||
end | ||
|
||
class << Readline::HISTORY | ||
|
||
include Enumerable | ||
include Truffle::ReadlineHistory | ||
|
||
def empty? | ||
size == 0 | ||
end | ||
|
||
def to_s | ||
'HISTORY' | ||
end | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 2 additions & 0 deletions
2
spec/truffle/tags/library/readline/basic_quote_characters_tags.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
fails:Readline.basic_quote_characters returns not nil | ||
fails:Readline.basic_quote_characters= returns the passed string |
2 changes: 2 additions & 0 deletions
2
spec/truffle/tags/library/readline/completer_quote_characters_tags.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
fails:Readline.completer_quote_characters returns nil | ||
fails:Readline.completer_quote_characters= returns the passed string |
2 changes: 2 additions & 0 deletions
2
spec/truffle/tags/library/readline/completer_word_break_characters_tags.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
fails:Readline.completer_word_break_characters returns nil | ||
fails:Readline.completer_word_break_characters= returns the passed string |
2 changes: 2 additions & 0 deletions
2
spec/truffle/tags/library/readline/completion_append_character_tags.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
fails:Readline.completion_append_character returns not nil | ||
fails:Readline.completion_append_character= returns the first character of the passed string |
2 changes: 2 additions & 0 deletions
2
spec/truffle/tags/library/readline/completion_case_fold_tags.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
fails:Readline.completion_case_fold returns nil | ||
fails:Readline.completion_case_fold= returns the passed boolean |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
fails:Readline.completion_proc returns nil | ||
fails:Readline.completion_proc= returns the passed Proc | ||
fails:Readline.completion_proc= returns an ArgumentError if not given an Proc or #call |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
fails:Readline.emacs_editing_mode returns nil |
2 changes: 2 additions & 0 deletions
2
spec/truffle/tags/library/readline/filename_quote_characters_tags.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
fails:Readline.filename_quote_characters returns nil | ||
fails:Readline.filename_quote_characters= returns the passed string |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
fails:Readline.vi_editing_mode returns nil |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
50 changes: 50 additions & 0 deletions
50
truffle/src/main/java/org/jruby/truffle/core/cast/NameToJavaStringWithDefaultNode.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
/* | ||
* Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved. This | ||
* code is released under a tri EPL/GPL/LGPL license. You can use it, | ||
* redistribute it and/or modify it under the terms of the: | ||
* | ||
* Eclipse Public License version 1.0 | ||
* GNU General Public License version 2 | ||
* GNU Lesser General Public License version 2.1 | ||
*/ | ||
package org.jruby.truffle.core.cast; | ||
|
||
import com.oracle.truffle.api.dsl.NodeChild; | ||
import com.oracle.truffle.api.dsl.Specialization; | ||
import com.oracle.truffle.api.frame.VirtualFrame; | ||
import com.oracle.truffle.api.source.SourceSection; | ||
import org.jruby.truffle.RubyContext; | ||
import org.jruby.truffle.language.NotProvided; | ||
import org.jruby.truffle.language.RubyNode; | ||
|
||
/** | ||
* Take a Symbol or some object accepting #to_str | ||
* and convert it to a Java String and defaults to | ||
* the given value if not provided. | ||
*/ | ||
@NodeChild(value = "value", type = RubyNode.class) | ||
public abstract class NameToJavaStringWithDefaultNode extends RubyNode { | ||
|
||
private final String defaultValue; | ||
@Child private NameToJavaStringNode toJavaStringNode; | ||
|
||
public NameToJavaStringWithDefaultNode(RubyContext context, SourceSection sourceSection, String defaultValue) { | ||
super(context, sourceSection); | ||
this.defaultValue = defaultValue; | ||
toJavaStringNode = NameToJavaStringNodeGen.create(null); | ||
} | ||
|
||
public abstract String executeString(VirtualFrame frame, Object value); | ||
|
||
@Specialization | ||
public String doDefault(VirtualFrame frame, NotProvided value) { | ||
return toJavaStringNode.executeToJavaString(frame, defaultValue); | ||
} | ||
|
||
@Specialization(guards = "wasProvided(value)") | ||
public String doProvided(VirtualFrame frame, Object value) { | ||
return toJavaStringNode.executeToJavaString(frame, value); | ||
} | ||
|
||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
88 changes: 88 additions & 0 deletions
88
truffle/src/main/java/org/jruby/truffle/stdlib/readline/ConsoleHolder.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
/* | ||
* Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved. This | ||
* code is released under a tri EPL/GPL/LGPL license. You can use it, | ||
* redistribute it and/or modify it under the terms of the: | ||
* | ||
* Eclipse Public License version 1.0 | ||
* GNU General Public License version 2 | ||
* GNU Lesser General Public License version 2.1 | ||
* | ||
* This code is modified from the Readline JRuby extension module | ||
* implementation with the following header: | ||
* | ||
* Version: EPL 1.0/GPL 2.0/LGPL 2.1 | ||
* | ||
* The contents of this file are subject to the Eclipse Public | ||
* License Version 1.0 (the "License"); you may not use this file | ||
* except in compliance with the License. You may obtain a copy of | ||
* the License at http://www.eclipse.org/legal/epl-v10.html | ||
* | ||
* Software distributed under the License is distributed on an "AS | ||
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or | ||
* implied. See the License for the specific language governing | ||
* rights and limitations under the License. | ||
* | ||
* Copyright (C) 2006 Ola Bini <ola@ologix.com> | ||
* Copyright (C) 2006 Damian Steer <pldms@mac.com> | ||
* Copyright (C) 2008 Joseph LaFata <joe@quibb.org> | ||
* | ||
* Alternatively, the contents of this file may be used under the terms of | ||
* either of the GNU General Public License Version 2 or later (the "GPL"), | ||
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), | ||
* in which case the provisions of the GPL or the LGPL are applicable instead | ||
* of those above. If you wish to allow use of your version of this file only | ||
* under the terms of either the GPL or the LGPL, and not to allow others to | ||
* use your version of this file under the terms of the EPL, indicate your | ||
* decision by deleting the provisions above and replace them with the notice | ||
* and other provisions required by the GPL or the LGPL. If you do not delete | ||
* the provisions above, a recipient may use your version of this file under | ||
* the terms of any one of the EPL, the GPL or the LGPL. | ||
*/ | ||
package org.jruby.truffle.stdlib.readline; | ||
|
||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; | ||
import jline.console.ConsoleReader; | ||
import jline.console.completer.Completer; | ||
import jline.console.history.History; | ||
import jline.console.history.MemoryHistory; | ||
|
||
import java.io.IOException; | ||
|
||
public class ConsoleHolder { | ||
|
||
private final ConsoleReader readline; | ||
private final Completer currentCompleter; | ||
private final History history; | ||
|
||
@TruffleBoundary | ||
public ConsoleHolder() { | ||
try { | ||
readline = new ConsoleReader(); | ||
} catch (IOException e) { | ||
throw new UnsupportedOperationException("Couldn't initialize readline", e); | ||
} | ||
|
||
readline.setHistoryEnabled(false); | ||
readline.setPaginationEnabled(true); | ||
readline.setBellEnabled(true); | ||
|
||
currentCompleter = new ReadlineNodes.RubyFileNameCompleter(); | ||
readline.addCompleter(currentCompleter); | ||
|
||
history = new MemoryHistory(); | ||
readline.setHistory(history); | ||
} | ||
|
||
public ConsoleReader getReadline() { | ||
return readline; | ||
} | ||
|
||
public Completer getCurrentCompleter() { | ||
return currentCompleter; | ||
} | ||
|
||
public History getHistory() { | ||
return history; | ||
} | ||
|
||
} |
296 changes: 296 additions & 0 deletions
296
truffle/src/main/java/org/jruby/truffle/stdlib/readline/ReadlineHistoryNodes.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,296 @@ | ||
/* | ||
* Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved. This | ||
* code is released under a tri EPL/GPL/LGPL license. You can use it, | ||
* redistribute it and/or modify it under the terms of the: | ||
* | ||
* Eclipse Public License version 1.0 | ||
* GNU General Public License version 2 | ||
* GNU Lesser General Public License version 2.1 | ||
* | ||
* This code is modified from the Readline JRuby extension module | ||
* implementation with the following header: | ||
* | ||
* Version: EPL 1.0/GPL 2.0/LGPL 2.1 | ||
* | ||
* The contents of this file are subject to the Eclipse Public | ||
* License Version 1.0 (the "License"); you may not use this file | ||
* except in compliance with the License. You may obtain a copy of | ||
* the License at http://www.eclipse.org/legal/epl-v10.html | ||
* | ||
* Software distributed under the License is distributed on an "AS | ||
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or | ||
* implied. See the License for the specific language governing | ||
* rights and limitations under the License. | ||
* | ||
* Copyright (C) 2006 Ola Bini <ola@ologix.com> | ||
* Copyright (C) 2006 Damian Steer <pldms@mac.com> | ||
* Copyright (C) 2008 Joseph LaFata <joe@quibb.org> | ||
* | ||
* Alternatively, the contents of this file may be used under the terms of | ||
* either of the GNU General Public License Version 2 or later (the "GPL"), | ||
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), | ||
* in which case the provisions of the GPL or the LGPL are applicable instead | ||
* of those above. If you wish to allow use of your version of this file only | ||
* under the terms of either the GPL or the LGPL, and not to allow others to | ||
* use your version of this file under the terms of the EPL, indicate your | ||
* decision by deleting the provisions above and replace them with the notice | ||
* and other provisions required by the GPL or the LGPL. If you do not delete | ||
* the provisions above, a recipient may use your version of this file under | ||
* the terms of any one of the EPL, the GPL or the LGPL. | ||
*/ | ||
package org.jruby.truffle.stdlib.readline; | ||
|
||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; | ||
import com.oracle.truffle.api.dsl.CreateCast; | ||
import com.oracle.truffle.api.dsl.NodeChild; | ||
import com.oracle.truffle.api.dsl.NodeChildren; | ||
import com.oracle.truffle.api.dsl.Specialization; | ||
import com.oracle.truffle.api.frame.VirtualFrame; | ||
import com.oracle.truffle.api.object.DynamicObject; | ||
import com.oracle.truffle.api.source.SourceSection; | ||
import jline.console.history.History; | ||
import org.jruby.truffle.RubyContext; | ||
import org.jruby.truffle.builtins.CoreClass; | ||
import org.jruby.truffle.builtins.CoreMethod; | ||
import org.jruby.truffle.builtins.CoreMethodArrayArgumentsNode; | ||
import org.jruby.truffle.builtins.CoreMethodNode; | ||
import org.jruby.truffle.builtins.YieldingCoreMethodNode; | ||
import org.jruby.truffle.core.cast.NameToJavaStringNode; | ||
import org.jruby.truffle.core.cast.NameToJavaStringNodeGen; | ||
import org.jruby.truffle.core.cast.ToIntNodeGen; | ||
import org.jruby.truffle.core.string.StringOperations; | ||
import org.jruby.truffle.language.RubyNode; | ||
import org.jruby.truffle.language.control.RaiseException; | ||
import org.jruby.truffle.language.objects.TaintNode; | ||
import org.jruby.truffle.language.objects.TaintNodeGen; | ||
|
||
@CoreClass("Truffle::ReadlineHistory") | ||
public abstract class ReadlineHistoryNodes { | ||
|
||
@CoreMethod(names = { "push", "<<" }, rest = true) | ||
public abstract static class PushNode extends CoreMethodArrayArgumentsNode { | ||
|
||
@Child private NameToJavaStringNode toJavaStringNode; | ||
|
||
public PushNode(RubyContext context, SourceSection sourceSection) { | ||
super(context, sourceSection); | ||
toJavaStringNode = NameToJavaStringNodeGen.create(); | ||
} | ||
|
||
@Specialization | ||
public DynamicObject push(VirtualFrame frame, DynamicObject history, Object... lines) { | ||
for (Object line : lines) { | ||
final String asString = toJavaStringNode.executeToJavaString(frame, line); | ||
addToHistory(asString); | ||
} | ||
|
||
return history; | ||
} | ||
|
||
@TruffleBoundary | ||
private void addToHistory(String item) { | ||
getContext().getConsoleHolder().getHistory().add(item); | ||
} | ||
|
||
} | ||
|
||
@CoreMethod(names = "pop", needsSelf = false) | ||
public abstract static class PopNode extends CoreMethodArrayArgumentsNode { | ||
|
||
@Child private TaintNode taintNode; | ||
|
||
public PopNode(RubyContext context, SourceSection sourceSection) { | ||
super(context, sourceSection); | ||
taintNode = TaintNodeGen.create(context, sourceSection, null); | ||
} | ||
|
||
@TruffleBoundary | ||
@Specialization | ||
public Object pop() { | ||
final ConsoleHolder consoleHolder = getContext().getConsoleHolder(); | ||
|
||
if (consoleHolder.getHistory().isEmpty()) { | ||
return nil(); | ||
} | ||
|
||
final String lastLine = consoleHolder.getHistory().removeLast().toString(); | ||
final DynamicObject ret = createString(StringOperations.encodeRope(lastLine, getDefaultInternalEncoding())); | ||
|
||
return taintNode.executeTaint(ret); | ||
} | ||
|
||
} | ||
|
||
@CoreMethod(names = "shift", needsSelf = false) | ||
public abstract static class ShiftNode extends CoreMethodArrayArgumentsNode { | ||
|
||
@Child private TaintNode taintNode; | ||
|
||
public ShiftNode(RubyContext context, SourceSection sourceSection) { | ||
super(context, sourceSection); | ||
taintNode = TaintNodeGen.create(context, sourceSection, null); | ||
} | ||
|
||
@TruffleBoundary | ||
@Specialization | ||
public Object shift() { | ||
final ConsoleHolder consoleHolder = getContext().getConsoleHolder(); | ||
|
||
if (consoleHolder.getHistory().isEmpty()) { | ||
return nil(); | ||
} | ||
|
||
final String lastLine = consoleHolder.getHistory().removeFirst().toString(); | ||
final DynamicObject ret = createString(StringOperations.encodeRope(lastLine, getDefaultInternalEncoding())); | ||
|
||
return taintNode.executeTaint(ret); | ||
} | ||
|
||
} | ||
|
||
@CoreMethod(names = { "length", "size" }, needsSelf = false) | ||
public abstract static class LengthNode extends CoreMethodArrayArgumentsNode { | ||
|
||
@TruffleBoundary | ||
@Specialization | ||
public int length() { | ||
return getContext().getConsoleHolder().getHistory().size(); | ||
} | ||
|
||
} | ||
|
||
@CoreMethod(names = "clear", needsSelf = false) | ||
public abstract static class ClearNode extends CoreMethodArrayArgumentsNode { | ||
|
||
@TruffleBoundary | ||
@Specialization | ||
public DynamicObject clear() { | ||
getContext().getConsoleHolder().getHistory().clear(); | ||
|
||
return nil(); | ||
} | ||
|
||
} | ||
|
||
@CoreMethod(names = "each", needsBlock = true) | ||
public abstract static class EachNode extends YieldingCoreMethodNode { | ||
|
||
@Child private TaintNode taintNode; | ||
|
||
public EachNode(RubyContext context, SourceSection sourceSection) { | ||
super(context, sourceSection); | ||
taintNode = TaintNodeGen.create(context, sourceSection, null); | ||
} | ||
|
||
@Specialization | ||
public DynamicObject each(VirtualFrame frame, DynamicObject history, DynamicObject block) { | ||
final ConsoleHolder consoleHolder = getContext().getConsoleHolder(); | ||
|
||
for (final History.Entry e : consoleHolder.getHistory()) { | ||
final DynamicObject line = createString(StringOperations.encodeRope(historyEntryToString(e), getDefaultInternalEncoding())); | ||
|
||
yield(frame, block, taintNode.executeTaint(line)); | ||
} | ||
|
||
return history; | ||
} | ||
|
||
@TruffleBoundary | ||
private String historyEntryToString(History.Entry entry) { | ||
return entry.value().toString(); | ||
} | ||
|
||
} | ||
|
||
@CoreMethod(names = "[]", needsSelf = false, required = 1, lowerFixnum = 1) | ||
public abstract static class GetIndexNode extends CoreMethodArrayArgumentsNode { | ||
|
||
@Child private TaintNode taintNode; | ||
|
||
public GetIndexNode(RubyContext context, SourceSection sourceSection) { | ||
super(context, sourceSection); | ||
taintNode = TaintNodeGen.create(context, sourceSection, null); | ||
} | ||
|
||
@TruffleBoundary | ||
@Specialization | ||
public Object getIndex(int index) { | ||
final ConsoleHolder consoleHolder = getContext().getConsoleHolder(); | ||
|
||
final int normalizedIndex = index < 0 ? index + consoleHolder.getHistory().size() : index; | ||
|
||
try { | ||
final String line = consoleHolder.getHistory().get(normalizedIndex).toString(); | ||
final DynamicObject ret = createString(StringOperations.encodeRope(line, getDefaultInternalEncoding())); | ||
|
||
return taintNode.executeTaint(ret); | ||
} catch (IndexOutOfBoundsException e) { | ||
throw new RaiseException(coreExceptions().indexErrorInvalidIndex(this)); | ||
} | ||
} | ||
|
||
} | ||
|
||
@CoreMethod(names = "[]=", needsSelf = false, required = 2) | ||
@NodeChildren({ | ||
@NodeChild(type = RubyNode.class, value = "index"), | ||
@NodeChild(type = RubyNode.class, value = "line") | ||
}) | ||
public abstract static class SetIndexNode extends CoreMethodNode { | ||
|
||
@CreateCast("index") public RubyNode coerceIndexToInt(RubyNode index) { | ||
return ToIntNodeGen.create(index); | ||
} | ||
|
||
@CreateCast("line") public RubyNode coerceLineToJavaString(RubyNode line) { | ||
return NameToJavaStringNodeGen.create(line); | ||
} | ||
|
||
@TruffleBoundary | ||
@Specialization | ||
public Object setIndex(int index, String line) { | ||
final ConsoleHolder consoleHolder = getContext().getConsoleHolder(); | ||
|
||
final int normalizedIndex = index < 0 ? index + consoleHolder.getHistory().size() : index; | ||
|
||
try { | ||
consoleHolder.getHistory().set(normalizedIndex, line); | ||
|
||
return nil(); | ||
} catch (IndexOutOfBoundsException e) { | ||
throw new RaiseException(coreExceptions().indexErrorInvalidIndex(this)); | ||
} | ||
} | ||
|
||
} | ||
|
||
@CoreMethod(names = "delete_at", needsSelf = false, required = 1, lowerFixnum = 1) | ||
public abstract static class DeleteAtNode extends CoreMethodArrayArgumentsNode { | ||
|
||
@Child private TaintNode taintNode; | ||
|
||
public DeleteAtNode(RubyContext context, SourceSection sourceSection) { | ||
super(context, sourceSection); | ||
taintNode = TaintNodeGen.create(context, sourceSection, null); | ||
} | ||
|
||
@TruffleBoundary | ||
@Specialization | ||
public Object deleteAt(int index) { | ||
final ConsoleHolder consoleHolder = getContext().getConsoleHolder(); | ||
|
||
final int normalizedIndex = index < 0 ? index + consoleHolder.getHistory().size() : index; | ||
|
||
try { | ||
final String line = consoleHolder.getHistory().remove(normalizedIndex).toString(); | ||
final DynamicObject ret = createString(StringOperations.encodeRope(line, getDefaultInternalEncoding())); | ||
|
||
return taintNode.executeTaint(ret); | ||
} catch (IndexOutOfBoundsException e) { | ||
throw new RaiseException(coreExceptions().indexErrorInvalidIndex(this)); | ||
} | ||
} | ||
|
||
} | ||
|
||
} |
330 changes: 330 additions & 0 deletions
330
truffle/src/main/java/org/jruby/truffle/stdlib/readline/ReadlineNodes.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,330 @@ | ||
/* | ||
* Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved. This | ||
* code is released under a tri EPL/GPL/LGPL license. You can use it, | ||
* redistribute it and/or modify it under the terms of the: | ||
* | ||
* Eclipse Public License version 1.0 | ||
* GNU General Public License version 2 | ||
* GNU Lesser General Public License version 2.1 | ||
* | ||
* This code is modified from the Readline JRuby extension module | ||
* implementation with the following header: | ||
* | ||
* Version: EPL 1.0/GPL 2.0/LGPL 2.1 | ||
* | ||
* The contents of this file are subject to the Eclipse Public | ||
* License Version 1.0 (the "License"); you may not use this file | ||
* except in compliance with the License. You may obtain a copy of | ||
* the License at http://www.eclipse.org/legal/epl-v10.html | ||
* | ||
* Software distributed under the License is distributed on an "AS | ||
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or | ||
* implied. See the License for the specific language governing | ||
* rights and limitations under the License. | ||
* | ||
* Copyright (C) 2006 Ola Bini <ola@ologix.com> | ||
* Copyright (C) 2006 Damian Steer <pldms@mac.com> | ||
* Copyright (C) 2008 Joseph LaFata <joe@quibb.org> | ||
* | ||
* Alternatively, the contents of this file may be used under the terms of | ||
* either of the GNU General Public License Version 2 or later (the "GPL"), | ||
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), | ||
* in which case the provisions of the GPL or the LGPL are applicable instead | ||
* of those above. If you wish to allow use of your version of this file only | ||
* under the terms of either the GPL or the LGPL, and not to allow others to | ||
* use your version of this file under the terms of the EPL, indicate your | ||
* decision by deleting the provisions above and replace them with the notice | ||
* and other provisions required by the GPL or the LGPL. If you do not delete | ||
* the provisions above, a recipient may use your version of this file under | ||
* the terms of any one of the EPL, the GPL or the LGPL. | ||
*/ | ||
package org.jruby.truffle.stdlib.readline; | ||
|
||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; | ||
import com.oracle.truffle.api.dsl.CreateCast; | ||
import com.oracle.truffle.api.dsl.NodeChild; | ||
import com.oracle.truffle.api.dsl.NodeChildren; | ||
import com.oracle.truffle.api.dsl.Specialization; | ||
import com.oracle.truffle.api.object.DynamicObject; | ||
import com.oracle.truffle.api.source.SourceSection; | ||
import jline.console.CursorBuffer; | ||
import jline.console.completer.Completer; | ||
import jline.console.completer.FileNameCompleter; | ||
import org.jcodings.specific.UTF8Encoding; | ||
import org.jruby.truffle.RubyContext; | ||
import org.jruby.truffle.builtins.CoreClass; | ||
import org.jruby.truffle.builtins.CoreMethod; | ||
import org.jruby.truffle.builtins.CoreMethodArrayArgumentsNode; | ||
import org.jruby.truffle.builtins.CoreMethodNode; | ||
import org.jruby.truffle.core.array.ArrayHelpers; | ||
import org.jruby.truffle.core.cast.BooleanCastWithDefaultNodeGen; | ||
import org.jruby.truffle.core.cast.NameToJavaStringNodeGen; | ||
import org.jruby.truffle.core.cast.NameToJavaStringWithDefaultNodeGen; | ||
import org.jruby.truffle.core.cast.ToStrNodeGen; | ||
import org.jruby.truffle.core.rope.RopeOperations; | ||
import org.jruby.truffle.core.string.StringOperations; | ||
import org.jruby.truffle.language.RubyNode; | ||
import org.jruby.truffle.language.control.RaiseException; | ||
import org.jruby.truffle.language.objects.TaintNode; | ||
import org.jruby.truffle.language.objects.TaintNodeGen; | ||
|
||
import java.io.IOException; | ||
import java.nio.CharBuffer; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
@CoreClass("Truffle::Readline") | ||
public abstract class ReadlineNodes { | ||
|
||
@CoreMethod(names = "basic_word_break_characters", onSingleton = true) | ||
public abstract static class BasicWordBreakCharactersNode extends CoreMethodArrayArgumentsNode { | ||
|
||
@Specialization | ||
public DynamicObject basicWordBreakCharacters() { | ||
return createString(StringOperations.encodeRope(ProcCompleter.getDelimiter(), UTF8Encoding.INSTANCE)); | ||
} | ||
|
||
} | ||
|
||
@CoreMethod(names = "basic_word_break_characters=", onSingleton = true, required = 1) | ||
@NodeChild(type = RubyNode.class, value = "characters") | ||
public abstract static class SetBasicWordBreakCharactersNode extends CoreMethodNode { | ||
|
||
@CreateCast("characters") public RubyNode coerceCharactersToString(RubyNode characters) { | ||
return ToStrNodeGen.create(null, null, characters); | ||
} | ||
|
||
@TruffleBoundary | ||
@Specialization | ||
public DynamicObject setBasicWordBreakCharacters(DynamicObject characters) { | ||
ProcCompleter.setDelimiter(RopeOperations.decodeUTF8(StringOperations.rope(characters))); | ||
|
||
return characters; | ||
} | ||
|
||
} | ||
|
||
@CoreMethod(names = "get_screen_size", onSingleton = true) | ||
public abstract static class GetScreenSizeNode extends CoreMethodArrayArgumentsNode { | ||
|
||
@TruffleBoundary | ||
@Specialization | ||
public DynamicObject getScreenSize() { | ||
final int[] store = { | ||
getContext().getConsoleHolder().getReadline().getTerminal().getHeight(), | ||
getContext().getConsoleHolder().getReadline().getTerminal().getWidth() | ||
}; | ||
|
||
return ArrayHelpers.createArray(getContext(), store, 2); | ||
} | ||
|
||
} | ||
|
||
@CoreMethod(names = "readline", isModuleFunction = true, optional = 2) | ||
@NodeChildren({ | ||
@NodeChild(type = RubyNode.class, value = "prompt"), | ||
@NodeChild(type = RubyNode.class, value = "addToHistory") | ||
}) | ||
public abstract static class ReadlineNode extends CoreMethodNode { | ||
|
||
@Child private TaintNode taintNode; | ||
|
||
public ReadlineNode(RubyContext context, SourceSection sourceSection) { | ||
super(context, sourceSection); | ||
taintNode = TaintNodeGen.create(context, sourceSection, null); | ||
} | ||
|
||
@CreateCast("prompt") public RubyNode coercePromptToJavaString(RubyNode prompt) { | ||
return NameToJavaStringWithDefaultNodeGen.create(null, null, coreStrings().EMPTY_STRING.toString(), prompt); | ||
} | ||
|
||
@CreateCast("addToHistory") public RubyNode coerceToBoolean(RubyNode addToHistory) { | ||
return BooleanCastWithDefaultNodeGen.create(false, addToHistory); | ||
} | ||
|
||
@TruffleBoundary | ||
@Specialization | ||
public Object readline(String prompt, boolean addToHistory) { | ||
getContext().getConsoleHolder().getReadline().setExpandEvents(false); | ||
|
||
DynamicObject line = nil(); | ||
String value = null; | ||
while (true) { | ||
try { | ||
getContext().getConsoleHolder().getReadline().getTerminal().setEchoEnabled(false); | ||
value = getContext().getConsoleHolder().getReadline().readLine(prompt); | ||
break; | ||
} catch (IOException e) { | ||
throw new RaiseException(coreExceptions().ioError(e.getMessage(), this)); | ||
} finally { | ||
getContext().getConsoleHolder().getReadline().getTerminal().setEchoEnabled(true); | ||
} | ||
} | ||
|
||
if (value != null) { | ||
if (addToHistory) { | ||
getContext().getConsoleHolder().getReadline().getHistory().add(value); | ||
} | ||
|
||
// Enebo: This is a little weird and a little broken. We just ask | ||
// for the bytes and hope they match default_external. This will | ||
// work for common cases, but not ones in which the user explicitly | ||
// sets the default_external to something else. The second problem | ||
// is that no al M17n encodings are valid encodings in java.lang.String. | ||
// We clearly need a byte[]-version of JLine since we cannot totally | ||
// behave properly using Java Strings. | ||
line = createString(StringOperations.encodeRope(value, getContext().getEncodingManager().getDefaultExternalEncoding())); | ||
} | ||
|
||
return taintNode.executeTaint(line); | ||
} | ||
|
||
} | ||
|
||
@CoreMethod(names = "point", onSingleton = true) | ||
public abstract static class PointNode extends CoreMethodArrayArgumentsNode { | ||
|
||
@TruffleBoundary | ||
@Specialization | ||
public int point() { | ||
return getContext().getConsoleHolder().getReadline().getCursorBuffer().cursor; | ||
} | ||
|
||
} | ||
|
||
@CoreMethod(names = "insert_text", constructor = true, required = 1) | ||
@NodeChildren({ | ||
@NodeChild(type = RubyNode.class, value = "self"), | ||
@NodeChild(type = RubyNode.class, value = "text") | ||
}) | ||
public abstract static class InsertTextNode extends CoreMethodNode { | ||
|
||
@CreateCast("text") public RubyNode coerceTextToString(RubyNode text) { | ||
return NameToJavaStringNodeGen.create(text); | ||
} | ||
|
||
@TruffleBoundary | ||
@Specialization | ||
public DynamicObject insertText(DynamicObject readline, String text) { | ||
getContext().getConsoleHolder().getReadline().getCursorBuffer().write(text); | ||
|
||
return readline; | ||
} | ||
|
||
} | ||
|
||
@CoreMethod(names = "delete_text", constructor = true) | ||
public abstract static class DeleteTextNode extends CoreMethodArrayArgumentsNode { | ||
|
||
@TruffleBoundary | ||
@Specialization | ||
public DynamicObject deleteText(DynamicObject readline) { | ||
getContext().getConsoleHolder().getReadline().getCursorBuffer().clear(); | ||
|
||
return readline; | ||
} | ||
|
||
} | ||
|
||
@CoreMethod(names = "line_buffer", onSingleton = true) | ||
public abstract static class LineBufferNode extends CoreMethodArrayArgumentsNode { | ||
|
||
@Child private TaintNode taintNode; | ||
|
||
public LineBufferNode(RubyContext context, SourceSection sourceSection) { | ||
super(context, sourceSection); | ||
taintNode = TaintNodeGen.create(context, sourceSection, null); | ||
} | ||
|
||
@TruffleBoundary | ||
@Specialization | ||
public Object lineBuffer() { | ||
final CursorBuffer cb = getContext().getConsoleHolder().getReadline().getCursorBuffer(); | ||
|
||
final DynamicObject ret = createString(StringOperations.encodeRope(cb.toString(), getDefaultInternalEncoding())); | ||
return taintNode.executeTaint(ret); | ||
} | ||
|
||
} | ||
|
||
@CoreMethod(names = "refresh_line", onSingleton = true) | ||
public abstract static class RefreshLineNode extends CoreMethodArrayArgumentsNode { | ||
|
||
@TruffleBoundary | ||
@Specialization | ||
public DynamicObject refreshLine() { | ||
try { | ||
getContext().getConsoleHolder().getReadline().redrawLine(); | ||
} catch (IOException e) { | ||
throw new RaiseException(coreExceptions().ioError(e.getMessage(), this)); | ||
} | ||
|
||
return nil(); | ||
} | ||
|
||
} | ||
|
||
// Taken from org.jruby.ext.readline.Readline.ProcCompleter. | ||
// Complete using a Proc object | ||
public static class ProcCompleter implements Completer { | ||
|
||
private final DynamicObject procCompleter; | ||
|
||
//\t\n\"\\'`@$><=;|&{( | ||
static private String[] delimiters = {" ", "\t", "\n", "\"", "\\", "'", "`", "@", "$", ">", "<", "=", ";", "|", "&", "{", "("}; | ||
|
||
public ProcCompleter(DynamicObject procCompleter) { | ||
this.procCompleter = procCompleter; | ||
} | ||
|
||
@TruffleBoundary | ||
public static String getDelimiter() { | ||
StringBuilder result = new StringBuilder(delimiters.length); | ||
for (String delimiter : delimiters) { | ||
result.append(delimiter); | ||
} | ||
return result.toString(); | ||
} | ||
|
||
@TruffleBoundary | ||
public static void setDelimiter(String delimiter) { | ||
List<String> l = new ArrayList<String>(); | ||
CharBuffer buf = CharBuffer.wrap(delimiter); | ||
while (buf.hasRemaining()) { | ||
l.add(String.valueOf(buf.get())); | ||
} | ||
delimiters = l.toArray(new String[l.size()]); | ||
} | ||
|
||
@TruffleBoundary | ||
private int wordIndexOf(String buffer) { | ||
int index = 0; | ||
for (String c : delimiters) { | ||
index = buffer.lastIndexOf(c); | ||
if (index != -1) return index; | ||
} | ||
return index; | ||
} | ||
|
||
public int complete(String buffer, int cursor, List candidates) { | ||
throw new UnsupportedOperationException("auto-completion via proc not yet supported"); | ||
} | ||
} | ||
|
||
// Taken from org.jruby.ext.readline.Readline.RubyFileNameCompleter. | ||
// Fix FileNameCompletor to work mid-line | ||
public static class RubyFileNameCompleter extends FileNameCompleter { | ||
|
||
@TruffleBoundary | ||
@Override | ||
public int complete(String buffer, int cursor, List candidates) { | ||
buffer = buffer.substring(0, cursor); | ||
int index = buffer.lastIndexOf(" "); | ||
if (index != -1) { | ||
buffer = buffer.substring(index + 1); | ||
} | ||
return index + 1 + super.complete(buffer, cursor, candidates); | ||
} | ||
|
||
} | ||
} |