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