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() != null && 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);
108 return Result
.CONTINUE
;
111 public Result
charTyped(final char c
, final Project project
, final Editor editor
, final PsiFile file
) {
113 myJavaLTTyped
= false;
114 handleAfterJavaLT(editor
);
117 return Result
.CONTINUE
;
121 private static FileType
getOriginalFileType(final PsiFile file
) {
122 final VirtualFile virtualFile
= file
.getVirtualFile();
123 return virtualFile
!= null ? virtualFile
.getFileType() : null;
126 private static boolean handleSemicolon(Editor editor
, FileType fileType
) {
127 if (fileType
!= StdFileTypes
.JAVA
) return false;
128 int offset
= editor
.getCaretModel().getOffset();
129 if (offset
== editor
.getDocument().getTextLength()) return false;
131 char charAt
= editor
.getDocument().getCharsSequence().charAt(offset
);
132 if (charAt
!= ';') return false;
134 editor
.getCaretModel().moveToOffset(offset
+ 1);
135 editor
.getScrollingModel().scrollToCaret(ScrollType
.RELATIVE
);
139 //need custom handler, since brace matcher cannot be used
140 private static boolean handleJavaGT(final Editor editor
) {
141 if (!CodeInsightSettings
.getInstance().AUTOINSERT_PAIR_BRACKET
) return false;
143 int offset
= editor
.getCaretModel().getOffset();
145 if (offset
== editor
.getDocument().getTextLength()) return false;
147 HighlighterIterator iterator
= ((EditorEx
) editor
).getHighlighter().createIterator(offset
);
148 if (iterator
.getTokenType() != JavaTokenType
.GT
) return false;
149 while (!iterator
.atEnd() && !JavaTypedHandlerUtil
.isTokenInvalidInsideReference(iterator
.getTokenType())) {
153 if (iterator
.atEnd()) return false;
154 if (JavaTypedHandlerUtil
.isTokenInvalidInsideReference(iterator
.getTokenType())) iterator
.retreat();
157 while (!iterator
.atEnd() && balance
>= 0) {
158 final IElementType tokenType
= iterator
.getTokenType();
159 if (tokenType
== JavaTokenType
.LT
) {
162 else if (tokenType
== JavaTokenType
.GT
) {
165 else if (JavaTypedHandlerUtil
.isTokenInvalidInsideReference(tokenType
)) {
173 editor
.getCaretModel().moveToOffset(offset
+ 1);
174 editor
.getScrollingModel().scrollToCaret(ScrollType
.RELATIVE
);
181 //need custom handler, since brace matcher cannot be used
182 private static void handleAfterJavaLT(final Editor editor
) {
183 if (!CodeInsightSettings
.getInstance().AUTOINSERT_PAIR_BRACKET
) return;
185 int offset
= editor
.getCaretModel().getOffset();
186 HighlighterIterator iterator
= ((EditorEx
) editor
).getHighlighter().createIterator(offset
);
187 while (iterator
.getStart() > 0 && !JavaTypedHandlerUtil
.isTokenInvalidInsideReference(iterator
.getTokenType())) {
191 if (JavaTypedHandlerUtil
.isTokenInvalidInsideReference(iterator
.getTokenType())) iterator
.advance();
194 while (!iterator
.atEnd() && balance
>= 0) {
195 final IElementType tokenType
= iterator
.getTokenType();
196 if (tokenType
== JavaTokenType
.LT
) {
199 else if (tokenType
== JavaTokenType
.GT
) {
202 else if (JavaTypedHandlerUtil
.isTokenInvalidInsideReference(tokenType
)) {
210 editor
.getDocument().insertString(offset
, ">");
214 private static void autoPopupJavadocLookup(final Project project
, final Editor editor
) {
215 if (ApplicationManager
.getApplication().isUnitTestMode()) return;
217 final CodeInsightSettings settings
= CodeInsightSettings
.getInstance();
218 if (settings
.AUTO_POPUP_JAVADOC_LOOKUP
) {
219 final PsiFile file
= PsiUtilBase
.getPsiFileInEditor(editor
, project
);
220 if (file
== null) return;
221 final Runnable request
= new Runnable(){
223 PsiDocumentManager
.getInstance(project
).commitAllDocuments();
224 CommandProcessor
.getInstance().executeCommand(project
, new Runnable() {
226 new JavadocAutoLookupHandler().invoke(project
, editor
, file
);
230 null, UndoConfirmationPolicy
.DEFAULT
, editor
.getDocument()
234 AutoPopupController
.getInstance(project
).invokeAutoPopupRunnable(request
, settings
.JAVADOC_LOOKUP_DELAY
);
238 public static boolean isAfterClassLikeIdentifierOrDot(final int offset
, final Editor editor
) {
239 HighlighterIterator iterator
= ((EditorEx
) editor
).getHighlighter().createIterator(offset
);
240 if (iterator
.atEnd()) return false;
241 if (iterator
.getStart() > 0) iterator
.retreat();
242 final IElementType tokenType
= iterator
.getTokenType();
243 if (tokenType
== JavaTokenType
.DOT
) return true;
244 if (tokenType
== JavaTokenType
.IDENTIFIER
&& iterator
.getEnd() == offset
) {
245 final CharSequence chars
= editor
.getDocument().getCharsSequence();
246 final char startChar
= chars
.charAt(iterator
.getStart());
247 if (!Character
.isUpperCase(startChar
)) return false;
248 final CharSequence word
= chars
.subSequence(iterator
.getStart(), iterator
.getEnd());
249 if (word
.length() == 1) return true;
250 for (int i
= 1; i
< word
.length(); i
++) {
251 if (Character
.isLowerCase(word
.charAt(i
))) return true;
258 private static class MemberAutoLookupCondition
implements Condition
<Editor
> {
259 public boolean value(final Editor editor
) {
260 final Project project
= editor
.getProject();
261 if (project
== null) return false;
262 PsiFile file
= PsiDocumentManager
.getInstance(project
).getPsiFile(editor
.getDocument());
263 if (file
== null) return false;
264 int offset
= editor
.getCaretModel().getOffset();
266 PsiElement lastElement
= file
.findElementAt(offset
- 1);
267 if (lastElement
== null) {
271 //do not show lookup when typing varargs ellipsis
272 final PsiElement prevSibling
= lastElement
.getPrevSibling();
273 if (prevSibling
== null || ".".equals(prevSibling
.getText())) return false;
274 PsiElement parent
= prevSibling
;
276 parent
= parent
.getParent();
277 } while(parent
instanceof PsiJavaCodeReferenceElement
|| parent
instanceof PsiTypeElement
);
278 if (parent
instanceof PsiParameterList
) return false;
280 if (!".".equals(lastElement
.getText()) && !"#".equals(lastElement
.getText())) {
284 final PsiElement element
= file
.findElementAt(offset
);
285 return element
== null ||
286 !"#".equals(lastElement
.getText()) ||
287 new SuperParentFilter(new ClassFilter(PsiDocComment
.class)).isAcceptable(element
, element
.getParent());