rsense-core 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +1 -0
- data/README.md +35 -0
- data/Rakefile +84 -0
- data/TypeAnnotation.tokens +41 -0
- data/build.xml +84 -0
- data/build_lib/antlr-3.2.jar +0 -0
- data/lib/jars/ant-1.7.0.jar +0 -0
- data/lib/jars/ant-launcher-1.7.0.jar +0 -0
- data/lib/jars/antlr-runtime-3.2.jar +0 -0
- data/lib/jars/bsf-2.3.0.jar +0 -0
- data/lib/rsense-core.rb +28 -0
- data/lib/rsense.jar +0 -0
- data/lib/rsense/core.rb +5 -0
- data/lib/rsense/core/version.rb +5 -0
- data/lib/rsense/parser.rb +6 -0
- data/lib/rsense/ruby.rb +19 -0
- data/lib/rsense/typing.rb +13 -0
- data/lib/rsense/typing/annotation.rb +20 -0
- data/lib/rsense/typing/runtime.rb +23 -0
- data/lib/rsense/typing/vertex.rb +15 -0
- data/lib/rsense/util.rb +9 -0
- data/rsense-core.gemspec +30 -0
- data/src/org/cx4a/rsense/CodeAssist.java +744 -0
- data/src/org/cx4a/rsense/CodeAssistError.java +31 -0
- data/src/org/cx4a/rsense/CodeAssistResult.java +42 -0
- data/src/org/cx4a/rsense/CodeCompletionResult.java +65 -0
- data/src/org/cx4a/rsense/FindDefinitionResult.java +24 -0
- data/src/org/cx4a/rsense/LoadResult.java +19 -0
- data/src/org/cx4a/rsense/Main.java +916 -0
- data/src/org/cx4a/rsense/Options.java +353 -0
- data/src/org/cx4a/rsense/Project.java +103 -0
- data/src/org/cx4a/rsense/TypeInferenceResult.java +25 -0
- data/src/org/cx4a/rsense/WhereResult.java +19 -0
- data/src/org/cx4a/rsense/parser/TypeAnnotation.g +221 -0
- data/src/org/cx4a/rsense/parser/TypeAnnotationLexer.java +1759 -0
- data/src/org/cx4a/rsense/parser/TypeAnnotationParser.java +2025 -0
- data/src/org/cx4a/rsense/ruby/Block.java +10 -0
- data/src/org/cx4a/rsense/ruby/Context.java +75 -0
- data/src/org/cx4a/rsense/ruby/DynamicMethod.java +10 -0
- data/src/org/cx4a/rsense/ruby/DynamicScope.java +51 -0
- data/src/org/cx4a/rsense/ruby/Frame.java +95 -0
- data/src/org/cx4a/rsense/ruby/IRubyObject.java +17 -0
- data/src/org/cx4a/rsense/ruby/LocalScope.java +43 -0
- data/src/org/cx4a/rsense/ruby/MetaClass.java +50 -0
- data/src/org/cx4a/rsense/ruby/Ruby.java +242 -0
- data/src/org/cx4a/rsense/ruby/RubyClass.java +146 -0
- data/src/org/cx4a/rsense/ruby/RubyModule.java +255 -0
- data/src/org/cx4a/rsense/ruby/RubyObject.java +94 -0
- data/src/org/cx4a/rsense/ruby/Scope.java +7 -0
- data/src/org/cx4a/rsense/ruby/SpecialObject.java +15 -0
- data/src/org/cx4a/rsense/ruby/Visibility.java +17 -0
- data/src/org/cx4a/rsense/typing/Graph.java +1690 -0
- data/src/org/cx4a/rsense/typing/Propagation.java +73 -0
- data/src/org/cx4a/rsense/typing/Template.java +84 -0
- data/src/org/cx4a/rsense/typing/TemplateAttribute.java +158 -0
- data/src/org/cx4a/rsense/typing/TypeSet.java +48 -0
- data/src/org/cx4a/rsense/typing/annotation/ClassType.java +57 -0
- data/src/org/cx4a/rsense/typing/annotation/MethodType.java +79 -0
- data/src/org/cx4a/rsense/typing/annotation/TypeAnnotation.java +4 -0
- data/src/org/cx4a/rsense/typing/annotation/TypeAny.java +7 -0
- data/src/org/cx4a/rsense/typing/annotation/TypeApplication.java +37 -0
- data/src/org/cx4a/rsense/typing/annotation/TypeConstraint.java +29 -0
- data/src/org/cx4a/rsense/typing/annotation/TypeExpression.java +11 -0
- data/src/org/cx4a/rsense/typing/annotation/TypeIdentity.java +59 -0
- data/src/org/cx4a/rsense/typing/annotation/TypeOptional.java +22 -0
- data/src/org/cx4a/rsense/typing/annotation/TypePragma.java +22 -0
- data/src/org/cx4a/rsense/typing/annotation/TypeSplat.java +22 -0
- data/src/org/cx4a/rsense/typing/annotation/TypeTuple.java +35 -0
- data/src/org/cx4a/rsense/typing/annotation/TypeUnion.java +23 -0
- data/src/org/cx4a/rsense/typing/annotation/TypeVariable.java +44 -0
- data/src/org/cx4a/rsense/typing/runtime/AliasMethod.java +94 -0
- data/src/org/cx4a/rsense/typing/runtime/AnnotationHelper.java +69 -0
- data/src/org/cx4a/rsense/typing/runtime/AnnotationResolver.java +523 -0
- data/src/org/cx4a/rsense/typing/runtime/Array.java +84 -0
- data/src/org/cx4a/rsense/typing/runtime/ClassTag.java +27 -0
- data/src/org/cx4a/rsense/typing/runtime/DefaultMethod.java +115 -0
- data/src/org/cx4a/rsense/typing/runtime/Hash.java +131 -0
- data/src/org/cx4a/rsense/typing/runtime/LoopTag.java +21 -0
- data/src/org/cx4a/rsense/typing/runtime/Method.java +32 -0
- data/src/org/cx4a/rsense/typing/runtime/MonomorphicObject.java +77 -0
- data/src/org/cx4a/rsense/typing/runtime/ObjectAllocator.java +40 -0
- data/src/org/cx4a/rsense/typing/runtime/PolymorphicObject.java +90 -0
- data/src/org/cx4a/rsense/typing/runtime/Proc.java +100 -0
- data/src/org/cx4a/rsense/typing/runtime/RuntimeHelper.java +1339 -0
- data/src/org/cx4a/rsense/typing/runtime/SpecialMethod.java +119 -0
- data/src/org/cx4a/rsense/typing/runtime/TypeVarMap.java +112 -0
- data/src/org/cx4a/rsense/typing/runtime/VertexHolder.java +48 -0
- data/src/org/cx4a/rsense/typing/vertex/CallVertex.java +122 -0
- data/src/org/cx4a/rsense/typing/vertex/MultipleAsgnVertex.java +23 -0
- data/src/org/cx4a/rsense/typing/vertex/PassThroughVertex.java +20 -0
- data/src/org/cx4a/rsense/typing/vertex/SValueVertex.java +24 -0
- data/src/org/cx4a/rsense/typing/vertex/SplatVertex.java +24 -0
- data/src/org/cx4a/rsense/typing/vertex/ToAryVertex.java +24 -0
- data/src/org/cx4a/rsense/typing/vertex/TypeVarVertex.java +22 -0
- data/src/org/cx4a/rsense/typing/vertex/Vertex.java +221 -0
- data/src/org/cx4a/rsense/typing/vertex/YieldVertex.java +70 -0
- data/src/org/cx4a/rsense/util/HereDocReader.java +48 -0
- data/src/org/cx4a/rsense/util/Logger.java +111 -0
- data/src/org/cx4a/rsense/util/NodeUtil.java +198 -0
- data/src/org/cx4a/rsense/util/SourceLocation.java +70 -0
- data/src/org/cx4a/rsense/util/StringUtil.java +63 -0
- data/src/resources/org/cx4a/rsense/rsense.properties +1 -0
- data/stubs/1.8/_builtin.rb +3006 -0
- data/stubs/1.8/bigdecimal.rb +131 -0
- data/stubs/1.8/cgi.rb +257 -0
- data/stubs/1.8/date.rb +147 -0
- data/stubs/1.8/optparse.rb +113 -0
- data/stubs/1.8/rational.rb +47 -0
- data/stubs/1.8/set.rb +94 -0
- data/stubs/1.8/socket.rb +461 -0
- data/stubs/1.8/stringio.rb +129 -0
- data/test/data/a file.rb +1 -0
- data/test/data/benchmark.rb +12 -0
- data/test/data/crlf.rb +5 -0
- data/test/data/test.rb +19 -0
- data/test/script/all.rsense +2 -0
- data/test/script/array_dynamic.rsense +25 -0
- data/test/script/block_nested.rsense +7 -0
- data/test/script/builtin.rsense +785 -0
- data/test/script/class_method_partial_update.rsense +52 -0
- data/test/script/class_partial_update.rsense +17 -0
- data/test/script/find-definition.rsense +72 -0
- data/test/script/method_arg_onearg.rsense +6 -0
- data/test/script/method_arg_optional.rsense +7 -0
- data/test/script/method_partial_update.rsense +14 -0
- data/test/script/method_yield_arrayarg.rsense +8 -0
- data/test/script/method_yield_arrayarg_expand.rsense +8 -0
- data/test/script/method_yield_arrayarg_splat.rsense +17 -0
- data/test/script/misc.rsense +2 -0
- data/test/script/proc_higher_order.rsense +22 -0
- data/test/script/regression.rsense +95 -0
- data/test/script/stdlib.rsense +66 -0
- data/test/script/where.rsense +41 -0
- metadata +315 -0
data/lib/rsense/util.rb
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
module Rsense
|
|
2
|
+
module Util
|
|
3
|
+
import Java::org.cx4a.rsense.util::HereDocReader
|
|
4
|
+
import Java::org.cx4a.rsense.util::Logger
|
|
5
|
+
import Java::org.cx4a.rsense.util::NodeUtil
|
|
6
|
+
import Java::org.cx4a.rsense.util::SourceLocation
|
|
7
|
+
import Java::org.cx4a.rsense.util::StringUtil
|
|
8
|
+
end
|
|
9
|
+
end
|
data/rsense-core.gemspec
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'rsense/core/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = "rsense-core"
|
|
8
|
+
spec.version = Rsense::Core::VERSION
|
|
9
|
+
spec.authors = ["Eric West", "Tomohiro Matsuyama"]
|
|
10
|
+
spec.email = ["esw9999@gmail.com", "tomo@cx4a.org"]
|
|
11
|
+
spec.summary = %q{RSense knows your code.}
|
|
12
|
+
spec.description = %q{RSense is a tool for doing static analysis of Ruby source code. Rsense-core contains the "library" portion of rsense, as well as the stubs and other artefacts it needs to run.}
|
|
13
|
+
spec.homepage = ""
|
|
14
|
+
spec.license = "GPL"
|
|
15
|
+
|
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
19
|
+
spec.require_paths = ["lib"]
|
|
20
|
+
|
|
21
|
+
spec.add_dependency "spoon", "~> 0.0.4"
|
|
22
|
+
spec.add_dependency "jruby-jars", "~> 1.7.4"
|
|
23
|
+
spec.add_dependency "jruby-parser", "~> 0.5.2"
|
|
24
|
+
spec.add_dependency "thor", "~> 0.18.1"
|
|
25
|
+
|
|
26
|
+
spec.add_development_dependency 'guard'
|
|
27
|
+
spec.add_development_dependency 'guard-minitest'
|
|
28
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
|
29
|
+
spec.add_development_dependency "rake"
|
|
30
|
+
end
|
|
@@ -0,0 +1,744 @@
|
|
|
1
|
+
package org.cx4a.rsense;
|
|
2
|
+
|
|
3
|
+
import java.io.File;
|
|
4
|
+
import java.io.FileInputStream;
|
|
5
|
+
import java.io.IOException;
|
|
6
|
+
import java.io.InputStream;
|
|
7
|
+
import java.io.InputStreamReader;
|
|
8
|
+
import java.io.LineNumberReader;
|
|
9
|
+
import java.io.Reader;
|
|
10
|
+
import java.io.StringReader;
|
|
11
|
+
import java.util.ArrayList;
|
|
12
|
+
import java.util.Collection;
|
|
13
|
+
import java.util.HashMap;
|
|
14
|
+
import java.util.HashSet;
|
|
15
|
+
import java.util.Iterator;
|
|
16
|
+
import java.util.List;
|
|
17
|
+
import java.util.Map;
|
|
18
|
+
import java.util.Set;
|
|
19
|
+
import java.util.regex.Matcher;
|
|
20
|
+
import java.util.regex.Pattern;
|
|
21
|
+
|
|
22
|
+
import org.jrubyparser.ast.INameNode;
|
|
23
|
+
import org.jrubyparser.ast.Node;
|
|
24
|
+
import org.jrubyparser.ast.NodeType;
|
|
25
|
+
import org.jrubyparser.parser.ParserConfiguration;
|
|
26
|
+
import org.jrubyparser.CompatVersion;
|
|
27
|
+
|
|
28
|
+
import org.cx4a.rsense.CodeCompletionResult.CompletionCandidate;
|
|
29
|
+
import org.cx4a.rsense.ruby.Block;
|
|
30
|
+
import org.cx4a.rsense.ruby.DynamicMethod;
|
|
31
|
+
import org.cx4a.rsense.ruby.IRubyObject;
|
|
32
|
+
import org.cx4a.rsense.ruby.MetaClass;
|
|
33
|
+
import org.cx4a.rsense.ruby.Ruby;
|
|
34
|
+
import org.cx4a.rsense.ruby.RubyClass;
|
|
35
|
+
import org.cx4a.rsense.ruby.RubyModule;
|
|
36
|
+
import org.cx4a.rsense.typing.Graph;
|
|
37
|
+
import org.cx4a.rsense.typing.TypeSet;
|
|
38
|
+
import org.cx4a.rsense.typing.runtime.Method;
|
|
39
|
+
import org.cx4a.rsense.typing.runtime.SpecialMethod;
|
|
40
|
+
import org.cx4a.rsense.typing.runtime.VertexHolder;
|
|
41
|
+
import org.cx4a.rsense.typing.vertex.CallVertex;
|
|
42
|
+
import org.cx4a.rsense.typing.vertex.Vertex;
|
|
43
|
+
import org.cx4a.rsense.util.Logger;
|
|
44
|
+
import org.cx4a.rsense.util.SourceLocation;
|
|
45
|
+
|
|
46
|
+
public class CodeAssist {
|
|
47
|
+
public static final String TYPE_INFERENCE_METHOD_NAME = "__rsense_type_inference__";
|
|
48
|
+
public static final String FIND_DEFINITION_METHOD_NAME_PREFIX = "__rsense_find_definition__";
|
|
49
|
+
public static final String PROJECT_CONFIG_NAME = ".project.rsense";
|
|
50
|
+
|
|
51
|
+
public static class Location {
|
|
52
|
+
// 3 ways to specify location
|
|
53
|
+
|
|
54
|
+
private int offset;
|
|
55
|
+
private int line, col;
|
|
56
|
+
private char[] mark;
|
|
57
|
+
|
|
58
|
+
private Location(int offset, int line, int col, String mark) {
|
|
59
|
+
this.offset = offset;
|
|
60
|
+
this.line = line;
|
|
61
|
+
this.col = col;
|
|
62
|
+
this.mark = mark != null ? mark.toCharArray() : null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public int getOffset() {
|
|
66
|
+
return offset;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
public int getLine() {
|
|
70
|
+
return line;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
public int getColumn() {
|
|
74
|
+
return col;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
public char[] getMark() {
|
|
78
|
+
return mark;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
public int findOffset(int offset, int line, char[] buf, int length) {
|
|
82
|
+
if (this.offset != -1) {
|
|
83
|
+
return this.offset;
|
|
84
|
+
} else if (this.line != -1) {
|
|
85
|
+
int col = 0;
|
|
86
|
+
int count = 0;
|
|
87
|
+
for (int i = 0; i < length; i++) {
|
|
88
|
+
char c = buf[i];
|
|
89
|
+
if (Character.isHighSurrogate(c) || c == '\r') {
|
|
90
|
+
} else if (c == '\n') {
|
|
91
|
+
line++;
|
|
92
|
+
col = 0;
|
|
93
|
+
count++;
|
|
94
|
+
} else {
|
|
95
|
+
col++;
|
|
96
|
+
count++;
|
|
97
|
+
if (line == this.line && col == this.col) {
|
|
98
|
+
return offset + count;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return -1;
|
|
103
|
+
} else if (this.mark != null) {
|
|
104
|
+
int count = 0;
|
|
105
|
+
for (int i = 0; i <= length - mark.length; i++) {
|
|
106
|
+
int j = 0;
|
|
107
|
+
if (Character.isHighSurrogate(buf[i]) || buf[i] == '\r') {
|
|
108
|
+
} else {
|
|
109
|
+
count++;
|
|
110
|
+
}
|
|
111
|
+
for (; j < mark.length && buf[i + j] == mark[j]; j++);
|
|
112
|
+
if (j == mark.length) {
|
|
113
|
+
return offset + count - 1;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return -1;
|
|
117
|
+
}
|
|
118
|
+
return -1;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
public int getSkip() {
|
|
122
|
+
return mark != null ? mark.length : 0;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
public static Location offsetLocation(int offset) {
|
|
126
|
+
return new Location(offset, -1, -1, null);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
public static Location logicalLocation(int line, int col) {
|
|
130
|
+
return new Location(-1, line, col, null);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
public static Location markLocation(String mark) {
|
|
134
|
+
return new Location(-1, -1, -1, mark);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
private static class Context {
|
|
139
|
+
public Project project;
|
|
140
|
+
public TypeSet typeSet;
|
|
141
|
+
public boolean main;
|
|
142
|
+
public String feature;
|
|
143
|
+
public int loadPathLevel;
|
|
144
|
+
|
|
145
|
+
public void clear() {
|
|
146
|
+
this.project = null;
|
|
147
|
+
this.typeSet = null;
|
|
148
|
+
this.main = false;
|
|
149
|
+
this.feature = null;
|
|
150
|
+
this.loadPathLevel = 0;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private class WhereEventListener implements Project.EventListener {
|
|
155
|
+
private int line;
|
|
156
|
+
private int closest;
|
|
157
|
+
private String name;
|
|
158
|
+
|
|
159
|
+
public void prepare(int line) {
|
|
160
|
+
this.line = line;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
public String getName() {
|
|
164
|
+
return name;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
public void update(Event event) {
|
|
168
|
+
if (context.main
|
|
169
|
+
&& (event.type == EventType.DEFINE
|
|
170
|
+
|| event.type == EventType.CLASS
|
|
171
|
+
|| event.type == EventType.MODULE)
|
|
172
|
+
&& event.name != null
|
|
173
|
+
&& event.node != null) {
|
|
174
|
+
SourceLocation loc = SourceLocation.of(event.node);
|
|
175
|
+
if (loc != null
|
|
176
|
+
&& line >= loc.getLine()
|
|
177
|
+
&& line - closest > line - loc.getLine()) {
|
|
178
|
+
closest = loc.getLine();
|
|
179
|
+
name = event.name;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
private class FindDefinitionEventListener implements Project.EventListener {
|
|
186
|
+
private int counter = 0;
|
|
187
|
+
private String prefix;
|
|
188
|
+
private Set<SourceLocation> locations = new HashSet<SourceLocation>();
|
|
189
|
+
|
|
190
|
+
public String setup() {
|
|
191
|
+
locations.clear();
|
|
192
|
+
|
|
193
|
+
// Make unique prefix to distinguish older find-definition command results.
|
|
194
|
+
return prefix = FIND_DEFINITION_METHOD_NAME_PREFIX + counter++;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
public Collection<SourceLocation> getLocations() {
|
|
198
|
+
return locations;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
public void update(Event event) {
|
|
202
|
+
CallVertex vertex;
|
|
203
|
+
if (context.main
|
|
204
|
+
&& event.type == EventType.METHOD_MISSING
|
|
205
|
+
&& (vertex = (CallVertex) event.vertex) != null
|
|
206
|
+
&& vertex.getName().startsWith(prefix)
|
|
207
|
+
&& vertex.getReceiverVertex() != null) {
|
|
208
|
+
String realName = vertex.getName().substring(prefix.length());
|
|
209
|
+
for (IRubyObject receiver : vertex.getReceiverVertex().getTypeSet()) {
|
|
210
|
+
RubyClass receiverType = receiver.getMetaClass();
|
|
211
|
+
if (receiverType != null) {
|
|
212
|
+
SourceLocation location = null;
|
|
213
|
+
|
|
214
|
+
// Try to find method
|
|
215
|
+
// TODO callSuper
|
|
216
|
+
Method method = (Method) receiverType.searchMethod(realName);
|
|
217
|
+
if (method != null) {
|
|
218
|
+
if (method.getLocation() != null)
|
|
219
|
+
locations.add(method.getLocation());
|
|
220
|
+
} else {
|
|
221
|
+
// Try to find constant
|
|
222
|
+
RubyModule klass = null;
|
|
223
|
+
if (receiverType instanceof MetaClass) {
|
|
224
|
+
MetaClass metaClass = (MetaClass) receiverType;
|
|
225
|
+
if (metaClass.getAttached() instanceof RubyModule)
|
|
226
|
+
klass = (RubyModule) metaClass.getAttached();
|
|
227
|
+
} else
|
|
228
|
+
klass = context.project.getGraph().getRuntime().getContext().getCurrentScope().getModule();
|
|
229
|
+
if (klass != null) {
|
|
230
|
+
IRubyObject constant = klass.getConstant(realName);
|
|
231
|
+
if (constant instanceof VertexHolder)
|
|
232
|
+
location = SourceLocation.of(((VertexHolder) constant).getVertex().getNode());
|
|
233
|
+
else if (constant instanceof RubyModule)
|
|
234
|
+
location = ((RubyModule) constant).getLocation();
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (location != null)
|
|
239
|
+
locations.add(location);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
vertex.cutout();
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
private org.jrubyparser.Parser rubyParser;
|
|
248
|
+
private final Options options;
|
|
249
|
+
private final Context context;
|
|
250
|
+
private Map<String, Project> projects;
|
|
251
|
+
private Project sandbox;
|
|
252
|
+
private FindDefinitionEventListener definitionFinder;
|
|
253
|
+
private WhereEventListener whereListener;
|
|
254
|
+
|
|
255
|
+
private SpecialMethod typeInferenceMethod = new SpecialMethod() {
|
|
256
|
+
public void call(Ruby runtime, TypeSet receivers, Vertex[] args, Block blcck, Result result) {
|
|
257
|
+
for (IRubyObject receiver : receivers) {
|
|
258
|
+
context.typeSet.add(receiver);
|
|
259
|
+
}
|
|
260
|
+
result.setResultTypeSet(receivers);
|
|
261
|
+
result.setNeverCallAgain(true); // cutout vertex
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
private SpecialMethod requireMethod = new SpecialMethod() {
|
|
266
|
+
public void call(Ruby runtime, TypeSet receivers, Vertex[] args, Block blcck, Result result) {
|
|
267
|
+
if (args != null && args.length > 0) {
|
|
268
|
+
String feature = Vertex.getString(args[0]);
|
|
269
|
+
if (feature != null) {
|
|
270
|
+
require(context.project, feature, "UTF-8");
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
private SpecialMethod requireNextMethod = new SpecialMethod() {
|
|
277
|
+
public void call(Ruby runtime, TypeSet receivers, Vertex[] args, Block blcck, Result result) {
|
|
278
|
+
if (context.feature != null) {
|
|
279
|
+
require(context.project, context.feature, "UTF-8", context.loadPathLevel + 1);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
public CodeAssist(Options options) {
|
|
285
|
+
this.context = new Context();
|
|
286
|
+
this.options = options;
|
|
287
|
+
clear();
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
public void openProject(String path, Options options) {
|
|
291
|
+
File dir = new File(path);
|
|
292
|
+
if (dir.isDirectory()) {
|
|
293
|
+
openProject(newProject(dir, options));
|
|
294
|
+
} else {
|
|
295
|
+
Logger.message("failed to open project: %s", path);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
public void openProject(Project project) {
|
|
300
|
+
projects.put(project.getName(), project);
|
|
301
|
+
Logger.message("project opened: %s", project.getName());
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
public void closeProject(String name) {
|
|
305
|
+
Project project = projects.remove(name);
|
|
306
|
+
if (project != null) {
|
|
307
|
+
Logger.message("project closed: %s", project.getName());
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
public Map<String, Project> getProjects() {
|
|
312
|
+
return projects;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
public Project getProject(Options options) {
|
|
316
|
+
Project project = projects.get(options.getProject());
|
|
317
|
+
if (project == null && options.isDetectProject()) {
|
|
318
|
+
File file = options.getDetectProject();
|
|
319
|
+
if (file == null && !options.isFileStdin()) {
|
|
320
|
+
file = options.getFile();
|
|
321
|
+
}
|
|
322
|
+
if (file != null) {
|
|
323
|
+
File parent = file;
|
|
324
|
+
while ((parent = parent.getParentFile()) != null) {
|
|
325
|
+
for (String[] list : new String[][] {new String[] {PROJECT_CONFIG_NAME},
|
|
326
|
+
new String[] {"Rakefile.rb"},
|
|
327
|
+
new String[] {"Rakefile"},
|
|
328
|
+
new String[] {"setup.rb"},
|
|
329
|
+
new String[] {"Makefile", "lib"}}) {
|
|
330
|
+
boolean found = true;
|
|
331
|
+
for (String name : list) {
|
|
332
|
+
if (!new File(parent, name).exists()) {
|
|
333
|
+
found = false;
|
|
334
|
+
break;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
if (found) {
|
|
338
|
+
project = getProjectByPath(parent);
|
|
339
|
+
if (project == null) {
|
|
340
|
+
project = newProject(parent, options);
|
|
341
|
+
}
|
|
342
|
+
return project;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return project != null ? project : sandbox;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
public Project getProjectByPath(File path) {
|
|
353
|
+
for (Project project : projects.values()) {
|
|
354
|
+
if (project.getPath().equals(path)) {
|
|
355
|
+
return project;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
private Project newProject(File path, Options options) {
|
|
362
|
+
File config = new File(path, PROJECT_CONFIG_NAME);
|
|
363
|
+
if (config.isFile()) {
|
|
364
|
+
options.loadConfig(config);
|
|
365
|
+
} else {
|
|
366
|
+
// Guess config
|
|
367
|
+
options.addOption("load-path", "lib");
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
String name = options.getName();
|
|
371
|
+
if (name == null) {
|
|
372
|
+
name = path.getName();
|
|
373
|
+
}
|
|
374
|
+
Project project = new Project(name, path);
|
|
375
|
+
project.setLoadPath(options.getLoadPath());
|
|
376
|
+
project.setGemPath(options.getGemPath());
|
|
377
|
+
openProject(project);
|
|
378
|
+
|
|
379
|
+
return project;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
public LoadResult load(Project project, File file, String encoding) {
|
|
383
|
+
return load(project, file, encoding, true);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
private LoadResult load(Project project, File file, String encoding, boolean prepare) {
|
|
387
|
+
if (project.isLoaded(file.getPath())) {
|
|
388
|
+
return LoadResult.alreadyLoaded();
|
|
389
|
+
}
|
|
390
|
+
project.setLoaded(file.getPath());
|
|
391
|
+
|
|
392
|
+
try {
|
|
393
|
+
InputStream in = new FileInputStream(file);
|
|
394
|
+
try {
|
|
395
|
+
return load(project, file, new InputStreamReader(in, encoding), prepare);
|
|
396
|
+
} finally {
|
|
397
|
+
in.close();
|
|
398
|
+
}
|
|
399
|
+
} catch (IOException e) {
|
|
400
|
+
return LoadResult.failWithException("Cannot open file", e);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
public LoadResult load(Project project, File file, Reader reader) {
|
|
405
|
+
return load(project, file, reader, true);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
private LoadResult load(Project project, File file, Reader reader, boolean prepare) {
|
|
409
|
+
boolean oldMain = context.main;
|
|
410
|
+
try {
|
|
411
|
+
if (prepare)
|
|
412
|
+
prepare(project);
|
|
413
|
+
else
|
|
414
|
+
context.main = false;
|
|
415
|
+
Node ast = parseFileContents(file, readAll(reader));
|
|
416
|
+
project.getGraph().load(ast);
|
|
417
|
+
|
|
418
|
+
LoadResult result = new LoadResult();
|
|
419
|
+
result.setAST(ast);
|
|
420
|
+
return result;
|
|
421
|
+
} catch (IOException e) {
|
|
422
|
+
return LoadResult.failWithException("Cannot load file", e);
|
|
423
|
+
} finally {
|
|
424
|
+
context.main = oldMain;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
public LoadResult require(Project project, String feature, String encoding) {
|
|
429
|
+
return require(project, feature, encoding, 0);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
private LoadResult require(Project project, String feature, String encoding, int loadPathLevel) {
|
|
433
|
+
if (project.isLoaded(feature)) {
|
|
434
|
+
return LoadResult.alreadyLoaded();
|
|
435
|
+
}
|
|
436
|
+
project.setLoaded(feature);
|
|
437
|
+
Logger.info("feature required: %s", feature);
|
|
438
|
+
|
|
439
|
+
if (File.pathSeparator.equals(";")) { // Windows?
|
|
440
|
+
feature = feature.replace('/', '\\');
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
List<File> loadPath = project.getLoadPath();
|
|
444
|
+
int loadPathLen = loadPath.size();
|
|
445
|
+
for (int i = loadPathLevel; i < loadPathLen; i++) {
|
|
446
|
+
File pathElement = loadPath.get(i);
|
|
447
|
+
int oldLoadPathLevel = context.loadPathLevel;
|
|
448
|
+
context.loadPathLevel = i;
|
|
449
|
+
String oldFeature = context.feature;
|
|
450
|
+
context.feature = feature;
|
|
451
|
+
try {
|
|
452
|
+
File file = new File(pathElement, feature + ".rb");
|
|
453
|
+
if (file.exists()) {
|
|
454
|
+
return load(project, file, encoding, false);
|
|
455
|
+
}
|
|
456
|
+
} finally {
|
|
457
|
+
context.feature = oldFeature;
|
|
458
|
+
context.loadPathLevel = oldLoadPathLevel;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
List<File> gemPath = project.getGemPath();
|
|
463
|
+
int gemPathLen = gemPath.size();
|
|
464
|
+
String sep = File.separator;
|
|
465
|
+
for (int i = 0; i < gemPathLen; i++) {
|
|
466
|
+
File pathElement = gemPath.get(i);
|
|
467
|
+
int oldLoadPathLevel = context.loadPathLevel;
|
|
468
|
+
context.loadPathLevel = i + loadPathLen;
|
|
469
|
+
String oldFeature = context.feature;
|
|
470
|
+
context.feature = feature;
|
|
471
|
+
try {
|
|
472
|
+
File gemsDir = new File(pathElement, "gems");
|
|
473
|
+
String[] gems = gemsDir.list();
|
|
474
|
+
if (gems != null) {
|
|
475
|
+
for (String gem : gems) {
|
|
476
|
+
File file = new File(gemsDir + sep + gem + sep + "lib" + sep + feature + ".rb");
|
|
477
|
+
if (file.exists()) {
|
|
478
|
+
return load(project, file, encoding, false);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
} finally {
|
|
483
|
+
context.feature = oldFeature;
|
|
484
|
+
context.loadPathLevel = oldLoadPathLevel;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
Logger.warn("cannot require: %s", feature);
|
|
489
|
+
return LoadResult.failWithNotFound();
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
public TypeInferenceResult typeInference(Project project, File file, String encoding, Location loc) {
|
|
493
|
+
try {
|
|
494
|
+
InputStream in = new FileInputStream(file);
|
|
495
|
+
try {
|
|
496
|
+
return typeInference(project, file, new InputStreamReader(in, encoding), loc);
|
|
497
|
+
} finally {
|
|
498
|
+
in.close();
|
|
499
|
+
}
|
|
500
|
+
} catch (IOException e) {
|
|
501
|
+
return TypeInferenceResult.failWithException("Cannot open file", e);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
public TypeInferenceResult typeInference(Project project, File file, Reader reader, Location loc) {
|
|
506
|
+
try {
|
|
507
|
+
prepare(project);
|
|
508
|
+
Node ast = parseFileContents(file, readAndInjectCode(reader, loc, TYPE_INFERENCE_METHOD_NAME, "(?:\\.|::)", "."));
|
|
509
|
+
project.getGraph().load(ast);
|
|
510
|
+
|
|
511
|
+
TypeInferenceResult result = new TypeInferenceResult();
|
|
512
|
+
result.setAST(ast);
|
|
513
|
+
TypeSet ts = new TypeSet();
|
|
514
|
+
for (IRubyObject receiver : context.typeSet) {
|
|
515
|
+
ts.add(receiver.getMetaClass());
|
|
516
|
+
}
|
|
517
|
+
result.setTypeSet(ts);
|
|
518
|
+
return result;
|
|
519
|
+
} catch (IOException e) {
|
|
520
|
+
return TypeInferenceResult.failWithException("Cannot read file", e);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
public CodeCompletionResult codeCompletion(Project project, File file, String encoding, Location loc) {
|
|
525
|
+
try {
|
|
526
|
+
InputStream in = new FileInputStream(file);
|
|
527
|
+
try {
|
|
528
|
+
return codeCompletion(project, file, new InputStreamReader(in, encoding), loc);
|
|
529
|
+
} finally {
|
|
530
|
+
in.close();
|
|
531
|
+
}
|
|
532
|
+
} catch (IOException e) {
|
|
533
|
+
return CodeCompletionResult.failWithException("Cannot open file", e);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
public CodeCompletionResult codeCompletion(Project project, File file, Reader reader, Location loc) {
|
|
538
|
+
try {
|
|
539
|
+
prepare(project);
|
|
540
|
+
Node ast = parseFileContents(file, readAndInjectCode(reader, loc, TYPE_INFERENCE_METHOD_NAME, "(?:\\.|::)", "."));
|
|
541
|
+
project.getGraph().load(ast);
|
|
542
|
+
|
|
543
|
+
CodeCompletionResult result = new CodeCompletionResult();
|
|
544
|
+
result.setAST(ast);
|
|
545
|
+
|
|
546
|
+
List<CompletionCandidate> candidates = new ArrayList<CompletionCandidate>();
|
|
547
|
+
for (IRubyObject receiver : context.typeSet) {
|
|
548
|
+
RubyClass rubyClass = receiver.getMetaClass();
|
|
549
|
+
for (String name : rubyClass.getMethods(true)) {
|
|
550
|
+
DynamicMethod method = rubyClass.searchMethod(name);
|
|
551
|
+
candidates.add(new CompletionCandidate(name,
|
|
552
|
+
method.toString(),
|
|
553
|
+
method.getModule().getMethodPath(null),
|
|
554
|
+
CompletionCandidate.Kind.METHOD));
|
|
555
|
+
}
|
|
556
|
+
if (receiver instanceof RubyModule) {
|
|
557
|
+
RubyModule module = ((RubyModule) receiver);
|
|
558
|
+
for (String name : module.getConstants(true)) {
|
|
559
|
+
RubyModule directModule = module.getConstantModule(name);
|
|
560
|
+
IRubyObject constant = directModule.getConstant(name);
|
|
561
|
+
String baseName = directModule.toString();
|
|
562
|
+
String qname = baseName + "::" + name;
|
|
563
|
+
CompletionCandidate.Kind kind
|
|
564
|
+
= (constant instanceof RubyClass)
|
|
565
|
+
? CompletionCandidate.Kind.CLASS
|
|
566
|
+
: (constant instanceof RubyModule)
|
|
567
|
+
? CompletionCandidate.Kind.MODULE
|
|
568
|
+
: CompletionCandidate.Kind.CONSTANT;
|
|
569
|
+
candidates.add(new CompletionCandidate(name, qname, baseName, kind));
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
result.setCandidates(candidates);
|
|
575
|
+
return result;
|
|
576
|
+
} catch (IOException e) {
|
|
577
|
+
return CodeCompletionResult.failWithException("Cannot read file", e);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
public WhereResult where(Project project, File file, String encoding, int line) {
|
|
582
|
+
try {
|
|
583
|
+
InputStream in = new FileInputStream(file);
|
|
584
|
+
try {
|
|
585
|
+
return where(project, file, new InputStreamReader(in, encoding), line);
|
|
586
|
+
} finally {
|
|
587
|
+
in.close();
|
|
588
|
+
}
|
|
589
|
+
} catch (IOException e) {
|
|
590
|
+
return WhereResult.failWithException("Cannot open file", e);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
public WhereResult where(Project project, File file, Reader reader, int line) {
|
|
595
|
+
try {
|
|
596
|
+
prepare(project);
|
|
597
|
+
Node ast = parseFileContents(file, readAll(reader));
|
|
598
|
+
|
|
599
|
+
whereListener.prepare(line);
|
|
600
|
+
project.addEventListener(whereListener);
|
|
601
|
+
try {
|
|
602
|
+
project.getGraph().load(ast);
|
|
603
|
+
} finally {
|
|
604
|
+
project.removeEventListener(whereListener);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
WhereResult result = new WhereResult();
|
|
608
|
+
result.setAST(ast);
|
|
609
|
+
result.setName(whereListener.getName());
|
|
610
|
+
|
|
611
|
+
return result;
|
|
612
|
+
} catch (IOException e) {
|
|
613
|
+
return WhereResult.failWithException("Cannot read file", e);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
public FindDefinitionResult findDefinition(Project project, File file, String encoding, Location loc) {
|
|
618
|
+
try {
|
|
619
|
+
InputStream in = new FileInputStream(file);
|
|
620
|
+
try {
|
|
621
|
+
return findDefinition(project, file, new InputStreamReader(in, encoding), loc);
|
|
622
|
+
} finally {
|
|
623
|
+
in.close();
|
|
624
|
+
}
|
|
625
|
+
} catch (IOException e) {
|
|
626
|
+
return FindDefinitionResult.failWithException("Cannot open file", e);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
public FindDefinitionResult findDefinition(Project project, File file, Reader reader, Location loc) {
|
|
631
|
+
try {
|
|
632
|
+
prepare(project);
|
|
633
|
+
Node ast = parseFileContents(file, readAndInjectCode(reader, loc, definitionFinder.setup(), "(?:\\.|::|\\s)(\\w+?[!?]?)", null));
|
|
634
|
+
|
|
635
|
+
project.addEventListener(definitionFinder);
|
|
636
|
+
try {
|
|
637
|
+
project.getGraph().load(ast);
|
|
638
|
+
} finally {
|
|
639
|
+
project.removeEventListener(definitionFinder);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
FindDefinitionResult result = new FindDefinitionResult();
|
|
643
|
+
result.setAST(ast);
|
|
644
|
+
|
|
645
|
+
result.setLocations(definitionFinder.getLocations());
|
|
646
|
+
|
|
647
|
+
return result;
|
|
648
|
+
} catch (IOException e) {
|
|
649
|
+
return FindDefinitionResult.failWithException("Cannot read file", e);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
public void clear() {
|
|
654
|
+
this.rubyParser = new org.jrubyparser.Parser(); // for parse
|
|
655
|
+
this.context.clear();
|
|
656
|
+
this.projects = new HashMap<String, Project>();
|
|
657
|
+
this.sandbox = new Project("(sandbox)", new File("."));
|
|
658
|
+
this.sandbox.setLoadPath(options.getLoadPath());
|
|
659
|
+
this.sandbox.setGemPath(options.getGemPath());
|
|
660
|
+
this.definitionFinder = new FindDefinitionEventListener();
|
|
661
|
+
this.whereListener = new WhereEventListener();
|
|
662
|
+
openProject(this.sandbox);
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
private void prepare(Project project) {
|
|
666
|
+
context.project = project;
|
|
667
|
+
context.typeSet = new TypeSet();
|
|
668
|
+
context.main = true;
|
|
669
|
+
|
|
670
|
+
Graph graph = project.getGraph();
|
|
671
|
+
graph.addSpecialMethod(TYPE_INFERENCE_METHOD_NAME, typeInferenceMethod);
|
|
672
|
+
graph.addSpecialMethod("require", requireMethod);
|
|
673
|
+
graph.addSpecialMethod("require_next", requireNextMethod);
|
|
674
|
+
|
|
675
|
+
require(project, "_builtin", "UTF-8");
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
private String readAll(Reader reader) throws IOException {
|
|
679
|
+
return readAndInjectCode(reader, null, null, null, null);
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
private String readAndInjectCode(Reader _reader, Location loc, String injection, String prefixPattern, String defaultPrefix) throws IOException {
|
|
683
|
+
LineNumberReader reader = new LineNumberReader(_reader);
|
|
684
|
+
int line = reader.getLineNumber() + 1;
|
|
685
|
+
int offset = -1;
|
|
686
|
+
char[] buf = new char[4096];
|
|
687
|
+
int read;
|
|
688
|
+
int len = 0;
|
|
689
|
+
StringBuilder buffer = new StringBuilder();
|
|
690
|
+
while ((read = reader.read(buf)) != -1) {
|
|
691
|
+
int index = 0;
|
|
692
|
+
if (loc != null) {
|
|
693
|
+
if (offset == -1) {
|
|
694
|
+
offset = loc.findOffset(len, line, buf, read);
|
|
695
|
+
}
|
|
696
|
+
for (int i = 0; i < read; i++) {
|
|
697
|
+
if (Character.isHighSurrogate(buf[i]) || buf[i] == '\r') {
|
|
698
|
+
} else {
|
|
699
|
+
len++;
|
|
700
|
+
if (len == offset) {
|
|
701
|
+
index = i + 1;
|
|
702
|
+
|
|
703
|
+
int pstart = Math.max(0, index - 128);
|
|
704
|
+
String pbuf = new String(buf, pstart, index - pstart);
|
|
705
|
+
Matcher matcher = Pattern.compile(".*" + prefixPattern, Pattern.DOTALL).matcher(pbuf);
|
|
706
|
+
boolean match = matcher.matches();
|
|
707
|
+
if (match) {
|
|
708
|
+
if (matcher.groupCount() > 0) {
|
|
709
|
+
int end = index - (pbuf.length() - matcher.start(1));
|
|
710
|
+
buffer.append(buf, 0, end);
|
|
711
|
+
buffer.append(injection);
|
|
712
|
+
buffer.append(buf, end, index - end);
|
|
713
|
+
} else {
|
|
714
|
+
buffer.append(buf, 0, index);
|
|
715
|
+
buffer.append(injection);
|
|
716
|
+
}
|
|
717
|
+
} else {
|
|
718
|
+
buffer.append(buf, 0, index);
|
|
719
|
+
if (defaultPrefix != null)
|
|
720
|
+
buffer.append(defaultPrefix);
|
|
721
|
+
buffer.append(injection);
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
index += loc.getSkip();
|
|
725
|
+
break;
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
buffer.append(buf, index, read - index);
|
|
731
|
+
}
|
|
732
|
+
return buffer.toString();
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
public Node parseFileContents(File file, String string) {
|
|
736
|
+
StringReader in = new StringReader(string);
|
|
737
|
+
CompatVersion version = CompatVersion.RUBY1_8;
|
|
738
|
+
//CompatVersion versionTwo = CompatVersion.RUBY1_9;
|
|
739
|
+
CompatVersion versionTwo = CompatVersion.RUBY2_0;
|
|
740
|
+
ParserConfiguration config = new ParserConfiguration(0, versionTwo);
|
|
741
|
+
config.setSyntax(ParserConfiguration.SyntaxGathering.COMMENTS);
|
|
742
|
+
return rubyParser.parse(file.getPath(), in, config);
|
|
743
|
+
}
|
|
744
|
+
}
|