From 8bc45dab578fe7de6d80059e3c40745e166b7bf0 Mon Sep 17 00:00:00 2001 From: Tomohiro Matsuyama Date: Wed, 10 Mar 2010 04:39:55 +0900 Subject: [PATCH] Rubygems support. --- bin/rsense | 14 ++- build.xml | 2 +- etc/config.rb | 7 ++ etc/rsense.el | 26 ++++- src/org/cx4a/rsense/CodeAssist.java | 45 +++++++- src/org/cx4a/rsense/Main.java | 49 ++++++++- src/org/cx4a/rsense/Options.java | 163 ++++++++++++++++++---------- src/org/cx4a/rsense/Project.java | 30 ++--- 8 files changed, 247 insertions(+), 89 deletions(-) create mode 100755 etc/config.rb diff --git a/bin/rsense b/bin/rsense index d122ae9..aebe55f 100755 --- a/bin/rsense +++ b/bin/rsense @@ -9,6 +9,7 @@ $classpath = ENV['RSENSE_CLASSPATH'] $pid_file = ENV['RSENSE_PID_FILE'] $log = ENV['RSENSE_LOG'] $debug = ENV['RSENSE_DEBUG'] +$config = ENV['RSENSE_CONFIG'] $end_mark = 'END' def process_args @@ -35,6 +36,8 @@ def process_args $log = value when 'debug' $debug = true + when 'config' + $config = value end else break @@ -61,7 +64,11 @@ def process_args end end - $classpath = '.' + ['', 'rsense.jar', 'antlr-runtime-3.2.jar', 'jruby.jar'].join("#{File::PATH_SEPARATOR}#{$rsense_home}/lib/") unless $classpath + sep = File::SEPARATOR + psep = File::PATH_SEPARATOR + $classpath = '.' + ['', 'rsense.jar', 'antlr-runtime-3.2.jar', 'jruby.jar'].join("#{psep}#{$rsense_home}#{sep}lib#{sep}") unless $classpath + + $config = "#{ENV['HOME']}#{sep}.rsense" unless $config ARGV << 'help' if ARGV.empty? end @@ -99,7 +106,7 @@ end def server_process begin - options = "--rsense-home=#{$rsense_home} --no-prompt --end-mark=#{$end_mark} #{'--debug' if $debug} #{'--log=' + $log if $log}" + options = "--home=#{$rsense_home} --no-prompt --end-mark=#{$end_mark} #{'--debug' if $debug} #{'--log=' + $log if $log} --config=#{$config}" io = IO.popen("java -cp #{$classpath} org.cx4a.rsense.Main script #{options}", 'r+') open_server do |serv| begin @@ -113,7 +120,7 @@ def server_process if cmd =~ /^(exit|quit)/ sock.puts('END') sock.close - exit + return end io.puts(cmd) sock.close_read @@ -139,7 +146,6 @@ def server_process end end - def start_server case ARGV[0] when 'server' diff --git a/build.xml b/build.xml index d494942..8af8d13 100644 --- a/build.xml +++ b/build.xml @@ -21,7 +21,7 @@ - diff --git a/etc/config.rb b/etc/config.rb new file mode 100755 index 0000000..db53e11 --- /dev/null +++ b/etc/config.rb @@ -0,0 +1,7 @@ +#!/usr/bin/env ruby + +require 'rubygems' + +puts "home = #{File.expand_path(File.dirname(File.dirname($0)))}" +puts "load-path = #{$:.join(File::PATH_SEPARATOR)}" +puts "gem-path = #{Gem.path.join(File::PATH_SEPARATOR)}" diff --git a/etc/rsense.el b/etc/rsense.el index 8dca21d..a154fc2 100644 --- a/etc/rsense.el +++ b/etc/rsense.el @@ -89,17 +89,23 @@ Nil means proper socket will be selected.") "--") args))) +(defun rsense-command-1 (command no-output) + (apply 'call-process + (rsense-interpreter) + nil (not no-output) nil + (cons (rsense-program) + (apply 'rsense-args + (append command '("--format=emacs")))))) + (defun rsense-command (&rest command) (car-safe (read-from-string (with-output-to-string (with-current-buffer standard-output - (apply 'call-process - (rsense-interpreter) - nil t nil - (cons (rsense-program) - (apply 'rsense-args - (append command '("--format=emacs")))))))))) + (rsense-command-1 command nil)))))) + +(defun rsense-command-no-output (&rest command) + (rsense-command-1 command t)) (defun rsense-buffer-command (buffer offset command &optional remove-until prefix) (unless rsense-temp-file @@ -136,6 +142,14 @@ Nil means proper socket will be selected.") (interactive) (message "%s" (rsense-command "version"))) +(defun rsense-version () + (interactive) + (message "%s" (rsense-command "version"))) + +(defun rsense-clear () + (interactive) + (rsense-command-no-output "clear")) + (defun rsense-type-help () (interactive) (let ((result (assoc-default 'type (rsense-type-inference (current-buffer) (point))))) diff --git a/src/org/cx4a/rsense/CodeAssist.java b/src/org/cx4a/rsense/CodeAssist.java index 1263d55..720f04b 100644 --- a/src/org/cx4a/rsense/CodeAssist.java +++ b/src/org/cx4a/rsense/CodeAssist.java @@ -225,6 +225,7 @@ private Project newProjectFromConfig(File config, Options options) { File path = config.getParentFile(); Project project = new Project(path.getName(), path); project.setLoadPath(options.getLoadPath()); + project.setGemPath(options.getGemPath()); return project; } @@ -233,10 +234,10 @@ public LoadResult load(Project project, File file, String encoding) { } private LoadResult load(Project project, File file, String encoding, boolean prepare) { - if (project.isLoaded(file)) { + if (project.isLoaded(file.getPath())) { return LoadResult.alreadyLoaded(); } - project.setLoaded(file); + project.setLoaded(file.getPath()); try { InputStream in = new FileInputStream(file); @@ -275,8 +276,18 @@ public LoadResult require(Project project, String feature, String encoding) { } private LoadResult require(Project project, String feature, String encoding, int loadPathLevel) { + if (project.isLoaded(feature)) { + return LoadResult.alreadyLoaded(); + } + project.setLoaded(feature); + + if (File.pathSeparator.equals(";")) { // Windows? + feature = feature.replace('/', '\\'); + } + List loadPath = project.getLoadPath(); - for (int i = loadPathLevel; i < loadPath.size(); i++) { + int loadPathLen = loadPath.size(); + for (int i = loadPathLevel; i < loadPathLen; i++) { String pathElement = loadPath.get(i); int oldLoadPathLevel = context.loadPathLevel; context.loadPathLevel = i; @@ -292,6 +303,33 @@ private LoadResult require(Project project, String feature, String encoding, int context.loadPathLevel = oldLoadPathLevel; } } + + List gemPath = project.getGemPath(); + int gemPathLen = gemPath.size(); + String sep = File.separator; + for (int i = 0; i < gemPathLen; i++) { + String pathElement = gemPath.get(i); + int oldLoadPathLevel = context.loadPathLevel; + context.loadPathLevel = i + loadPathLen; + String oldFeature = context.feature; + context.feature = feature; + try { + File gemsDir = new File(pathElement, "gems"); + String[] gems = gemsDir.list(); + if (gems != null) { + for (String gem : gems) { + File file = new File(gemsDir + sep + gem + sep + "lib" + sep + feature + ".rb"); + if (file.exists()) { + return load(project, file, encoding, false); + } + } + } + } finally { + context.feature = oldFeature; + context.loadPathLevel = oldLoadPathLevel; + } + } + Logger.warn("cannot require: %s", feature); return LoadResult.failWithNotFound(); } @@ -377,6 +415,7 @@ public void clear() { this.projects = new HashMap(); this.sandbox = new Project("(sandbox)", null); this.sandbox.setLoadPath(options.getLoadPath()); + this.sandbox.setGemPath(options.getGemPath()); } private void prepare(Project project) { diff --git a/src/org/cx4a/rsense/Main.java b/src/org/cx4a/rsense/Main.java index 4bdf4e1..ad03813 100644 --- a/src/org/cx4a/rsense/Main.java +++ b/src/org/cx4a/rsense/Main.java @@ -107,21 +107,26 @@ private void usage() { + " --prompt= - Prompt string in interactive shell mode\n" + " --no-prompt - Do not show prompt\n" + "\n" + + " environment - Print environment.\n" + " help - Print this help.\n" + "\n" + " version - Print version information.\n" + "\n" + "script-command:\n" - + " exit/quit - Exit script.\n" + + " exit\n" + + " quit - Exit script.\n" + "\n" + " clear - Clear current environment.\n" + "\n" + "common-options:\n" - + " --rsense-home= - Specify RSense home directory\n" + + " --home= - Specify RSense home directory\n" + " --debug - Print debug messages\n" + " --log= - Log file to output (default stderr)\n" + " --format= - Output format (plain, emacs)\n" + " --encoding= - Input encoding\n" + + " --load-path= - Load path string (: or ; separated)\n" + + " --gem-path= - Gem path string (: or ; separated)\n" + + " --config= - Config file\n" + "\n" + "test-options:\n" + " --test= - Specify fixture name\n" @@ -151,12 +156,33 @@ private Options parseOptions(String[] args, int offset) { if (arg.startsWith("--")) { String[] lr = arg.substring(2).split("="); if (lr.length >= 1) { - options.put(lr[0], lr.length >= 2 ? lr[1] : ""); + options.addOption(lr[0], lr.length >= 2 ? lr[1] : null); } } else { options.addRestArg(arg); } } + try { + // load config + String config = options.getConfig(); + if (config != null && new File(config).exists()) { + InputStream in = new FileInputStream(config); + try { + BufferedReader reader = new BufferedReader(new InputStreamReader(in)); + String line; + while ((line = reader.readLine()) != null) { + String[] lr = line.split("\\s*=\\s*", 2); + if (lr.length >= 1) { + options.addOption(lr[0], lr.length >= 2 ? lr[1] : null); + } + } + } finally { + in.close(); + } + } + } catch (IOException e) { + throw new RuntimeException(e); + } return options; } @@ -251,6 +277,8 @@ private void command(String command, Options options) { script(options); } else if (command.equals("clear")) { commandClear(options); + } else if (command.equals("environment")) { + commandEnvironment(options); } else if (command.equals("help")) { commandHelp(options); } else if (command.equals("version")) { @@ -401,6 +429,21 @@ private void commandClear(Options options) { codeAssist.clear(); } + private void commandEnvironment(Options options) { + out.println("version: " + versionString()); + out.println("home: " + options.getRsenseHome()); + out.println("debug: " + (options.isDebug() ? "yes" : "no")); + out.println("log: " + (options.getLog() != null ? options.getLog() : "")); + out.println("load-path:"); + for (String path : options.getLoadPath()) { + out.println(" - " + path); + } + out.println("gem-path:"); + for (String path : options.getLoadPath()) { + out.println(" - " + path); + } + } + private void commandHelp(Options options) { if (options.isEmacsFormat()) { out.print("\""); diff --git a/src/org/cx4a/rsense/Options.java b/src/org/cx4a/rsense/Options.java index eb85044..e3a9272 100644 --- a/src/org/cx4a/rsense/Options.java +++ b/src/org/cx4a/rsense/Options.java @@ -2,6 +2,7 @@ import java.io.File; import java.io.Reader; +import java.io.IOException; import java.util.List; import java.util.ArrayList; @@ -15,11 +16,52 @@ import org.cx4a.rsense.util.HereDocReader; -public class Options extends HashMap { +public class Options extends HashMap> { private static final long serialVersionUID = 0L; private List rest = new ArrayList(); + public Options() {} + + public void addOption(String name) { + addOption(name, null); + } + + public void addOption(String name, String value) { + List list = get(name); + if (list == null) { + list = new ArrayList(); + put(name, list); + } + if (value != null) { + list.add(value); + } + } + + public void addOptions(String name, List value) { + List list = get(name); + if (list == null) { + list = new ArrayList(); + put(name, list); + } + if (value != null) { + list.addAll(value); + } + } + + public boolean hasOption(String name) { + return containsKey(name); + } + + public String getOption(String name) { + List list = get(name); + return list != null && !list.isEmpty() ? list.get(0) : null; + } + + public List getOptions(String name) { + return get(name); + } + public void addRestArg(String arg) { rest.add(arg); } @@ -29,22 +71,18 @@ public List getRestArgs() { } public boolean isFormatGiven() { - return containsKey("format"); + return hasOption("format"); } public boolean isEncodingGiven() { - return containsKey("encoding"); + return hasOption("encoding"); } public String getFormat() { - String format = get("format"); + String format = getOption("format"); return format != null ? format : defaultFormat(); } - public void setFormat(String format) { - put("format", format); - } - public boolean isPlainFormat() { return "plain".equals(getFormat()); } @@ -54,25 +92,21 @@ public boolean isEmacsFormat() { } public String getEncoding() { - String encoding = get("encoding"); + String encoding = getOption("encoding"); return encoding != null ? encoding : defaultEncoding(); } - public void setEncoding(String encoding) { - put("encoding", encoding); - } - public String getPrompt() { - return containsKey("no-prompt") ? "" : get("prompt"); + return hasOption("no-prompt") ? "" : getOption("prompt"); } public File getFile() { - String file = get("file"); + String file = getOption("file"); return file == null ? null : new File(file); } public boolean isFileStdin() { - return !containsKey("file") || "-".equals(get("file")); + return !hasOption("file") || "-".equals(getOption("file")); } public HereDocReader getHereDocReader(Reader reader) { @@ -80,7 +114,7 @@ public HereDocReader getHereDocReader(Reader reader) { } public CodeAssist.Location getLocation() { - String location = get("location"); + String location = getOption("location"); if (location == null) { return CodeAssist.Location.markLocation("_|_"); } @@ -97,68 +131,59 @@ public CodeAssist.Location getLocation() { } public String getEndMark() { - return get("end-mark"); + return getOption("end-mark"); } public boolean isDebug() { - return containsKey("debug"); + return hasOption("debug"); } public String getLog() { - return get("log"); + return getOption("log"); } public String getRsenseHome() { - String rsenseHome = get("rsense-home"); + String rsenseHome = getOption("home"); return rsenseHome != null ? rsenseHome : "."; } - public String getLoadPath() { + public List getLoadPath() { + List loadPath = getPathList("load-path"); String sep = File.separator; String psep = File.pathSeparator; - String loadPath = get("load-path"); - if (loadPath == null) { - if (psep.equals(";")) { - // Windows maybe (ActiveRuby) - loadPath = "C:\\Program Files\\ruby-1.8\\lib\\ruby\\1.8"; - } else { - // Unix maybe - loadPath = "/usr/lib/ruby/1.8"; - } - } // add stub path - loadPath = getRsenseHome() + sep + "stubs" + sep + "1.8" + psep + loadPath; + loadPath.add(getRsenseHome() + sep + "stubs" + sep + "1.8"); return loadPath; } + public List getGemPath() { + return getPathList("gem-path"); + } + public String getProject() { - return get("project"); + return getOption("project"); } public boolean isDetectProject() { - return containsKey("detect-project"); + return hasOption("detect-project"); } public boolean isKeepEnv() { - return containsKey("keep-env"); + return hasOption("keep-env"); } public boolean isTest() { - return containsKey("test"); + return hasOption("test"); } public boolean isTestColor() { - return containsKey("test-color"); - } - - public void setTestColor(boolean testColor) { - put("test-color", ""); + return hasOption("test-color"); } public String getTest() { - return get("test"); + return getOption("test"); } public Set getShouldContain() { @@ -170,7 +195,7 @@ public Set getShouldNotContain() { } public Set getShouldBe() { - if (containsKey("should-be-empty")) { + if (hasOption("should-be-empty")) { return Collections.emptySet(); } else { return getStringSet("should-be"); @@ -178,12 +203,35 @@ public Set getShouldBe() { } public boolean isShouldBeGiven() { - return containsKey("should-be") || containsKey("should-be-empty"); + return hasOption("should-be") || hasOption("should-be-empty"); } + public boolean isPrintAST() { + return hasOption("print-ast"); + } + + public void inherit(Options parent) { + addOption("home", parent.getRsenseHome()); + if (parent.isDebug()) { + addOption("debug"); + } + addOption("log", parent.getLog()); + addOptions("load-path", parent.getOptions("load-path")); + addOptions("gem-path", parent.getOptions("gem-path")); + addOption("format", parent.getFormat()); + addOption("encoding", parent.getEncoding()); + if (parent.isTestColor()) { + addOption("test-color"); + } + } + + public String getConfig() { + return getOption("config"); + } + private Set getStringSet(String name) { Set result; - String str = get(name); + String str = getOption(name); if (str == null) { result = Collections.emptySet(); } else { @@ -192,20 +240,19 @@ private Set getStringSet(String name) { return result; } - public boolean isPrintAST() { - return containsKey("print-ast"); - } - - public void inherit(Options parent) { - if (!isFormatGiven()) { - setFormat(parent.getFormat()); - } - if (!isEncodingGiven()) { - setEncoding(parent.getEncoding()); + private List getPathList(String name) { + List list = getOptions(name); + List result = new ArrayList(); + if (list != null) { + for (String paths : list) { + for (String path : paths.split(File.pathSeparator)) { + result.add(path); + } + } } - setTestColor(parent.isTestColor()); + return result; } - + public static String defaultFormat() { return "plain"; } diff --git a/src/org/cx4a/rsense/Project.java b/src/org/cx4a/rsense/Project.java index 45f9f6b..131687a 100644 --- a/src/org/cx4a/rsense/Project.java +++ b/src/org/cx4a/rsense/Project.java @@ -20,7 +20,8 @@ public class Project { private Ruby runtime; private Graph graph; private List loadPath; - private Set loaded; + private List gemPath; + private Set loaded; public Project(String name, File path) { this.name = name; @@ -28,7 +29,8 @@ public Project(String name, File path) { this.runtime = new Ruby(); this.graph = new Graph(runtime); this.loadPath = new ArrayList(); - this.loaded = new HashSet(); + this.gemPath = new ArrayList(); + this.loaded = new HashSet(); } public String getName() { @@ -51,23 +53,23 @@ public List getLoadPath() { return loadPath; } - public void setLoadPath(String loadPathStr) { - if (loadPathStr != null) { - for (String pathElement : loadPathStr.split(File.pathSeparator)) { - addLoadPath(pathElement); - } - } + public void setLoadPath(List loadPath) { + this.loadPath.addAll(loadPath); } - public void addLoadPath(String pathElement) { - this.loadPath.add(pathElement); + public List getGemPath() { + return gemPath; } - public boolean isLoaded(File file) { - return loaded.contains(file); + public void setGemPath(List gemPath) { + this.gemPath.addAll(gemPath); } - public void setLoaded(File file) { - loaded.add(file); + public boolean isLoaded(String name) { + return loaded.contains(name); + } + + public void setLoaded(String name) { + loaded.add(name); } }