1 29 package org.jruby.ext; 30 31 import java.io.IOException ; 32 import java.util.List ; 33 import java.util.Iterator ; 34 import java.util.Collections ; 35 36 import org.jruby.Ruby; 37 import org.jruby.RubyModule; 38 import org.jruby.RubyArray; 39 import org.jruby.runtime.CallbackFactory; 40 import org.jruby.runtime.ThreadContext; 41 import org.jruby.runtime.load.Library; 42 import org.jruby.runtime.builtin.IRubyObject; 43 44 import jline.ConsoleReader; 45 import jline.Completor; 46 import jline.FileNameCompletor; 47 import jline.CandidateListCompletionHandler; 48 import jline.History; 49 50 54 public class Readline { 55 public static class Service implements Library { 56 public void load(final Ruby runtime) throws IOException { 57 createReadline(runtime); 58 } 59 } 60 61 private static ConsoleReader readline; 62 private static Completor currentCompletor; 63 private static History history; 64 65 public static void createReadline(Ruby runtime) throws IOException { 66 history = new History(); 67 currentCompletor = null; 68 69 RubyModule mReadline = runtime.defineModule("Readline"); 70 CallbackFactory readlinecb = runtime.callbackFactory(Readline.class); 71 mReadline.defineMethod("readline",readlinecb.getFastSingletonMethod("s_readline",IRubyObject.class,IRubyObject.class)); 72 mReadline.module_function(new IRubyObject[]{runtime.newSymbol("readline")}); 73 mReadline.defineMethod("completion_append_character=",readlinecb.getFastSingletonMethod("s_set_completion_append_character",IRubyObject.class)); 74 mReadline.module_function(new IRubyObject[]{runtime.newSymbol("completion_append_character=")}); 75 mReadline.defineMethod("completion_proc=",readlinecb.getFastSingletonMethod("s_set_completion_proc",IRubyObject.class)); 76 mReadline.module_function(new IRubyObject[]{runtime.newSymbol("completion_proc=")}); 77 IRubyObject hist = runtime.getObject().callMethod(runtime.getCurrentContext(), "new"); 78 mReadline.setConstant("HISTORY",hist); 79 hist.defineSingletonMethod("push",readlinecb.getFastOptSingletonMethod("s_push")); 80 hist.defineSingletonMethod("pop",readlinecb.getFastSingletonMethod("s_pop")); 81 hist.defineSingletonMethod("to_a",readlinecb.getFastSingletonMethod("s_hist_to_a")); 82 } 83 84 protected static void initReadline() throws IOException { 86 readline = new ConsoleReader(); 87 readline.setUseHistory(false); 88 readline.setUsePagination(true); 89 readline.setBellEnabled(false); 90 ((CandidateListCompletionHandler) readline.getCompletionHandler()).setAlwaysIncludeNewline(false); 91 if (currentCompletor == null) 92 currentCompletor = new RubyFileNameCompletor(); 93 readline.addCompletor(currentCompletor); 94 history = readline.getHistory(); 95 readline.setHistory(history); 96 } 97 98 public static History getHistory() { 99 return history; 100 } 101 102 public static void setCompletor(Completor completor) { 103 if (readline != null) readline.removeCompletor(currentCompletor); 104 currentCompletor = completor; 105 if (readline != null) readline.addCompletor(currentCompletor); 106 } 107 108 public static Completor getCompletor() { 109 return currentCompletor; 110 } 111 112 public static IRubyObject s_readline(IRubyObject recv, IRubyObject prompt, IRubyObject add_to_hist) throws IOException { 113 if (readline == null) initReadline(); IRubyObject line = recv.getRuntime().getNil(); 115 String v = readline.readLine(prompt.toString()); 116 if(null != v) { 117 if (add_to_hist.isTrue()) 118 readline.getHistory().addToHistory(v); 119 line = recv.getRuntime().newString(v); 120 } 121 return line; 122 } 123 124 public static IRubyObject s_push(IRubyObject recv, IRubyObject[] lines) throws Exception { 125 for (int i = 0; i < lines.length; i++) { 126 history.addToHistory(lines[i].toString()); 127 } 128 return recv.getRuntime().getNil(); 129 } 130 131 public static IRubyObject s_pop(IRubyObject recv) throws Exception { 132 return recv.getRuntime().getNil(); 133 } 134 135 public static IRubyObject s_hist_to_a(IRubyObject recv) throws Exception { 136 RubyArray histList = recv.getRuntime().newArray(); 137 for (Iterator i = history.getHistoryList().iterator(); i.hasNext();) { 138 histList.append(recv.getRuntime().newString((String ) i.next())); 139 } 140 return histList; 141 } 142 143 public static IRubyObject s_set_completion_append_character(IRubyObject recv, IRubyObject achar) throws Exception { 144 return recv.getRuntime().getNil(); 145 } 146 147 public static IRubyObject s_set_completion_proc(IRubyObject recv, IRubyObject proc) throws Exception { 148 if (!proc.respondsTo("call")) 149 throw recv.getRuntime().newArgumentError("argument must respond to call"); 150 setCompletor(new ProcCompletor(proc)); 151 return recv.getRuntime().getNil(); 152 } 153 154 public static class ProcCompletor implements Completor { 156 IRubyObject procCompletor; 157 158 public ProcCompletor(IRubyObject procCompletor) { 159 this.procCompletor = procCompletor; 160 } 161 162 public int complete(String buffer, int cursor, List candidates) { 163 buffer = buffer.substring(0, cursor); 164 int index = buffer.lastIndexOf(" "); 165 if (index != -1) buffer = buffer.substring(index + 1); 166 ThreadContext context = procCompletor.getRuntime().getCurrentContext(); 167 168 IRubyObject comps = procCompletor.callMethod(context, "call", new IRubyObject[] { procCompletor.getRuntime().newString(buffer) }).callMethod(context, "to_a"); 169 if (comps instanceof List ) { 170 for (Iterator i = ((List ) comps).iterator(); i.hasNext();) { 171 Object obj = i.next(); 172 if (obj != null) candidates.add(obj.toString()); 173 } 174 Collections.sort(candidates); 175 } 176 return cursor - buffer.length(); 177 } 178 } 179 180 public static class RubyFileNameCompletor extends FileNameCompletor { 182 public int complete(String buffer, int cursor, List candidates) { 183 buffer = buffer.substring(0, cursor); 184 int index = buffer.lastIndexOf(" "); 185 if (index != -1) buffer = buffer.substring(index + 1); 186 return index + 1 + super.complete(buffer, cursor, candidates); 187 } 188 } 189 190 } | Popular Tags |