PageRenderTime 45ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/bundles/plugins-trunk/RubyPlugin/src/org/jedit/ruby/parser/RubyNodeVisitor.java

#
Java | 587 lines | 489 code | 70 blank | 28 comment | 95 complexity | c379726397598f5b78e7790b57bcfe94 MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, Apache-2.0, LGPL-2.0, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, MPL-2.0-no-copyleft-exception, IPL-1.0
  1. /*
  2. * RubyNodeVisitor.java -
  3. *
  4. * Copyright 2005 Robert McKinnon
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License
  8. * as published by the Free Software Foundation; either version 2
  9. * of the License, or any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, write to the Free Software
  18. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  19. */
  20. package org.jedit.ruby.parser;
  21. import org.jruby.ast.visitor.AbstractVisitor;
  22. import org.jruby.ast.*;
  23. import org.jruby.lexer.yacc.SyntaxException;
  24. import org.jruby.lexer.yacc.ISourcePosition;
  25. import org.jruby.evaluator.Instruction;
  26. import org.jedit.ruby.ast.*;
  27. import org.jedit.ruby.RubyPlugin;
  28. import java.util.List;
  29. import java.util.LinkedList;
  30. import java.util.ArrayList;
  31. import java.util.Iterator;
  32. /**
  33. * @author robmckinnon at users.sourceforge.net
  34. */
  35. final class RubyNodeVisitor extends AbstractVisitor {
  36. private static final String CLASS = "class";
  37. private static final String MODULE = "module";
  38. private final List<String> namespaceNames;
  39. private final List<String> compositeNamespaceNames;
  40. private final LinkedList<Member> currentMember;
  41. private int methodIndex;
  42. private final List<Member> methods;
  43. private final List<RubyParser.WarningListener> problemListeners;
  44. private final LineCounter lineCounter;
  45. private final NameVisitor nameVisitor;
  46. private MethodCallWithSelfAsAnImplicitReceiver methodCall;
  47. private boolean inIfNode;
  48. private boolean underModuleNode;
  49. private Root root;
  50. public RubyNodeVisitor(LineCounter lineCounts, List<Member> methodMembers, List<RubyParser.WarningListener> listeners) {
  51. inIfNode = false;
  52. underModuleNode = false;
  53. lineCounter = lineCounts;
  54. namespaceNames = new ArrayList<String>();
  55. compositeNamespaceNames = new ArrayList<String>();
  56. currentMember = new LinkedList<Member>();
  57. root = new Root(RubyPlugin.getEndOfFileOffset());
  58. currentMember.add(root);
  59. nameVisitor = new NameVisitor();
  60. problemListeners = listeners;
  61. methods = methodMembers;
  62. methodCall = null;
  63. methodIndex = 0;
  64. }
  65. public final List<Member> getMembers() {
  66. return currentMember.getFirst().getChildMembersAsList();
  67. }
  68. protected final Instruction visitNode(Node node) {
  69. if (printNode()) {
  70. String name = node.getClass().getName();
  71. int index = name.lastIndexOf('.');
  72. ISourcePosition position = node.getPosition();
  73. if (position != null) {
  74. System.out.println("Line " + position.getStartLine() +"-"+ position.getEndLine() +
  75. ": " + position.getStartOffset() + "-" + position.getEndOffset() +
  76. " " + name.substring(index + 1));
  77. } else {
  78. System.out.print(" Node: " + name.substring(index + 1));
  79. }
  80. }
  81. return null;
  82. }
  83. private static boolean printNode() {
  84. // return !(node instanceof NewlineNode);
  85. return false;
  86. }
  87. private void visitNodeIterator(Iterator iterator) {
  88. while (iterator.hasNext()) {
  89. Node node = (Node) iterator.next();
  90. visitNode(node);
  91. if (printNode()) {
  92. RubyPlugin.log("", getClass());
  93. }
  94. node.accept(this);
  95. }
  96. }
  97. public final Instruction visitBlockNode(BlockNode node) {
  98. visitNode(node);
  99. RubyPlugin.log("", getClass());
  100. visitNodeIterator(node.childNodes().iterator());
  101. return null;
  102. }
  103. public final Instruction visitNewlineNode(NewlineNode node) {
  104. visitNode(node);
  105. node.getNextNode().accept(this);
  106. return null;
  107. }
  108. public final Instruction visitModuleNode(ModuleNode module) {
  109. // System.out.print("[");
  110. addParentNode(MODULE, module, module, module.getBodyNode());
  111. // System.out.print("]");
  112. return null;
  113. }
  114. public Instruction visitSClassNode(SClassNode selfClassNode) {
  115. selfClassNode.getBodyNode().accept(this);
  116. return null;
  117. }
  118. public final Instruction visitClassNode(ClassNode classNode) {
  119. boolean tempUnderModuleNode = underModuleNode;
  120. underModuleNode = false;
  121. Member member = addParentNode(CLASS, classNode, classNode, classNode.getBodyNode());
  122. Node superNode = classNode.getSuperNode();
  123. if (superNode != null) {
  124. superNode.accept(nameVisitor);
  125. StringBuffer name = new StringBuffer();
  126. for (String namespace : nameVisitor.namespaces) {
  127. name.append(namespace).append("::");
  128. }
  129. nameVisitor.namespaces.clear();
  130. name.append(nameVisitor.name);
  131. ((ClassMember)member).setSuperClassName(name.toString());
  132. }
  133. underModuleNode = tempUnderModuleNode;
  134. return null;
  135. }
  136. private Member addParentNode(String memberType, Node node, IScopingNode scopeNode, Node bodyNode) {
  137. visitNode(node);
  138. scopeNode.getCPath().accept(nameVisitor);
  139. String name = nameVisitor.name;
  140. Member member;
  141. if (memberType == MODULE) {
  142. member = new Module(name);
  143. } else {
  144. member = new ClassMember(name);
  145. }
  146. member = populateOffsets(member, scopeNode.getCPath().getPosition(), node.getPosition(), memberType);
  147. int colonNameCount = nameVisitor.namespaces.size();
  148. for (String namespace : nameVisitor.namespaces) {
  149. compositeNamespaceNames.add(namespace);
  150. namespaceNames.add(namespace);
  151. }
  152. nameVisitor.namespaces.clear();
  153. populateNamespace(member);
  154. compositeNamespaceNames.clear();
  155. namespaceNames.add(name);
  156. Member parent = currentMember.getLast();
  157. parent.addChildMember(member);
  158. currentMember.add(member);
  159. if (memberType == MODULE) {
  160. underModuleNode = true;
  161. }
  162. tranverseChildren(bodyNode, node);
  163. if (memberType == MODULE) {
  164. underModuleNode = false;
  165. }
  166. namespaceNames.remove(name);
  167. while (colonNameCount > 0) {
  168. namespaceNames.remove(namespaceNames.size() - 1);
  169. colonNameCount--;
  170. }
  171. currentMember.removeLast();
  172. return member;
  173. }
  174. private void tranverseChildren(Node bodyNode, Node node) {
  175. if (bodyNode == null) {
  176. if (node.childNodes() != null) {
  177. for (Object child : node.childNodes()) {
  178. if (child instanceof ArgumentNode) {
  179. // do nothing
  180. } else if (child instanceof Node) {
  181. Node childNode = (Node) (child);
  182. childNode.accept(this);
  183. }
  184. }
  185. }
  186. } else {
  187. bodyNode.accept(this);
  188. }
  189. }
  190. public Instruction visitArgsCatNode(ArgsCatNode node) {
  191. visitNode(node);
  192. return null;
  193. }
  194. public Instruction visitArgsNode(ArgsNode node) {
  195. visitNode(node);
  196. // node.accept(this);
  197. return null;
  198. }
  199. public final Instruction visitDefnNode(DefnNode node) {
  200. visitNode(node);
  201. Member method;
  202. try {
  203. method = getMember("def", methodIndex, methods, node.getName(), node.getNameNode().getPosition(), node.getPosition());
  204. } catch (IndexAdjustmentException e) {
  205. methodIndex = e.getIndex();
  206. method = getMethodNoCheckedException(methodIndex, methods, node.getName(), node.getPosition());
  207. }
  208. methodIndex++;
  209. Member parent = currentMember.getLast();
  210. parent.addChildMember(method);
  211. return null;
  212. }
  213. private Member getMethodNoCheckedException(int index, List<Member> members, String memberName, ISourcePosition position) {
  214. try {
  215. return getMember("def", index, members, memberName, position, position);
  216. } catch (IndexAdjustmentException e) {
  217. return throwCantFindException("def", memberName, position);
  218. }
  219. }
  220. public final Instruction visitDefsNode(DefsNode node) {
  221. visitNode(node);
  222. Method method = (Method)methods.get(methodIndex++);
  223. populateReceiverName(method, node);
  224. populateOffsets(method, node.getPosition(), node.getPosition(), "def");
  225. currentMember.getLast().addChildMember(method);
  226. currentMember.add(method);
  227. tranverseChildren(node.getBodyNode(), node);
  228. currentMember.removeLast();
  229. return null;
  230. }
  231. private void populateReceiverName(Method method, DefsNode node) {
  232. String methodName = node.getName();
  233. Node receiverNode = node.getReceiverNode();
  234. if (receiverNode instanceof ConstNode) {
  235. ConstNode constNode = (ConstNode)receiverNode;
  236. method.setReceiver(constNode.getName(), methodName);
  237. } else if (receiverNode instanceof SelfNode) {
  238. method.setReceiverToSelf(methodName);
  239. }
  240. RubyPlugin.log(": " + method.getFullName(), getClass());
  241. }
  242. private void populateNamespace(Member member) {
  243. StringBuffer namespace = new StringBuffer();
  244. if (namespaceNames.size() > 0) {
  245. for (String name : namespaceNames) {
  246. namespace.append(name).append("::");
  247. }
  248. member.setNamespace(namespace.toString());
  249. }
  250. if (compositeNamespaceNames.size() > 0) {
  251. namespace = new StringBuffer();
  252. for (String name : compositeNamespaceNames) {
  253. namespace.append(name).append("::");
  254. }
  255. member.setCompositeNamespace(namespace.toString());
  256. }
  257. }
  258. private Member getMember(String memberType, int index, List<Member> members, String memberName, ISourcePosition startPosition, ISourcePosition endPosition) throws IndexAdjustmentException {
  259. Member member;
  260. try {
  261. member = members.get(index);
  262. String shortName = member.getShortName();
  263. if (!memberName.equals(shortName)) {
  264. index++;
  265. while(index < members.size()) {
  266. member = members.get(index);
  267. if (memberName.equals(member.getShortName())) {
  268. throw new IndexAdjustmentException(index);
  269. } else {
  270. index++;
  271. }
  272. }
  273. throw new Exception();
  274. }
  275. } catch (Exception e) {
  276. if (e instanceof IndexAdjustmentException) {
  277. throw (IndexAdjustmentException)e;
  278. } else {
  279. return throwCantFindException(memberType, memberName, endPosition);
  280. }
  281. }
  282. return populateOffsets(member, startPosition, endPosition, memberType);
  283. }
  284. private Member populateOffsets(Member member, ISourcePosition position, ISourcePosition endPosition, String memberType) {
  285. member.setStartOffset(getStartOffset(position, member));
  286. member.setEndOffset(getEndOffset(endPosition));
  287. member.setStartOuterOffset(getOuterOffset(member, memberType+" "));
  288. return member;
  289. }
  290. private Member throwCantFindException(String memberType, String memberName, ISourcePosition position) {
  291. String message = "parser can't find " + memberType + " " + memberName;
  292. for (RubyParser.WarningListener listener : problemListeners) {
  293. listener.error(position, message);
  294. }
  295. throw new SyntaxException(position, message);
  296. }
  297. public Instruction visitRootNode(RootNode node) {
  298. visitNode(node);
  299. RubyPlugin.log("",getClass());
  300. if (node.getBodyNode() != null) {
  301. node.getBodyNode().accept(this);
  302. }
  303. return null;
  304. }
  305. public final Instruction visitIfNode(IfNode node) {
  306. visitNode(node);
  307. if (node.getThenBody() != null) {
  308. inIfNode = true;
  309. node.getThenBody().accept(this);
  310. inIfNode = false;
  311. }
  312. return null;
  313. }
  314. public final Instruction visitIterNode(IterNode node) {
  315. visitNode(node);
  316. RubyPlugin.log("", getClass());
  317. if (node.getBodyNode() != null) {
  318. node.getBodyNode().accept(this);
  319. }
  320. return null;
  321. }
  322. public Instruction visitArrayNode(ArrayNode node) {
  323. if (isMethodCall()) {
  324. for (int i = 0; i < node.size(); i++) {
  325. node.get(i).accept(this);
  326. }
  327. }
  328. return null;
  329. }
  330. private boolean isMethodCall() {
  331. return methodCall != null;
  332. }
  333. public Instruction visitConstNode(ConstNode node) {
  334. if (isMethodCall()) {
  335. methodCall.addArgument(node.getName());
  336. }
  337. return null;
  338. }
  339. public Instruction visitSymbolNode(SymbolNode node) {
  340. if (isMethodCall()) {
  341. methodCall.addArgument(":" + node.getName());
  342. }
  343. return null;
  344. }
  345. public Instruction visitStrNode(StrNode node) {
  346. if (isMethodCall()) {
  347. methodCall.addArgument("'" + node.getValue().toString() + "'");
  348. }
  349. return null;
  350. }
  351. public Instruction visitHashNode(HashNode node) {
  352. if (isMethodCall()) {
  353. node.getListNode().accept(this);
  354. }
  355. return null;
  356. }
  357. public final Instruction visitFCallNode(FCallNode node) {
  358. visitNode(node);
  359. String name = node.getName();
  360. RubyPlugin.log(": " + name, getClass());
  361. Member parent = currentMember.getLast();
  362. if (parent instanceof Root ||
  363. parent instanceof ClassMember ||
  364. parent instanceof Module ||
  365. (isRspecMethodName(parent.getName()) && isRspecMethodName(name)) ||
  366. (isRakeMethodName(parent.getName()) && isRakeMethodName(name)) ) {
  367. MethodCallWithSelfAsAnImplicitReceiver call = new MethodCallWithSelfAsAnImplicitReceiver(name);
  368. call.setStartOuterOffset(getStartOffset(node.getPosition(), call));
  369. call.setStartOffset(call.getStartOuterOffset() + name.length() + 1);
  370. call.setEndOffset(getEndOffset(node.getPosition()));
  371. parent.addChildMember(call);
  372. currentMember.add(call);
  373. methodCall = call;
  374. if (node.getArgsNode() != null) {
  375. node.getArgsNode().accept(this);
  376. }
  377. if (node.getIterNode() != null) {
  378. node.getIterNode().accept(this);
  379. }
  380. methodCall = null;
  381. currentMember.removeLast();
  382. }
  383. return null;
  384. }
  385. private boolean isRakeMethodName(String name) {
  386. // ignore 'desc' as always next to something else
  387. return name.equals("directory") ||
  388. name.equals("file") ||
  389. name.equals("file_create") ||
  390. name.equals("import") ||
  391. name.equals("multitask") ||
  392. name.equals("namespace") ||
  393. name.equals("rule") ||
  394. name.equals("task");
  395. }
  396. private boolean isRspecMethodName(String name) {
  397. return name.equals("describe") ||
  398. name.equals("it") ||
  399. name.equals("before") ||
  400. name.equals("after") ||
  401. name.equals("shared_examples_for") ||
  402. name.equals("it_should_behave_like") ||
  403. name.equals("fixtures") ||
  404. name.equals("context") ||
  405. name.equals("controller_name") ||
  406. name.equals("integrate_views");
  407. }
  408. public final Instruction visitClassVarDeclNode(ClassVarDeclNode node) {
  409. visitNode(node);
  410. RubyPlugin.log(": " + node.getName(), getClass());
  411. return null;
  412. }
  413. public final Instruction visitClassVarAsgnNode(ClassVarAsgnNode node) {
  414. visitNode(node);
  415. RubyPlugin.log(": " + node.getName(), getClass());
  416. return null;
  417. }
  418. private int getStartOffset(ISourcePosition position, Member member) {
  419. if (inIfNode ||
  420. underModuleNode ||
  421. (member instanceof Method && currentMember.getLast() == root) ||
  422. (member instanceof Method && !member.getName().equals(member.getFullName())) ) {
  423. int startOffset = position.getStartOffset();
  424. int index = lineCounter.getLineAtOffset(startOffset);
  425. String line = lineCounter.getLine(index);
  426. int offset = line.indexOf(member.getShortName());
  427. if (offset != -1) {
  428. return offset + lineCounter.getStartOffset(index);
  429. } else {
  430. return position.getStartOffset();
  431. }
  432. } else {
  433. return position.getStartOffset();
  434. }
  435. }
  436. private int getOuterOffset(Member member, String keyword) {
  437. int startOffset = member.getStartOffset();
  438. int index = lineCounter.getLineAtOffset(startOffset);
  439. String line = lineCounter.getLineUpTo(index, startOffset);
  440. return line.lastIndexOf(keyword) + lineCounter.getStartOffset(index);
  441. }
  442. private int getEndOffset(ISourcePosition position) {
  443. int end = position.getEndOffset();
  444. if (lineCounter.charAt(end - 3) == 'e'
  445. && lineCounter.charAt(end - 2) == 'n'
  446. && lineCounter.charAt(end - 1) == 'd') {
  447. return end;
  448. }
  449. if (lineCounter.charAt(end - 4) == 'e'
  450. && lineCounter.charAt(end - 3) == 'n'
  451. && lineCounter.charAt(end - 2) == 'd') {
  452. return end - 1;
  453. }
  454. char endChar = lineCounter.charAt(end);
  455. if (((int)endChar) != 65535) {
  456. switch (endChar) {
  457. case 'd': return end + 1;
  458. case 'n': return end + 2;
  459. case 'e': return end + 3;
  460. }
  461. int endLine = position.getEndLine();
  462. String line = lineCounter.getLine(endLine);
  463. int start = lineCounter.getStartOffset(endLine);
  464. int beginIndex = end - start;
  465. String text = line.substring(beginIndex);
  466. if (text.indexOf("end") != -1) {
  467. return text.indexOf("end") + 3 + beginIndex;
  468. } else {
  469. return position.getEndOffset();
  470. }
  471. } else {
  472. return position.getEndOffset();
  473. }
  474. }
  475. private static final class IndexAdjustmentException extends Exception {
  476. private final int index;
  477. public IndexAdjustmentException(int index) {
  478. this.index = index;
  479. }
  480. public final int getIndex() {
  481. return index;
  482. }
  483. }
  484. private static final class NameVisitor extends AbstractVisitor {
  485. private final List<String> namespaces;
  486. private String name;
  487. private int visits;
  488. public NameVisitor() {
  489. namespaces = new ArrayList<String>();
  490. visits = 0;
  491. }
  492. protected final Instruction visitNode(Node node) {
  493. return null;
  494. }
  495. public final Instruction visitColon2Node(Colon2Node node) {
  496. visits++;
  497. if (node.getLeftNode() != null) {
  498. node.getLeftNode().accept(this);
  499. }
  500. visits--;
  501. if (visits == 0) {
  502. name = node.getName();
  503. } else {
  504. namespaces.add(node.getName());
  505. }
  506. return null;
  507. }
  508. public Instruction visitConstNode(ConstNode node) {
  509. if (visits == 0) {
  510. name = node.getName();
  511. } else {
  512. namespaces.add(node.getName());
  513. }
  514. return null;
  515. }
  516. }
  517. }