IDEADEV-40651:'Select word' (ctrl+w) should select closure content at one of steps
[idea/community.git] / plugins / groovy / src / org / jetbrains / plugins / groovy / editor / selection / GroovyGStringSelectioner.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.jetbrains.plugins.groovy.editor.selection;
17
18 import com.intellij.codeInsight.editorActions.SelectWordUtil;
19 import com.intellij.lang.ASTNode;
20 import com.intellij.openapi.editor.Editor;
21 import com.intellij.openapi.util.TextRange;
22 import com.intellij.psi.PsiElement;
23 import com.intellij.psi.tree.IElementType;
24 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
25 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrString;
26 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrStringInjection;
27
28 import java.util.ArrayList;
29 import java.util.List;
30
31 import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.*;
32
33 /**
34  * @author Maxim.Medvedev
35  */
36 public class GroovyGStringSelectioner extends GroovyBasicSelectioner {
37   public boolean canSelect(PsiElement e) {
38     PsiElement parent = e.getParent();
39     return parent instanceof GrStringInjection || parent instanceof GrString;
40   }
41
42   @Override
43   public List<TextRange> select(PsiElement e, CharSequence editorText, int cursorOffset, Editor editor) {
44     List<TextRange> ranges = new ArrayList<TextRange>();
45     PsiElement parent = e.getParent();
46
47     if (parent instanceof GrString) {
48       final TextRange selection =
49         new TextRange(editor.getSelectionModel().getSelectionStart(), editor.getSelectionModel().getSelectionEnd());
50
51       TextRange range = getLineTextRange(e, cursorOffset);
52       ranges.add(range);
53       if (selection.contains(range)) {
54
55         PsiElement firstChild = parent.getFirstChild();
56         PsiElement lastChild = parent.getLastChild();
57         if (firstChild.getNode().getElementType() == mGSTRING_BEGIN) {
58           firstChild = firstChild.getNextSibling();
59         }
60         if (lastChild.getNode().getElementType() == mGSTRING_END) {
61           lastChild = lastChild.getPrevSibling();
62         }
63
64         if (firstChild != null && lastChild != null) {
65           range = new TextRange(firstChild.getTextOffset(), lastChild.getTextOffset() + lastChild.getTextLength());
66           ranges.add(range);
67         }
68
69         if (selection.contains(range) || firstChild == null || lastChild == null) {
70           ranges.add(parent.getTextRange());
71         }
72       }
73     }
74     else if (parent instanceof GrStringInjection) {
75       if (e instanceof GrReferenceExpression) {
76         List<TextRange> r = new ArrayList<TextRange>(2);
77         SelectWordUtil.addWordSelection(editor.getSettings().isCamelWords(), editorText, cursorOffset, r);
78         for (TextRange textRange : r) {
79           if (editorText.charAt(textRange.getStartOffset()) == '$') {
80             textRange = new TextRange(textRange.getStartOffset() + 1, textRange.getEndOffset());
81           }
82           ranges.add(textRange);
83         }
84
85       }
86       ranges.add(parent.getTextRange());
87       ranges.add(e.getTextRange());
88     }
89
90     return ranges;
91   }
92
93   private static TextRange getLineTextRange(PsiElement e, int cursorOffset) {
94     assert e.getParent() instanceof GrString;
95
96     PsiElement next = e;
97     int startOffset = cursorOffset;
98     int endOffset = cursorOffset;
99     if (e.getNode().getElementType() == mGSTRING_CONTENT) {
100       final String text = e.getText();
101       int cur;
102       int index = -1;
103       while (true) {
104         cur = text.indexOf('\n', index + 1);
105         if (cur < 0 || cur + e.getTextOffset() > cursorOffset) break;
106         index = cur;
107       }
108       if (index >= 0) {
109         startOffset = e.getTextOffset() + index + 1;
110       }
111
112       index = text.indexOf('\n', cursorOffset - e.getTextOffset());
113       if (index >= 0) {
114         endOffset = e.getTextOffset() + index + 1;
115       }
116     }
117
118     if (startOffset == cursorOffset) {
119       do {
120         if (next == null) break;
121         final ASTNode node = next.getNode();
122         if (node == null) break;
123         final IElementType type = node.getElementType();
124         if (type == mGSTRING_BEGIN) break;
125         if (type == mGSTRING_CONTENT) {
126           final int i = next.getText().lastIndexOf('\n');
127           if (i >= 0) {
128             startOffset = next.getTextOffset() + i + 1;
129             break;
130           }
131         }
132         startOffset = next.getTextOffset();
133         next = next.getPrevSibling();
134       }
135       while (true);
136     }
137
138     if (endOffset == cursorOffset) {
139       next = e;
140       do {
141         if (next == null) break;
142         final ASTNode node = next.getNode();
143         if (node == null) break;
144
145         final IElementType type = node.getElementType();
146         if (type == mGSTRING_END) {
147           endOffset = next.getTextOffset();
148           break;
149         }
150         if (type == mGSTRING_CONTENT) {
151           final int i = next.getText().indexOf('\n');
152           if (i >= 0) {
153             endOffset = next.getTextOffset() + i + 1;
154             break;
155           }
156         }
157         endOffset = next.getTextOffset() + next.getTextLength();
158         next = next.getNextSibling();
159       }
160       while (true);
161     }
162
163     return new TextRange(startOffset, endOffset);
164   }
165 }