2 * Copyright 2000-2009 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com
.intellij
.codeInsight
.editorActions
;
18 import com
.intellij
.codeInsight
.AutoPopupController
;
19 import com
.intellij
.codeInsight
.CodeInsightSettings
;
20 import com
.intellij
.codeInsight
.completion
.JavadocAutoLookupHandler
;
21 import com
.intellij
.openapi
.application
.ApplicationManager
;
22 import com
.intellij
.openapi
.command
.CommandProcessor
;
23 import com
.intellij
.openapi
.command
.UndoConfirmationPolicy
;
24 import com
.intellij
.openapi
.editor
.Editor
;
25 import com
.intellij
.openapi
.editor
.ScrollType
;
26 import com
.intellij
.openapi
.editor
.EditorModificationUtil
;
27 import com
.intellij
.openapi
.editor
.ex
.EditorEx
;
28 import com
.intellij
.openapi
.editor
.highlighter
.HighlighterIterator
;
29 import com
.intellij
.openapi
.fileTypes
.FileType
;
30 import com
.intellij
.openapi
.fileTypes
.StdFileTypes
;
31 import com
.intellij
.openapi
.project
.Project
;
32 import com
.intellij
.openapi
.util
.Condition
;
33 import com
.intellij
.openapi
.vfs
.VirtualFile
;
34 import com
.intellij
.psi
.*;
35 import com
.intellij
.psi
.filters
.ClassFilter
;
36 import com
.intellij
.psi
.filters
.position
.SuperParentFilter
;
37 import com
.intellij
.psi
.javadoc
.PsiDocComment
;
38 import com
.intellij
.psi
.jsp
.JspFile
;
39 import com
.intellij
.psi
.tree
.IElementType
;
40 import com
.intellij
.psi
.util
.PsiTreeUtil
;
41 import com
.intellij
.psi
.util
.PsiUtil
;
42 import com
.intellij
.psi
.util
.PsiUtilBase
;
43 import org
.jetbrains
.annotations
.Nullable
;
48 public class JavaTypedHandler
extends TypedHandlerDelegate
{
49 private boolean myJavaLTTyped
;
51 public Result
checkAutoPopup(final char charTyped
, final Project project
, final Editor editor
, final PsiFile file
) {
52 if (charTyped
== '@' && file
instanceof PsiJavaFile
) {
53 autoPopupJavadocLookup(project
, editor
);
56 if (charTyped
== '#' || charTyped
== '.') {
57 AutoPopupController
.getInstance(project
).autoPopupMemberLookup(editor
, new MemberAutoLookupCondition());
61 return Result
.CONTINUE
;
64 public Result
beforeCharTyped(final char c
, final Project project
, final Editor editor
, final PsiFile file
, final FileType fileType
) {
65 final FileType originalFileType
= getOriginalFileType(file
);
67 int offsetBefore
= editor
.getCaretModel().getOffset();
69 //important to calculate before inserting charTyped
70 myJavaLTTyped
= '<' == c
&&
71 file
instanceof PsiJavaFile
&&
72 !(file
instanceof JspFile
) &&
73 CodeInsightSettings
.getInstance().AUTOINSERT_PAIR_BRACKET
&&
74 PsiUtil
.isLanguageLevel5OrHigher(file
) &&
75 isAfterClassLikeIdentifierOrDot(offsetBefore
, editor
);
78 if (file
instanceof PsiJavaFile
&& !(file
instanceof JspFile
) &&
79 CodeInsightSettings
.getInstance().AUTOINSERT_PAIR_BRACKET
&&
80 PsiUtil
.isLanguageLevel5OrHigher(file
)) {
81 if (handleJavaGT(editor
)) return Result
.STOP
;
86 if (handleSemicolon(editor
, fileType
)) return Result
.STOP
;
88 if (originalFileType
== StdFileTypes
.JAVA
&& c
== '{') {
89 int offset
= editor
.getCaretModel().getOffset();
90 HighlighterIterator iterator
= ((EditorEx
) editor
).getHighlighter().createIterator(offset
- 1);
91 while (iterator
.getTokenType() == TokenType
.WHITE_SPACE
) {
94 if (iterator
.getTokenType() == JavaTokenType
.RBRACKET
|| iterator
.getTokenType() == JavaTokenType
.EQ
) {
95 return Result
.CONTINUE
;
97 PsiDocumentManager
.getInstance(project
).commitDocument(editor
.getDocument());
98 final PsiElement leaf
= file
.findElementAt(offset
);
99 if (PsiTreeUtil
.getParentOfType(leaf
, PsiArrayInitializerExpression
.class, false, PsiCodeBlock
.class, PsiMember
.class) != null) {
100 return Result
.CONTINUE
;
102 if (PsiTreeUtil
.getParentOfType(leaf
, PsiCodeBlock
.class, false, PsiMember
.class) != null) {
103 EditorModificationUtil
.insertStringAtCaret(editor
, "{", false, true);
104 TypedHandler
.indentOpenedBrace(project
, editor
);
109 return Result
.CONTINUE
;
112 public Result
charTyped(final char c
, final Project project
, final Editor editor
, final PsiFile file
) {
114 myJavaLTTyped
= false;
115 handleAfterJavaLT(editor
);
118 return Result
.CONTINUE
;
122 private static FileType
getOriginalFileType(final PsiFile file
) {
123 final VirtualFile virtualFile
= file
.getVirtualFile();
124 return virtualFile
!= null ? virtualFile
.getFileType() : null;
127 private static boolean handleSemicolon(Editor editor
, FileType fileType
) {
128 if (fileType
!= StdFileTypes
.JAVA
) return false;
129 int offset
= editor
.getCaretModel().getOffset();
130 if (offset
== editor
.getDocument().getTextLength()) return false;
132 char charAt
= editor
.getDocument().getCharsSequence().charAt(offset
);
133 if (charAt
!= ';') return false;
135 editor
.getCaretModel().moveToOffset(offset
+ 1);
136 editor
.getScrollingModel().scrollToCaret(ScrollType
.RELATIVE
);
140 //need custom handler, since brace matcher cannot be used
141 private static boolean handleJavaGT(final Editor editor
) {
142 if (!CodeInsightSettings
.getInstance().AUTOINSERT_PAIR_BRACKET
) return false;
144 int offset
= editor
.getCaretModel().getOffset();
146 if (offset
== editor
.getDocument().getTextLength()) return false;
148 HighlighterIterator iterator
= ((EditorEx
) editor
).getHighlighter().createIterator(offset
);
149 if (iterator
.getTokenType() != JavaTokenType
.GT
) return false;
150 while (!iterator
.atEnd() && !JavaTypedHandlerUtil
.isTokenInvalidInsideReference(iterator
.getTokenType())) {
154 if (iterator
.atEnd()) return false;
155 if (JavaTypedHandlerUtil
.isTokenInvalidInsideReference(iterator
.getTokenType())) iterator
.retreat();
158 while (!iterator
.atEnd() && balance
>= 0) {
159 final IElementType tokenType
= iterator
.getTokenType();
160 if (tokenType
== JavaTokenType
.LT
) {
163 else if (tokenType
== JavaTokenType
.GT
) {
166 else if (JavaTypedHandlerUtil
.isTokenInvalidInsideReference(tokenType
)) {
174 editor
.getCaretModel().moveToOffset(offset
+ 1);
175 editor
.getScrollingModel().scrollToCaret(ScrollType
.RELATIVE
);
182 //need custom handler, since brace matcher cannot be used
183 private static void handleAfterJavaLT(final Editor editor
) {
184 if (!CodeInsightSettings
.getInstance().AUTOINSERT_PAIR_BRACKET
) return;
186 int offset
= editor
.getCaretModel().getOffset();
187 HighlighterIterator iterator
= ((EditorEx
) editor
).getHighlighter().createIterator(offset
);
188 while (iterator
.getStart() > 0 && !JavaTypedHandlerUtil
.isTokenInvalidInsideReference(iterator
.getTokenType())) {
192 if (JavaTypedHandlerUtil
.isTokenInvalidInsideReference(iterator
.getTokenType())) iterator
.advance();
195 while (!iterator
.atEnd() && balance
>= 0) {
196 final IElementType tokenType
= iterator
.getTokenType();
197 if (tokenType
== JavaTokenType
.LT
) {
200 else if (tokenType
== JavaTokenType
.GT
) {
203 else if (JavaTypedHandlerUtil
.isTokenInvalidInsideReference(tokenType
)) {
211 editor
.getDocument().insertString(offset
, ">");
215 private static void autoPopupJavadocLookup(final Project project
, final Editor editor
) {
216 if (ApplicationManager
.getApplication().isUnitTestMode()) return;
218 final CodeInsightSettings settings
= CodeInsightSettings
.getInstance();
219 if (settings
.AUTO_POPUP_JAVADOC_LOOKUP
) {
220 final PsiFile file
= PsiUtilBase
.getPsiFileInEditor(editor
, project
);
221 if (file
== null) return;
222 final Runnable request
= new Runnable(){
224 PsiDocumentManager
.getInstance(project
).commitAllDocuments();
225 CommandProcessor
.getInstance().executeCommand(project
, new Runnable() {
227 new JavadocAutoLookupHandler().invoke(project
, editor
, file
);
231 null, UndoConfirmationPolicy
.DEFAULT
, editor
.getDocument()
235 AutoPopupController
.getInstance(project
).invokeAutoPopupRunnable(request
, settings
.JAVADOC_LOOKUP_DELAY
);
239 public static boolean isAfterClassLikeIdentifierOrDot(final int offset
, final Editor editor
) {
240 HighlighterIterator iterator
= ((EditorEx
) editor
).getHighlighter().createIterator(offset
);
241 if (iterator
.atEnd()) return false;
242 if (iterator
.getStart() > 0) iterator
.retreat();
243 final IElementType tokenType
= iterator
.getTokenType();
244 if (tokenType
== JavaTokenType
.DOT
) return true;
245 if (tokenType
== JavaTokenType
.IDENTIFIER
&& iterator
.getEnd() == offset
) {
246 final CharSequence chars
= editor
.getDocument().getCharsSequence();
247 final char startChar
= chars
.charAt(iterator
.getStart());
248 if (!Character
.isUpperCase(startChar
)) return false;
249 final CharSequence word
= chars
.subSequence(iterator
.getStart(), iterator
.getEnd());
250 if (word
.length() == 1) return true;
251 for (int i
= 1; i
< word
.length(); i
++) {
252 if (Character
.isLowerCase(word
.charAt(i
))) return true;
259 private static class MemberAutoLookupCondition
implements Condition
<Editor
> {
260 public boolean value(final Editor editor
) {
261 final Project project
= editor
.getProject();
262 if (project
== null) return false;
263 PsiFile file
= PsiDocumentManager
.getInstance(project
).getPsiFile(editor
.getDocument());
264 if (file
== null) return false;
265 int offset
= editor
.getCaretModel().getOffset();
267 PsiElement lastElement
= file
.findElementAt(offset
- 1);
268 if (lastElement
== null) {
272 //do not show lookup when typing varargs ellipsis
273 final PsiElement prevSibling
= lastElement
.getPrevSibling();
274 if (prevSibling
== null || ".".equals(prevSibling
.getText())) return false;
275 PsiElement parent
= prevSibling
;
277 parent
= parent
.getParent();
278 } while(parent
instanceof PsiJavaCodeReferenceElement
|| parent
instanceof PsiTypeElement
);
279 if (parent
instanceof PsiParameterList
) return false;
281 if (!".".equals(lastElement
.getText()) && !"#".equals(lastElement
.getText())) {
285 final PsiElement element
= file
.findElementAt(offset
);
286 return element
== null ||
287 !"#".equals(lastElement
.getText()) ||
288 new SuperParentFilter(new ClassFilter(PsiDocComment
.class)).isAcceptable(element
, element
.getParent());