PageRenderTime 74ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/bundles/plugins-trunk/RubyPlugin/src/org/jedit/ruby/structure/ProgressiveSelector.java

#
Java | 437 lines | 382 code | 33 blank | 22 comment | 56 complexity | 71f18df50da1c7c49f8c91c46f30d28e 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. * ProgressiveSelector.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.structure;
  21. import org.gjt.sp.jedit.Buffer;
  22. import org.gjt.sp.jedit.TextUtilities;
  23. import org.gjt.sp.jedit.View;
  24. import org.gjt.sp.jedit.textarea.JEditTextArea;
  25. import org.gjt.sp.jedit.textarea.Selection;
  26. import org.gjt.sp.jedit.textarea.TextArea;
  27. import org.jedit.ruby.RubyPlugin;
  28. import org.jedit.ruby.utils.CommandUtils;
  29. import org.jedit.ruby.ast.Member;
  30. import org.jedit.ruby.ast.RubyMembers;
  31. import org.jedit.ruby.parser.RubyParser;
  32. /**
  33. * @author robmckinnon at users.sourceforge.net
  34. */
  35. public final class ProgressiveSelector {
  36. private static final RubyTokenHandler tokenHandler = new RubyTokenHandler();
  37. public static void doProgressiveSelection(View view) {
  38. JEditTextArea textArea = view.getTextArea();
  39. String text = textArea.getText();
  40. if (text.length() > 0) {
  41. doProgressiveSelection(textArea, text, view);
  42. }
  43. }
  44. private static void doProgressiveSelection(JEditTextArea textArea, String text, View view) {
  45. int caretPosition = textArea.getCaretPosition();
  46. Selection[] selections = textArea.getSelection();
  47. Selection selection = selections.length > 0 ? selections[0] : null;
  48. textArea.selectNone();
  49. boolean needToSelectMoreDefault = true;
  50. if (caretPosition == text.length() || !matchesLiteralChar(text.charAt(caretPosition))) {
  51. if (!(caretPosition > 0 && matchesLiteralChar(text.charAt(caretPosition - 1)))) {
  52. needToSelectMoreDefault = false;
  53. selectWord(textArea);
  54. if (textArea.getSelection().length == 0) {
  55. selectBeyondLine(view, textArea, selection);
  56. }
  57. }
  58. }
  59. if (needToSelectMore(textArea, selection, needToSelectMoreDefault)) {
  60. selectMore(caretPosition, textArea, selection, view);
  61. }
  62. }
  63. private static void selectMore(int caretPosition, JEditTextArea textArea, Selection selection, View view) {
  64. Buffer buffer = view.getBuffer();
  65. try {
  66. handleLiteral(buffer, caretPosition, textArea, selection);
  67. } catch (Exception e) {
  68. e.printStackTrace();
  69. RubyPlugin.log(e.getMessage(), ProgressiveSelector.class);
  70. }
  71. if (needToSelectMore(textArea, selection)) {
  72. selectLineExcludingWhitespace(textArea);
  73. }
  74. if (needToSelectMore(textArea, selection)) {
  75. selectLine(textArea);
  76. }
  77. if (needToSelectMore(textArea, selection)) {
  78. selectBeyondLine(view, textArea, selection);
  79. }
  80. }
  81. private static void handleLiteral(Buffer buffer, int caretPosition, JEditTextArea textArea, Selection selection) {
  82. RubyToken first = tokenHandler.getTokenAtCaret(buffer, caretPosition);
  83. RubyToken second = first.getNextToken();
  84. RubyToken prior = first;
  85. RubyToken current = second;
  86. if (prior.isLiteral() || current.isLiteral()) {
  87. if (current.isLiteral() && (selection != null || !prior.isLiteral())) {
  88. while (current.isNextLiteral()) {
  89. current = current.getNextToken();
  90. }
  91. }
  92. if (prior.isLiteral() && (selection != null || !current.isLiteral())) {
  93. while (prior.isPreviousLiteral()) {
  94. prior = prior.getPreviousToken();
  95. }
  96. }
  97. int lineStartOffset = textArea.getLineStartOffset(textArea.getCaretLine());
  98. int start = lineStartOffset;
  99. int end = lineStartOffset;
  100. if (prior.isLiteral()) {
  101. start += prior.offset;
  102. } else {
  103. start += second.offset;
  104. }
  105. if (current.isLiteral()) {
  106. end += current.offset + current.length;
  107. } else {
  108. end += first.offset + first.length;
  109. }
  110. RubyPlugin.log("prior " + prior + " current " + current, ProgressiveSelector.class);
  111. RubyPlugin.log("start " + start + " end " + end, ProgressiveSelector.class);
  112. if (selection != null) {
  113. RubyPlugin.log("sstart " + selection.getStart() + " send " + selection.getEnd(), ProgressiveSelector.class);
  114. }
  115. boolean unselectQuotes = true;
  116. boolean emptyString = start == (end - 2);
  117. if (selection != null) {
  118. if (selection.getStart() == start) {
  119. unselectQuotes = false;
  120. } else if ((start + 1) == selection.getStart() && (end - 1) == selection.getEnd()) {
  121. unselectQuotes = false;
  122. }
  123. } else {
  124. unselectQuotes = false;
  125. if(!emptyString) {
  126. if (first.length == 1 && first.isLiteral() && !first.isPreviousLiteral()) {
  127. start++;
  128. } else if(second.length == 1 && second.isLiteral() && !second.isNextLiteral()) {
  129. end--;
  130. }
  131. }
  132. }
  133. if (!current.isLiteral() || !prior.isLiteral()) {
  134. unselectQuotes = false;
  135. }
  136. if (unselectQuotes && !emptyString) {
  137. start++;
  138. end--;
  139. }
  140. setSelection(start, end, textArea);
  141. }
  142. }
  143. private static boolean matchesLiteralChar(char character) {
  144. switch (character) {
  145. case '\'':
  146. case '"':
  147. case '[':
  148. case ']':
  149. return true;
  150. default:
  151. return false;
  152. }
  153. }
  154. private static void selectBeyondLine(View view, JEditTextArea textArea, Selection selection) {
  155. if (RubyPlugin.isRuby(view.getBuffer())) {
  156. try {
  157. try {
  158. RubyMembers members = RubyParser.getMembers(view);
  159. Member member = members.getMemberAt(textArea.getCaretPosition());
  160. selectBeyondLineRuby(textArea, selection, member);
  161. } catch (Exception e) {
  162. selectBeyondLineNonRuby(textArea, selection);
  163. }
  164. } catch (Exception e) {
  165. selectBeyondLineNonRuby(textArea, selection);
  166. }
  167. } else {
  168. selectBeyondLineNonRuby(textArea, selection);
  169. }
  170. }
  171. private static void selectBeyondLineRuby(JEditTextArea textArea, Selection selection, Member member) {
  172. if (member == null) {
  173. selectBeyondLineNonRuby(textArea, selection);
  174. } else {
  175. // if (insideMember(textArea, member)) {
  176. // selectParagraphInMember(textArea, member, selection);
  177. // } else {
  178. selectMemberOrParent(member, textArea, selection);
  179. // }
  180. }
  181. }
  182. private static boolean insideMember(JEditTextArea textArea, Member member) {
  183. int caretLine = textArea.getCaretLine();
  184. int memberStartLine = textArea.getLineOfOffset(member.getStartOffset());
  185. int memberEndLine = textArea.getLineOfOffset(member.getEndOffset());
  186. return memberStartLine < caretLine && caretLine < memberEndLine;
  187. }
  188. private static void selectParagraphInMember(JEditTextArea textArea, Member member, Selection selection) {
  189. selectParagraph(textArea);
  190. Selection paragraphSelection = textArea.getSelection()[0];
  191. if (paragraphSelection != null) {
  192. boolean hitMemberStart = paragraphSelection.getStart() <= member.getStartOffset();
  193. boolean hitMemberEnd = paragraphSelection.getEnd() >= member.getEndOffset();
  194. if (!(hitMemberStart && hitMemberEnd)) {
  195. if (hitMemberStart) {
  196. int line = textArea.getLineOfOffset(member.getStartOffset());
  197. int offset = textArea.getLineStartOffset(line + 1);
  198. setSelection(offset, paragraphSelection.getEnd(), textArea);
  199. } else if (hitMemberEnd) {
  200. int line = textArea.getLineOfOffset(member.getEndOffset());
  201. int offset = textArea.getLineEndOffset(line - 1);
  202. setSelection(paragraphSelection.getStart(), offset, textArea);
  203. }
  204. }
  205. if (needToSelectMore(textArea, selection)) {
  206. selectMemberOrParent(member, textArea, selection);
  207. }
  208. } else {
  209. selectMemberOrParent(member, textArea, selection);
  210. }
  211. }
  212. private static void selectBeyondLineNonRuby(JEditTextArea textArea, Selection selection) {
  213. selectParagraph(textArea);
  214. if (textArea.getSelection().length == 0 || needToSelectMore(textArea, selection)) {
  215. selectAll(textArea);
  216. }
  217. }
  218. private static void selectMemberOrParent(Member member, JEditTextArea textArea, Selection selection) {
  219. if (insideMember(textArea, member)) {
  220. selectMemberContents(member, textArea);
  221. }
  222. if (needToSelectMore(textArea, selection)) {
  223. selectMember(member, textArea);
  224. }
  225. if (needToSelectMore(textArea, selection)) {
  226. if (member.hasParentMember()) {
  227. member = member.getParentMember();
  228. selectMemberOrParent(member, textArea, selection);
  229. } else {
  230. selectAll(textArea);
  231. }
  232. }
  233. }
  234. private static void selectMemberContents(Member member, JEditTextArea textArea) {
  235. int start = member.getStartOffset();
  236. int line = textArea.getLineOfOffset(start) + 1;
  237. start = textArea.getLineStartOffset(line);
  238. int end = member.getEndOffset();
  239. line = textArea.getLineOfOffset(end) - 1;
  240. end = textArea.getLineEndOffset(line);
  241. if (start < end) {
  242. setSelection(start, end, textArea);
  243. }
  244. }
  245. private static void selectMember(Member member, JEditTextArea textArea) {
  246. int start = member.getStartOffset();
  247. int line = textArea.getLineOfOffset(start);
  248. start = textArea.getLineStartOffset(line);
  249. int end = member.getEndOffset();
  250. char character = textArea.getText(end, 1).charAt(0);
  251. if (character != '\n' && character != '\r') {
  252. end++;
  253. }
  254. setSelection(start, end, textArea);
  255. }
  256. private static void setSelection(int start, int end, JEditTextArea textArea) {
  257. Selection.Range range = new Selection.Range(start, end);
  258. textArea.setSelection(range);
  259. }
  260. private static boolean needToSelectMore(JEditTextArea textArea, Selection originalSelection) {
  261. return needToSelectMore(textArea, originalSelection, false);
  262. }
  263. private static boolean needToSelectMore(JEditTextArea textArea, Selection originalSelection, boolean defaultNeed) {
  264. if (originalSelection != null && !defaultNeed) {
  265. Selection[] selections = textArea.getSelection();
  266. if (selections == null || selections.length == 0) {
  267. return true;
  268. } else {
  269. Selection selection = selections[0];
  270. int start = originalSelection.getStart();
  271. int end = originalSelection.getEnd();
  272. return selection.getStart() >= start && selection.getEnd() <= end;
  273. }
  274. } else {
  275. return defaultNeed;
  276. }
  277. }
  278. /**
  279. * Selects the word at the caret position.
  280. *
  281. * @since jEdit 2.7pre2
  282. */
  283. private static void selectWord(TextArea textArea) {
  284. int line = textArea.getCaretLine();
  285. int lineStart = textArea.getLineStartOffset(line);
  286. int offset = textArea.getCaretPosition() - lineStart;
  287. if (textArea.getLineLength(line) == 0)
  288. return;
  289. String lineText = textArea.getLineText(line);
  290. String noWordSep = ((Buffer)CommandUtils.getBuffer(textArea)).getStringProperty("noWordSep");
  291. if (offset == textArea.getLineLength(line))
  292. offset--;
  293. int wordStart = TextUtilities.findWordStart(lineText, offset, noWordSep);
  294. int wordEnd = TextUtilities.findWordEnd(lineText, offset + 1, noWordSep);
  295. Selection s = new Selection.Range(lineStart + wordStart, lineStart + wordEnd);
  296. addToSelection(textArea, s);
  297. }
  298. /**
  299. * Selects the paragraph at the caret position.
  300. *
  301. * @since jEdit 2.7pre2
  302. */
  303. private static void selectParagraph(JEditTextArea textArea) {
  304. int caretLine = textArea.getCaretLine();
  305. if (textArea.getLineLength(caretLine) == 0) {
  306. textArea.getToolkit().beep();
  307. return;
  308. }
  309. int start = caretLine;
  310. int end = caretLine;
  311. while (start >= 0) {
  312. if (textArea.getLineLength(start) == 0 || textArea.getLineText(start).trim().length() == 0)
  313. break;
  314. else
  315. start--;
  316. }
  317. while (end < textArea.getLineCount()) {
  318. if (textArea.getLineLength(end) == 0 || textArea.getLineText(end).trim().length() == 0)
  319. break;
  320. else
  321. end++;
  322. }
  323. int selectionStart = (start != textArea.getLineCount()-1) ? textArea.getLineStartOffset(start + 1) : textArea.getLineEndOffset(start);
  324. int selectionEnd = (end - 1 >= 0) ? textArea.getLineEndOffset(end - 1) - 1 : textArea.getLineStartOffset(end);
  325. if (selectionEnd > selectionStart) {
  326. Selection s = new Selection.Range(selectionStart, selectionEnd);
  327. addToSelection(textArea, s);
  328. }
  329. }
  330. /**
  331. * Selects the current line.
  332. *
  333. * @since jEdit 2.7pre2
  334. */
  335. private static void selectLine(JEditTextArea textArea) {
  336. int caretLine = textArea.getCaretLine();
  337. int start = textArea.getLineStartOffset(caretLine);
  338. int end = textArea.getLineEndOffset(caretLine) - 1;
  339. Selection s = new Selection.Range(start, end);
  340. addToSelection(textArea, s);
  341. }
  342. private static void selectLineExcludingWhitespace(JEditTextArea textArea) {
  343. int caretLine = textArea.getCaretLine();
  344. int start = textArea.getLineStartOffset(caretLine);
  345. int end = textArea.getLineEndOffset(caretLine) - 1;
  346. int nonSpaceStartOffset = RubyPlugin.getNonSpaceStartOffset(caretLine);
  347. if (textArea.getCaretPosition() >= nonSpaceStartOffset) {
  348. start = nonSpaceStartOffset;
  349. }
  350. Selection s = new Selection.Range(start, end);
  351. addToSelection(textArea, s);
  352. }
  353. private static void addToSelection(TextArea textArea, Selection s) {
  354. if (textArea.isMultipleSelectionEnabled()) {
  355. textArea.addToSelection(s);
  356. } else {
  357. textArea.setSelection(s);
  358. }
  359. }
  360. /**
  361. * Selects all text in the buffer.
  362. */
  363. private static void selectAll(JEditTextArea textArea) {
  364. textArea.setSelection(new Selection.Range(0, textArea.getBufferLength()));
  365. }
  366. }