diff options
author | Tor Norbye <tnorbye@google.com> | 2014-04-25 07:38:52 -0700 |
---|---|---|
committer | Tor Norbye <tnorbye@google.com> | 2014-04-25 07:38:58 -0700 |
commit | 0f831a730c50607e2ffd95020875af6185e17734 (patch) | |
tree | 3caf947bbfceeb37eda9c4a1bee319fdd7678fe5 /java/java-impl/src/com/intellij/codeInsight/template | |
parent | 92584642bab4fdb27ac1067c124f4636be762978 (diff) | |
download | idea-0f831a730c50607e2ffd95020875af6185e17734.tar.gz |
Snapshot idea/136.1761 from git://git.jetbrains.org/idea/community.git
3608216: Merge remote-tracking branch 'origin/master'
0d16248: Django inspection HTMLs moved to appropriate place
1ed8715: PY-1194 Provide completion and navigation for url tag in django template Inspection added.
4c6ec52: Naive fix for EA-53569
44c2a03: Do not wrap border with TitledBorder if there is not title
8591130: omg2 [r=ignatov]
dce57e7: omg
3b164b9: Merge remote-tracking branch 'origin/master'
9c04fe7: Ctrl-Q: process java 8 signature style as 1.8 javadoc could also be run on sources 1.6 (IDEA-124175)
b073132: javadoc: ensure that generic arguments are shown in error presentation
b8fb550: IDEA-124271
209d176: disable stream api conversion on iterable (IDEA-124222)
6ca89eb: default external javadoc url for java 8 fixed (IDEA-124175)
2fe28f0: Merge remote-tracking branch 'origin/master'
f65712c: Merge remote-tracking branch 'origin/master'
7de60e2f: Don't escape spaces as it brakes mappings and they are escaped later in GeneralCommandLine (PY-12550).
2bd67bc: IDEA-90194 Ability to disable drag & drop in Project View
f772fb2: Merge remote-tracking branch 'origin/master'
817908d: fixed importing for the iron python
053b716: Merge remote-tracking branch 'origin/master'
1d04dc7: fixed unittest folder tests
76f35f3: suppress doCancelAction
0d9b47d: IDEA-124260 Data Sources and Drivers: can't apply changes to schemas that are shown and used to resolve
f72e4f5: testdata for IDEA-124148
1c431a8: create meth reference from usage: substitute param types (IDEA-124322)
c160fc5: copy to temp/array for non-effectively final variables used in lambda expressions
265f0dc: lambda -> anonymous: static calls (IDEA-124187)
2200bba: method hierarchy: search functional expressions by current method only (IDEA-124320); navigation (IDEA-124319)
54ced00: highlight all pairs of methods with same erasures (IDEA-124116)
f3efd90: IDEA-120865 jre7 osx fullscreen: "space" is not freed when project is closed
6924cc4: IDEA-118062 Gradle Run Configuration: 'working directory' is set incorrectly
09333b1: External system: source package prefix support added; fix for out-of-process mode support
6de52dc: Gradle: update Tooling API version up to 1.12-rc-1, fix classpath for out-of-process mode
4ff20d5: InconsistentResourceBundle test fixed
90bb5ab: IDEA-124301 jdk9 builds cannot be used for project/module compilation
1236318: always go to file if nothing found
43f40bf: Merge remote-tracking branch 'origin/master'
b2d0942: Add default mapping to /vagrant only in case of absent mappings in Vagrantfile (PY-12700).
fc31680: IDEA-124290
773f103: Offer to launch Vagrant if it is down on getting ssh-config (PY-12672).
460ef41: DumbService.smartInvokeLater(...)
577579d: cleanup after extracting java postfix template provider
214f99b: IDEA-103836
a41e7d8: Merge remote-tracking branch 'origin/master'
4076596: fixed memory leak
c5207b2: fixed PY-12543 Project Interpreters: too big or too small details popup
40d91f5: IDEA-63106 new inspections added to InconsistentResourceBundleInspection
4bc9f42: fixed PY-12690 Detected from creating virtualenv interpreter has invalid slashes
e6d8c98: extract right arrow
43b861b: don't use borders and insets in output html/rtf (IDEA-67767)
6c4dbea: use service instead of application component (IDEA-67767)
aa6fb9f: remove 'Strip common indent' setting from UI (IDEA-67767)
5651ab5: IDEA-123775 (String.equals("") inspection fixes code incorrectly)
c1d9f5f: fixed PY-12696 Create VirtualEnv: when adding first base interpreter it is not selected right away
482491a: Platform: Local History can survive for more than 30 days now (int overflow fixed)
a0125dc: Platform: always allow writing module files (IDEA-123899)
3a294e9: removed unused component methods
7cc124e: optimisation: avoid containingfile calculation
06f574a: readable toString()
60f2a36: perforce tests fixed
46fea0f: fewer create/delete operations
7269bce: drain file type detection queue in tests
d0c6ef5: cleanup
9df7136: Gradle: installer build fix
152c230: Platform: Local History can survive for more than 30 days now
f5e7215: do not rely on memory index storage isBufferedFlag to process physical file contents
ebe62cf: customize password field labels
97ade8d: ability to enable only visible components
3541938: introduce beforeOKAction()
13bf7e1: cleanup
4a12b1f: junit: workaround for old junit versions (IDEA-124201)
21003e0: Merge remote-tracking branch 'origin/master'
c2067ff: PY-1194 Provide completion and navigation for url tag in django template PY-3591 Support {% url %} tag arguments completion
5f295fe: do not cancel offline inspections
2b8e2e8: add unchecked warning calls also for method references
57e886f: testdata: check that method ref on static interface method is accepted
8bad807: lambda: do not skip generic method when non-generic method exist in the same class
7d74b9d: NPE
0da9f91: testdata: ensure overload resolution prefer correct one from methods with boxed types
2c49817: NPE
d5107d6: testdata: ensure intersection type is not missed during inference
7898108: testdata: ensure lambda can't be inferred from type parameter
5b91054: testdata: ensure overload resolution by return type should not take place for interrupted control flow
9aa2f3a: testdata: ensure correct parameters number in functional interface method
1698f7b: testdata: ensure method ref qualified are checked over static problems
b49d8e75: testdata
f1f6fcf: functional interfaces: ignore methods with type parameters during abstract method choosing
a55db18: lambda: accept intersection type when exactly one conjunct is functional
4a34bd5: do not report unrelated return type error only for generics methods
76a9293: testdata
17b020f: new overload resolution: covariant comparison of return types
98ab76f: new overload resolution: most specific check on invalid class exceptions fixed
552dfe9: lambda: intersection type produces conjunction of abstract methods
bd9dbfc: testdata
9914757: warn about underscore identifier
72ae579: IDEA-109187 new HgReference validator implemented
d86307e: tip reference moved as a separated static constant to HgUtil
71cd276: Fix scrolling in module aware configurables [r=ushakov]
37ed33e: Missed commit for IDEA-79312
da52f84: IDEA-79312 text cursor gets lost - could bee disabled by focus.fix.lost.cursor key
a39b289: [git log] IDEA-122305 Fix structure filter.
339dd23: beforeShown() & preselect laf-default editor scheme 2
f9d8704: PY-1194 Provide completion and navigation for url tag in django template PY-3591 Support {% url %} tag arguments completion
17e8b30: ctrl+delete/ctrl+backspace enhancements IDEA-100906 Ruby-Editor: CTRL+BACKSPACE Could Not Delete A String
a95fd00: remove copyright messages
bf06d9d: read-action for getFilesForFastWordSearch
cdcd770: IDEA-123687 Velocity formatter inserts wrong linebreak
562443f: Fixed WI-17474 Custom Folding: defaultstate="collapsed" does not seem to have any effect (the node itself is already a line comment)
d86998d: Cleanup warnings
4bbd22c: jb v8 debug protocol: evaluate
46eaf0b: NPE
4101b8c: cleanup
399e745: write action required
004dfc4: IDEA-124220 Auto-popup parameter info should work after smart completion which inserted a comma
0bd06e5: More on comparison that is always false due to being out of type range on implicit type conversion (IDEA-124210)
b6bccb7: AppCode: minimum OS X version specified in .app
ca8a657: add aliases for Lafs
e254ebf: Merge branch 'master' of git.labs.intellij.net:idea/community
5c594f6: Gradle: update Tooling API version up to 1.12-rc-1
17fcca3: Merge remote-tracking branch 'origin/master'
5248eb0: IDEA-119996 FilePathImpl: avoid VFS refresh from getVirtualFile etc.
9c69111: [vcs] Reuse the VirtualFile instead of creating FilePathImpl on File
c08f50b: fixed EA-54835 - NPE: PythonNoseTestConfigurationProducer.isAvailable
d627711: fixed EA-55749 - AIOOBE: PyStructuredDocstringFormatter.formatDocstring
c9a1c11: apply laf once, fix stale bg, add beforeShown() & preselect laf-default editor scheme
3e576c2: fixed EA-55872 - NPE: PyProtectedMemberInspection$Visitor.checkReference
147c647: use ValueType enum instead of plain numbers
2e517c5: IDEA-112050 vcs: fix ByteBuffer to array conversion
97a6a21: possible race condition fixed
cb20ef5: Prevent horizontal scrollbar appearance if an error message is too long
cbb86d4: IDEA-124021 Cannot resolve method 'print(int)' with JSP custom tags: comment
362b4d7: IDEA-123986 New project wizard: remove highlighting from disabled templates
cc57b39: do not include runtime dependencies to compilation scope (ZD-25927)
7ac3c11: paste rich-text data: NPE fixed
89ca157: Extract Java live postfix templates provider
e5eb7aa: EA-31437 - assert: DocumentFoldingInfo.writeExternal
e2461d5: use JBImageIcon
e9eb171: initial
ed33f35: fix Keymap page on Mac/Darcula. see same in LafManager for more info.
99bc2e9: open some API
d9aed3f: process class names just once in completion
094f040: IDEA-122362 'getClass()' should be suggested in smart completion if Class<? extends SomeAncestorOfCurrentClass> is expected
1a34573: IDEA-120139 Autopopup doesn't popup sometimes after completion
e8c981a: create LookupUi and move there some ui-related code from LookupImpl; skip this code in tests
89d771e: @NotNull
8d25ba3: Merge remote-tracking branch 'origin/master'
7fcce27: fix darcula lafinfo
bfef59e: Start plugins wizard #19 usability improvements
5cbd491: minor fix
58d2f15: fix bg
40fa260: don't create content entry for temp directory of temp project when opening single file
e31a4c1: Correctly handle custom folding "desc" attribute followed by "defaultstate"
448e9f8: don't report on incomplete code or comparisons that are already reported by "Constant conditions & exceptions"
6e68111: remove superfluous inspection
a2cc5ba: expand description
814378f: Gradle: gradle distribution pattern updated
e9485f0: use TransparentPanel
db66426: initial
a712571: suppress unused params inspection
640ddf3: make labels have constant height to avoid relayout and blinking
921f39e: don't fill bg if component is not opaque
a43d7f8: added new test for live postfix templates
50addd3: IDEA-123935 New Project wizard: no project/global libraries are created on new module adding via project structure
6021da6: Merge remote-tracking branch 'origin/master'
36848a5: fixed test data
7a63d2b: Gradle: tooling extensions integration tests, avoid dependency for explicit distributions downloading in a separate run configuration (like this one http://buildserver.labs.intellij.net/viewType.html?buildTypeId=IDEATrunk_GradleDownloads)
405332a: make richCopy functionality work properly on Linux (IDEA-67767)
3dca908: some 'finally' blocks
00a4968: reverted content-dependent flag
6064460: Merge remote-tracking branch 'origin/master'
5f203cb: Fixed IDEA-124191 Code style preference : space within empty method call/declaration parentheses are not saved
0419dcc: EA-56035 - NCDFE: UnixProcessManager.<clinit>
0285ece: cleanup: use ordinary invokeLater and avoid unnecessary synchronization
0922a97: Vagrant error messaging fixed.
2676330: Save vagrant executable path to application settings (PY-12680).
947d8ab: missing test data
6154126: IDEA-124137 (Inspection: "equals() between objects of inconvertible types" should support java.util.Objects.equals() or com.google.common.base.Objects.equal())
c9fd1bb: fix name
f3ec0f7: lookup arranger tweak [zolotov, shrago] relates to IDEA-121998 SQL code completion too aggressive
c324511: fix escaping, parse decimal literals #WEB-11938 fixed
a5bcae9: Github: fix error message
95d057d: Github: fix invalid condition
dda5341: Github: log invalid requests
d493d76: it is even better to store deltas for direct mapping values
a67ac0b: proper unpacking code
3ea5951: Properties AST factory moved to impl
c0550f7: save keys of particular snapshot before and out of index update optimistically load keys of particular snapshot before and out of index update
d6a5968: custom implementation for IdIndex inputs mapping: -20% for saving deltas
b52691e: saving / reading longs in compact variable byte format + using it for simplier code in PersistentHashMapValueStorage
0ae869e: cleanup
3222cd0: cleanup
e96aa7b: use Gray.TRANSPARENT
2861f10: use Gray.TRANSPARENT
c009821: Merge remote-tracking branch 'origin/master'
caa86fc: what a nice NPE!
3c769ac: goto popup calculations should be cancellable even after Ctrl+Space (IDEA-123714 ?)
c6fcabf: IDEA-122250 New Package: qualified name is checked against ignored directories as a whole
3af306c: IDEA-55556 Inspection suggestion: Comparison that is always false due to being out of type range on implicit type conversion
996d0c6: IDEA-123782 "Complete current statement" doesn't put colon for case branches
7a2cc54: IDEA-122383 PageUp/PageDown don't work in ShowUsages popup (Ctrl-Alt-F7)
29fec22: IDEA-123948 Constant condition and exceptions: false positive on MappedByteBuffer.getInt
0fd86de: IDEA-122946 final modifier not inserted when completing a sequence of parameters from an inner method IDEA-123493 Smart completion makes completed variable final
019a68b: IDEA-123289 Infer nullability of 'for each' variable from type annotations of the collection parameter
c18c4ff: IDEA-120964 Make it more obvious that background compiler is disabled in power save mode
dc2f64a: use superclass for inner class variable name suggestion (IDEA-122109)
c47713f: prefer T if Class<? extends T> is expected (IDEA-121339)
c960901: OC-9559 IDEA-107592
64fc873: cleanup
c159a1a: Start plugins wizard #17
043fa14: extra assert
7eac40c: larger btree block size / load factor increased for better space utilization
a4598a1: enable snapshot mapping
e80c2b3: trigram index enabled in internal mode
527f139: delta encoding of file id set to decrease output size ~3x
a47785b: move to MnemonicHelper
5b12f32: cleanup
27ef5f4: bg progresses for presentation mode
656e311: add transparent color
ddce960: EA-55788 - assert: GitRepositoryImpl
dfc2ef7: external-system: project import fixed to use correct fileToImport
675d14e: OC-9559 IDEA-107592
e6d5ea6: IDEA-86665 hg annotate: ignore whitespace changes
6e551ba: test hg executor updated with ignoreNonZeroExitCode addition option
8798162d: cleanup
45f98c5: IDEA-124021 Cannot resolve method 'print(int)' with JSP custom tags
67f1d44: Better fix for IDEA-124096 open commit actions popup on ctrl+alt+P
8aab4ce: IDEA-123691 Minor project wizard edits: moving settings to the first page
9cb1c40: IDEA-122845 Try to fetch issues, if resource with information about user is not available in this version of Redmine
a2c9393: Add key to JiraRepository to enable old behavior where credentials were sent with every request via basic auth
ce17994: Add special type of exception to indicate request failure from response handlers and other callbacks. ResponseHandler has option to suppress exceptions, if requested resource was not found.
de0f3df: notnull, toString
10c30e2: IDEA-123886, dropped UTF guessing setting
bf14e11: remove obsolete cruft
7b15687: notnull
c82fb9d: mem leak
de48566: test
8a9b3d0: doc leak
d163e52: comment
1274dba: removed confusing createTempPath()
057c5f9: do not expand usage tree if there are too many nodes
e7f245f: debugging IDEA-120167 Phantom eternal Ant task on make
6bf1671: Merge remote-tracking branch 'origin/master'
be70ee8: PY-2880: Django: provide completion for context_object_name value in django template * test added * fixture:complete() is reenterable now
1a99a14: Start plugins wizard #16 and it works
30f6e0e: fix false positive with smart tabs
76fcb91: use provided value description instead of just classname don't compute object presentation by custom configured properties if value description is not equals to class name
014c0e3: jb v8 debug protocol: load full string value
223f19a: typo
339e7d6: WEB-11834 IIS ACL Permissions when creating foundation/bootstrap projects from templates
b875e62: remove freemarker leftovers from html lexer #WEB-11907 fixed
b84c236: push language levels synchronously for non-refresh vfs events (IDEA-123876)
a32e2eb: code style
a593533: Github: mark invalid password as invalid
10d5070: [git] IDEA-124081 implement IDEA-98189 for clone
a9fa3a2: IDEA-124096 open commit actions popup on ctrl+alt+P
83bc60b: AppCode: Xcode keymap tunes for tool windows +review CR-IC-5162
c976200: Merge remote-tracking branch 'origin/master'
48d5f8c: Refactoring: getImportSourceQName() is used
d324bc0: GeneratedFolder icon
5d90886: Add "try-with-resources" live postfix template
cfec5b0: cleanup
e587000: Platform: bug-typo in DnDSupport
814a79a: Unify prefix calculation rules for live templates
f447531: IDEA-124005 Reading resources out of a source directory doesn't make it a resource directory
Change-Id: Ie2989b1157ae12b92c48d9556db64999ea68e83e
Diffstat (limited to 'java/java-impl/src/com/intellij/codeInsight/template')
31 files changed, 657 insertions, 239 deletions
diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/completion/PostfixTemplateCompletionContributor.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/completion/PostfixTemplateCompletionContributor.java index b6a7b5f687ce..eb6b585f0ec1 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/completion/PostfixTemplateCompletionContributor.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/completion/PostfixTemplateCompletionContributor.java @@ -21,34 +21,19 @@ import com.intellij.codeInsight.template.CustomLiveTemplate; import com.intellij.codeInsight.template.impl.TemplateManagerImpl; import com.intellij.codeInsight.template.postfix.templates.PostfixLiveTemplate; import com.intellij.openapi.editor.Editor; -import com.intellij.patterns.ElementPattern; -import com.intellij.psi.JavaTokenType; -import com.intellij.psi.PsiElement; +import com.intellij.patterns.PlatformPatterns; import com.intellij.psi.PsiFile; -import com.intellij.psi.impl.source.tree.ElementType; -import com.intellij.psi.tree.TokenSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import static com.intellij.patterns.PsiJavaPatterns.psiElement; -import static com.intellij.patterns.StandardPatterns.string; - public class PostfixTemplateCompletionContributor extends CompletionContributor { - private static final TokenSet SUITABLE_ELEMENTS = TokenSet.orSet(ElementType.KEYWORD_BIT_SET, - ElementType.LITERAL_BIT_SET, - TokenSet.create(JavaTokenType.IDENTIFIER)); - public PostfixTemplateCompletionContributor() { - extend(CompletionType.BASIC, identifierAfterDot(), new PostfixTemplatesCompletionProvider()); + extend(CompletionType.BASIC, PlatformPatterns.psiElement(), new PostfixTemplatesCompletionProvider()); } @Nullable - public static PostfixLiveTemplate getPostfixLiveTemplate(@NotNull PsiFile file, @NotNull Editor editor) { + public static PostfixLiveTemplate getPostfixLiveTemplate(@NotNull PsiFile file, @NotNull Editor editor) { PostfixLiveTemplate postfixLiveTemplate = CustomLiveTemplate.EP_NAME.findExtension(PostfixLiveTemplate.class); return postfixLiveTemplate != null && TemplateManagerImpl.isApplicable(postfixLiveTemplate, editor, file) ? postfixLiveTemplate : null; } - - private static ElementPattern<? extends PsiElement> identifierAfterDot() { - return psiElement().withElementType(SUITABLE_ELEMENTS).afterLeaf(psiElement().withText(string().contains("."))); - } } diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/completion/PostfixTemplateLookupElement.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/completion/PostfixTemplateLookupElement.java index 0aeba2baaced..c01f6073845a 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/completion/PostfixTemplateLookupElement.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/completion/PostfixTemplateLookupElement.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,7 +19,8 @@ import com.intellij.codeInsight.lookup.LookupElementPresentation; import com.intellij.codeInsight.template.impl.CustomLiveTemplateLookupElement; import com.intellij.codeInsight.template.postfix.templates.PostfixLiveTemplate; import com.intellij.codeInsight.template.postfix.templates.PostfixTemplate; -import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; public class PostfixTemplateLookupElement extends CustomLiveTemplateLookupElement { @@ -30,7 +31,7 @@ public class PostfixTemplateLookupElement extends CustomLiveTemplateLookupElemen @NotNull PostfixTemplate postfixTemplate, @NotNull String templateKey, boolean sudden) { - super(liveTemplate, templateKey, postfixTemplate.getPresentableName(), postfixTemplate.getDescription(), sudden, true); + super(liveTemplate, templateKey, StringUtil.trimStart(templateKey, "."), postfixTemplate.getDescription(), sudden, true); myTemplate = postfixTemplate; } @@ -43,15 +44,11 @@ public class PostfixTemplateLookupElement extends CustomLiveTemplateLookupElemen public void renderElement(LookupElementPresentation presentation) { super.renderElement(presentation); if (sudden) { - presentation.setTailText(" " + arrow() + " " + myTemplate.getExample()); + presentation.setTailText(" " + UIUtil.rightArrow() + " " + myTemplate.getExample()); } else { presentation.setTypeText(myTemplate.getExample()); presentation.setTypeGrayed(true); } } - - private static String arrow() { - return SystemInfo.isMac ? "→" : "->"; - } } diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/completion/PostfixTemplatesCompletionProvider.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/completion/PostfixTemplatesCompletionProvider.java index db253a6808b1..ec0aead68085 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/completion/PostfixTemplatesCompletionProvider.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/completion/PostfixTemplatesCompletionProvider.java @@ -19,6 +19,7 @@ import com.intellij.codeInsight.completion.CompletionParameters; import com.intellij.codeInsight.completion.CompletionProvider; import com.intellij.codeInsight.completion.CompletionResultSet; import com.intellij.codeInsight.completion.PrefixMatcher; +import com.intellij.codeInsight.template.CustomTemplateCallback; import com.intellij.codeInsight.template.impl.LiveTemplateCompletionContributor; import com.intellij.codeInsight.template.postfix.settings.PostfixTemplatesSettings; import com.intellij.codeInsight.template.postfix.templates.PostfixLiveTemplate; @@ -42,11 +43,12 @@ class PostfixTemplatesCompletionProvider extends CompletionProvider<CompletionPa PostfixLiveTemplate postfixLiveTemplate = getPostfixLiveTemplate(parameters.getOriginalFile(), parameters.getEditor()); if (postfixLiveTemplate != null) { postfixLiveTemplate.addCompletions(parameters, result.withPrefixMatcher(new MyPrefixMatcher(result.getPrefixMatcher().getPrefix()))); - CharSequence documentContent = parameters.getEditor().getDocument().getCharsSequence(); - String possibleKey = postfixLiveTemplate.computeTemplateKeyWithoutContextChecking(documentContent, parameters.getOffset()); + String possibleKey = postfixLiveTemplate.computeTemplateKeyWithoutContextChecking( + new CustomTemplateCallback(parameters.getEditor(), parameters.getOriginalFile(), false)); if (possibleKey != null) { result = result.withPrefixMatcher(possibleKey); - result.restartCompletionOnPrefixChange(StandardPatterns.string().oneOf(postfixLiveTemplate.getAllTemplateKeys())); + result.restartCompletionOnPrefixChange( + StandardPatterns.string().oneOf(postfixLiveTemplate.getAllTemplateKeys(parameters.getOriginalFile(), parameters.getOffset()))); } } } diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/settings/PostfixTemplatesConfigurable.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/settings/PostfixTemplatesConfigurable.java index 75c7d93b515a..90d4eed6e2b9 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/settings/PostfixTemplatesConfigurable.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/settings/PostfixTemplatesConfigurable.java @@ -19,7 +19,11 @@ import com.intellij.application.options.editor.EditorOptionsProvider; import com.intellij.codeInsight.CodeInsightBundle; import com.intellij.codeInsight.template.impl.LiveTemplateCompletionContributor; import com.intellij.codeInsight.template.impl.TemplateSettings; +import com.intellij.codeInsight.template.postfix.templates.LanguagePostfixTemplate; import com.intellij.codeInsight.template.postfix.templates.PostfixTemplate; +import com.intellij.codeInsight.template.postfix.templates.PostfixTemplateProvider; +import com.intellij.lang.LanguageExtensionPoint; +import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.options.Configurable; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.options.SearchableConfigurable; @@ -34,8 +38,9 @@ import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import java.awt.*; -import java.util.*; +import java.util.Comparator; import java.util.List; +import java.util.Map; public class PostfixTemplatesConfigurable implements SearchableConfigurable, EditorOptionsProvider, Configurable.NoScroll { @Nullable @@ -61,7 +66,14 @@ public class PostfixTemplatesConfigurable implements SearchableConfigurable, Edi } myTemplatesSettings = settings; - List<PostfixTemplate> templates = Arrays.asList(PostfixTemplate.EP_NAME.getExtensions()); + + LanguageExtensionPoint[] extensions = new ExtensionPointName<LanguageExtensionPoint>(LanguagePostfixTemplate.EP_NAME).getExtensions(); + + List<PostfixTemplate> templates = ContainerUtil.newArrayList(); + for (LanguageExtensionPoint extension : extensions) { + templates.addAll(((PostfixTemplateProvider)extension.getInstance()).getTemplates()); + } + ContainerUtil.sort(templates, new Comparator<PostfixTemplate>() { @Override public int compare(PostfixTemplate o1, PostfixTemplate o2) { @@ -184,7 +196,7 @@ public class PostfixTemplatesConfigurable implements SearchableConfigurable, Edi private static String shortcutToString(char shortcut) { if (shortcut == TemplateSettings.SPACE_CHAR) { return SPACE; - } + } if (shortcut == TemplateSettings.ENTER_CHAR) { return ENTER; } diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/settings/PostfixTemplatesListPanel.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/settings/PostfixTemplatesListPanel.java index 0c21e04b4c97..4ecc8c0ee4ac 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/settings/PostfixTemplatesListPanel.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/settings/PostfixTemplatesListPanel.java @@ -1,3 +1,18 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.intellij.codeInsight.template.postfix.settings; import com.intellij.codeInsight.template.postfix.templates.PostfixTemplate; @@ -29,7 +44,7 @@ public class PostfixTemplatesListPanel { @NotNull @Override public String fun(@NotNull PostfixTemplate template) { - return template.getKey().replaceFirst(".", ""); + return template.getKey().replaceFirst("\\.", ""); } }; @@ -114,7 +129,9 @@ public class PostfixTemplatesListPanel { public void setState(@NotNull Map<String, Boolean> templatesState) { myTemplatesState.clear(); for (Map.Entry<String, Boolean> entry : templatesState.entrySet()) { - myTemplatesState.put(entry.getKey(), entry.getValue()); + if (!entry.getValue()) { + myTemplatesState.put(entry.getKey(), entry.getValue()); + } } } @@ -172,7 +189,13 @@ public class PostfixTemplatesListPanel { @Override public void setValue(@NotNull PostfixTemplate template, Boolean value) { - myTemplatesState.put(template.getKey(), value); + String key = template.getKey(); + if (value) { + myTemplatesState.remove(key); + } + else { + myTemplatesState.put(key, value); + } } } diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/BooleanPostfixTemplate.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/BooleanPostfixTemplate.java index ab6f90eaa752..f8700bed4644 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/BooleanPostfixTemplate.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/BooleanPostfixTemplate.java @@ -28,7 +28,7 @@ abstract public class BooleanPostfixTemplate extends PostfixTemplate { @Override public boolean isApplicable(@NotNull PsiElement context, @NotNull Document copyDocument, int newOffset) { - PsiExpression topmostExpression = getTopmostExpression(context); + PsiExpression topmostExpression = PostfixTemplatesUtils.getTopmostExpression(context); return topmostExpression != null && PostfixTemplatesUtils.isBoolean(topmostExpression.getType()); } } diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/ElseStatementPostfixTemplate.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/ElseStatementPostfixTemplate.java index 37187455f40c..a148a1bf8896 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/ElseStatementPostfixTemplate.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/ElseStatementPostfixTemplate.java @@ -1,3 +1,18 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.intellij.codeInsight.template.postfix.templates; import com.intellij.codeInsight.CodeInsightServicesUtil; @@ -15,7 +30,7 @@ public class ElseStatementPostfixTemplate extends BooleanPostfixTemplate { @Override public void expand(@NotNull PsiElement context, @NotNull Editor editor) { - PsiExpression expression = getTopmostExpression(context); + PsiExpression expression = PostfixTemplatesUtils.getTopmostExpression(context); assert expression != null; PsiExpression invertedExpression = (PsiExpression)expression.replace(CodeInsightServicesUtil.invertCondition(expression)); TextRange range = PostfixTemplatesUtils.ifStatement(invertedExpression.getProject(), editor, invertedExpression); diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/ExpressionPostfixTemplateWithChooser.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/ExpressionPostfixTemplateWithChooser.java index 8de536551ab5..3bd7928c3e1c 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/ExpressionPostfixTemplateWithChooser.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/ExpressionPostfixTemplateWithChooser.java @@ -42,6 +42,13 @@ public abstract class ExpressionPostfixTemplateWithChooser extends PostfixTempla super(name, description, example); } + protected ExpressionPostfixTemplateWithChooser(@NotNull String name, + @NotNull String key, + @NotNull String description, + @NotNull String example) { + super(name, key, description, example); + } + @Override public boolean isApplicable(@NotNull PsiElement context, @NotNull Document copyDocument, int newOffset) { return !getExpressions(context, copyDocument, newOffset).isEmpty(); @@ -75,20 +82,22 @@ public abstract class ExpressionPostfixTemplateWithChooser extends PostfixTempla } }, new PsiExpressionTrimRenderer.RenderFunction(), - "Expressions", 0, ScopeHighlighter.NATURAL_RANGER); + "Expressions", 0, ScopeHighlighter.NATURAL_RANGER + ); } } @NotNull protected List<PsiExpression> getExpressions(@NotNull PsiElement context, @NotNull Document document, final int offset) { - List<PsiExpression> expressions = ContainerUtil.filter(IntroduceVariableBase.collectExpressions(context.getContainingFile(), document, - Math.max(offset - 1, 0), false), + List<PsiExpression> expressions = ContainerUtil.filter(IntroduceVariableBase.collectExpressions(context.getContainingFile(), document, + Math.max(offset - 1, 0), false), new Condition<PsiExpression>() { @Override public boolean value(PsiExpression expression) { return expression.getTextRange().getEndOffset() == offset; } - }); + } + ); return ContainerUtil.filter(expressions.isEmpty() ? maybeTopmostExpression(context) : expressions, getTypeCondition()); } @@ -100,7 +109,7 @@ public abstract class ExpressionPostfixTemplateWithChooser extends PostfixTempla @NotNull private static List<PsiExpression> maybeTopmostExpression(@NotNull PsiElement context) { - PsiExpression expression = getTopmostExpression(context); + PsiExpression expression = PostfixTemplatesUtils.getTopmostExpression(context); PsiType type = expression != null ? expression.getType() : null; if (type == null || PsiType.VOID.equals(type)) return ContainerUtil.emptyList(); return ContainerUtil.createMaybeSingletonList(expression); diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/ForIndexedPostfixTemplate.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/ForIndexedPostfixTemplate.java index c353c8d39145..bf025d17741f 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/ForIndexedPostfixTemplate.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/ForIndexedPostfixTemplate.java @@ -37,7 +37,7 @@ public abstract class ForIndexedPostfixTemplate extends PostfixTemplate { @Override public boolean isApplicable(@NotNull PsiElement context, @NotNull Document copyDocument, int newOffset) { - PsiExpression expr = getTopmostExpression(context); + PsiExpression expr = PostfixTemplatesUtils.getTopmostExpression(context); return expr != null && (PostfixTemplatesUtils.isNumber(expr.getType()) || PostfixTemplatesUtils.isArray(expr.getType()) || PostfixTemplatesUtils.isIterable(expr.getType())); @@ -45,7 +45,7 @@ public abstract class ForIndexedPostfixTemplate extends PostfixTemplate { @Override public void expand(@NotNull PsiElement context, @NotNull Editor editor) { - PsiExpression expr = getTopmostExpression(context); + PsiExpression expr = PostfixTemplatesUtils.getTopmostExpression(context); if (expr == null) { PostfixTemplatesUtils.showErrorHint(context.getProject(), editor); return; diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/ForeachPostfixTemplate.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/ForeachPostfixTemplate.java index aabf47c8eaa1..72bbb4f703bf 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/ForeachPostfixTemplate.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/ForeachPostfixTemplate.java @@ -37,13 +37,13 @@ public class ForeachPostfixTemplate extends PostfixTemplate { @Override public boolean isApplicable(@NotNull PsiElement context, @NotNull Document copyDocument, int newOffset) { - PsiExpression expr = getTopmostExpression(context); + PsiExpression expr = PostfixTemplatesUtils.getTopmostExpression(context); return expr != null && (PostfixTemplatesUtils.isArray(expr.getType()) || PostfixTemplatesUtils.isIterable(expr.getType())); } @Override public void expand(@NotNull PsiElement context, @NotNull Editor editor) { - PsiExpression expr = getTopmostExpression(context); + PsiExpression expr = PostfixTemplatesUtils.getTopmostExpression(context); if (expr == null) return; Project project = context.getProject(); diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/FormatPostfixTemplate.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/FormatPostfixTemplate.java index 5d99745168a4..a799f39ca466 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/FormatPostfixTemplate.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/FormatPostfixTemplate.java @@ -36,7 +36,7 @@ public class FormatPostfixTemplate extends PostfixTemplate { @Override public boolean isApplicable(@NotNull PsiElement context, @NotNull Document copyDocument, int newOffset) { - PsiExpression expr = getTopmostExpression(context); + PsiExpression expr = PostfixTemplatesUtils.getTopmostExpression(context); PsiType type = expr != null ? expr.getType() : null; return expr != null && type != null && CommonClassNames.JAVA_LANG_STRING.equals(type.getCanonicalText()); } diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/IfStatementPostfixTemplate.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/IfStatementPostfixTemplate.java index 9610ec57c23d..812247428e51 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/IfStatementPostfixTemplate.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/IfStatementPostfixTemplate.java @@ -1,3 +1,18 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.intellij.codeInsight.template.postfix.templates; import com.intellij.codeInsight.template.postfix.util.PostfixTemplatesUtils; @@ -14,7 +29,7 @@ public class IfStatementPostfixTemplate extends BooleanPostfixTemplate { @Override public void expand(@NotNull PsiElement context, @NotNull final Editor editor) { - PsiExpression expression = getTopmostExpression(context); + PsiExpression expression = PostfixTemplatesUtils.getTopmostExpression(context); assert expression != null; TextRange range = PostfixTemplatesUtils.ifStatement(expression.getProject(), editor, expression); if (range != null) { diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/InstanceofExpressionPostfixTemplate.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/InstanceofExpressionPostfixTemplate.java index 8c5b58e79123..1884d099edcd 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/InstanceofExpressionPostfixTemplate.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/InstanceofExpressionPostfixTemplate.java @@ -19,7 +19,6 @@ import com.intellij.codeInsight.guess.GuessManager; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.PsiTypeLookupItem; import com.intellij.codeInsight.template.*; -import com.intellij.codeInsight.template.postfix.util.Aliases; import com.intellij.codeInsight.template.postfix.util.PostfixTemplatesUtils; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; @@ -35,25 +34,30 @@ import org.jetbrains.annotations.NotNull; import java.util.LinkedHashSet; import java.util.Set; -@Aliases(".inst") public class InstanceofExpressionPostfixTemplate extends PostfixTemplate { + public InstanceofExpressionPostfixTemplate() { - super("instanceof", "Surrounds expression with instanceof", "expr instanceof SomeType ? ((SomeType) expr). : null"); + this("instanceof"); + } + + public InstanceofExpressionPostfixTemplate(String alias) { + super(alias, "Surrounds expression with instanceof", "expr instanceof SomeType ? ((SomeType) expr). : null"); } @Override public boolean isApplicable(@NotNull PsiElement context, @NotNull Document copyDocument, int newOffset) { - return PostfixTemplatesUtils.isNotPrimitiveTypeExpression(getTopmostExpression(context)); + return PostfixTemplatesUtils.isNotPrimitiveTypeExpression(PostfixTemplatesUtils.getTopmostExpression(context)); } @Override public void expand(@NotNull PsiElement context, @NotNull Editor editor) { - PsiExpression expression = getTopmostExpression(context); + PsiExpression expression = PostfixTemplatesUtils.getTopmostExpression(context); if (!PostfixTemplatesUtils.isNotPrimitiveTypeExpression(expression)) return; surroundExpression(context.getProject(), editor, expression); } - private static void surroundExpression(@NotNull Project project, @NotNull Editor editor, @NotNull PsiExpression expr) throws IncorrectOperationException { + private static void surroundExpression(@NotNull Project project, @NotNull Editor editor, @NotNull PsiExpression expr) + throws IncorrectOperationException { assert expr.isValid(); PsiType[] types = GuessManager.getInstance(project).guessTypeToCast(expr); final boolean parenthesesNeeded = expr instanceof PsiPolyadicExpression || diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/JavaPostfixTemplateProvider.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/JavaPostfixTemplateProvider.java new file mode 100644 index 000000000000..2ee071b98559 --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/JavaPostfixTemplateProvider.java @@ -0,0 +1,130 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInsight.template.postfix.templates; + +import com.intellij.codeInsight.completion.CompletionInitializationContext; +import com.intellij.codeInsight.completion.JavaCompletionContributor; +import com.intellij.codeInsight.template.CustomTemplateCallback; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.command.CommandProcessor; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.util.Ref; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.Set; + + +public class JavaPostfixTemplateProvider implements PostfixTemplateProvider { + private final Set<PostfixTemplate> templates; + + + public JavaPostfixTemplateProvider() { + templates = ContainerUtil.newHashSet(new AssertStatementPostfixTemplate(), + new CastExpressionPostfixTemplate(), + new ElseStatementPostfixTemplate(), + new ForAscendingPostfixTemplate(), + new ForDescendingPostfixTemplate(), + new ForeachPostfixTemplate(), + new FormatPostfixTemplate(), + new IfStatementPostfixTemplate(), + new InstanceofExpressionPostfixTemplate(), + new InstanceofExpressionPostfixTemplate("inst"), + new IntroduceFieldPostfixTemplate(), + new IntroduceVariablePostfixTemplate(), + new IsNullCheckPostfixTemplate(), + new NotExpressionPostfixTemplate(), + new NotExpressionPostfixTemplate("!"), + new NotNullCheckPostfixTemplate(), + new NotNullCheckPostfixTemplate("nn"), + new ParenthesizedExpressionPostfixTemplate(), + new ReturnStatementPostfixTemplate(), + new SoutPostfixTemplate(), + new SwitchStatementPostfixTemplate(), + new SynchronizedStatementPostfixTemplate(), + new ThrowExceptionPostfixTemplate(), + new TryStatementPostfixTemplate(), + new TryWithResourcesPostfixTemplate(), + new WhileStatementPostfixTemplate()); + } + + @NotNull + @Override + public Set<PostfixTemplate> getTemplates() { + return templates; + } + + @Override + public boolean isTerminalSymbol(char currentChar) { + return currentChar == '.' || currentChar == '!'; + } + + @NotNull + @Override + public PsiElement preExpand(@NotNull Editor editor, @NotNull PsiElement context, int offset, @NotNull final String key) { + + return addSemicolonIfNeeded(editor, editor.getDocument(), context, offset - key.length()); + } + + @NotNull + @Override + public PsiFile preCheck(@NotNull Editor editor, @NotNull PsiFile copyFile, int currentOffset) { + Document document = copyFile.getViewProvider().getDocument(); + assert document != null; + CharSequence sequence = document.getCharsSequence(); + StringBuilder fileContentWithoutKey = new StringBuilder(sequence); + if (isSemicolonNeeded(copyFile, editor)) { + fileContentWithoutKey.insert(currentOffset, ';'); + copyFile = PostfixLiveTemplate.copyFile(copyFile, fileContentWithoutKey); + } + + return copyFile; + } + + @NotNull + private static PsiElement addSemicolonIfNeeded(@NotNull final Editor editor, + @NotNull final Document document, + @NotNull final PsiElement context, + final int offset) { + ApplicationManager.getApplication().assertIsDispatchThread(); + + final Ref<PsiElement> newContext = Ref.create(context); + final PsiFile file = context.getContainingFile(); + if (isSemicolonNeeded(file, editor)) { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + CommandProcessor.getInstance().runUndoTransparentAction(new Runnable() { + public void run() { + document.insertString(offset, ";"); + PsiDocumentManager.getInstance(context.getProject()).commitDocument(document); + newContext.set(CustomTemplateCallback.getContext(file, offset - 1)); + } + }); + } + }); + } + return newContext.get(); + } + + private static boolean isSemicolonNeeded(@NotNull PsiFile file, @NotNull Editor editor) { + return JavaCompletionContributor.semicolonNeeded(editor, file, CompletionInitializationContext.calcStartOffset(editor)); + } +} diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/LanguagePostfixTemplate.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/LanguagePostfixTemplate.java new file mode 100644 index 000000000000..2360d9721ab8 --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/LanguagePostfixTemplate.java @@ -0,0 +1,27 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInsight.template.postfix.templates; + +import com.intellij.lang.LanguageExtension; + +public class LanguagePostfixTemplate extends LanguageExtension<PostfixTemplateProvider> { + public static final LanguagePostfixTemplate LANG_EP = new LanguagePostfixTemplate(); + public static final String EP_NAME = "com.intellij.codeInsight.template.postfixTemplateProvider"; + + private LanguagePostfixTemplate() { + super(EP_NAME); + } +} diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/NonVoidPostfixTemplate.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/NonVoidPostfixTemplate.java index 906a943e15e6..09757e53e826 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/NonVoidPostfixTemplate.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/NonVoidPostfixTemplate.java @@ -28,7 +28,7 @@ abstract public class NonVoidPostfixTemplate extends PostfixTemplate { @Override public boolean isApplicable(@NotNull PsiElement context, @NotNull Document copyDocument, int newOffset) { - PsiExpression expr = getTopmostExpression(context); + PsiExpression expr = PostfixTemplatesUtils.getTopmostExpression(context); return expr != null && PostfixTemplatesUtils.isNonVoid(expr.getType()); } } diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/NotExpressionPostfixTemplate.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/NotExpressionPostfixTemplate.java index 9900c0a4c57d..e2e3db8b8be6 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/NotExpressionPostfixTemplate.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/NotExpressionPostfixTemplate.java @@ -16,14 +16,12 @@ package com.intellij.codeInsight.template.postfix.templates; import com.intellij.codeInsight.CodeInsightServicesUtil; -import com.intellij.codeInsight.template.postfix.util.Aliases; import com.intellij.codeInsight.template.postfix.util.PostfixTemplatesUtils; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.util.Condition; import com.intellij.psi.PsiExpression; import org.jetbrains.annotations.NotNull; -@Aliases("!") public class NotExpressionPostfixTemplate extends ExpressionPostfixTemplateWithChooser { private static final Condition<PsiExpression> BOOLEAN_TYPE_CONDITION = new Condition<PsiExpression>() { @Override @@ -36,6 +34,10 @@ public class NotExpressionPostfixTemplate extends ExpressionPostfixTemplateWithC super("not", "Negates boolean expression", "!expr"); } + public NotExpressionPostfixTemplate(String alias) { + super(alias, alias, "Negates boolean expression", "!expr"); + } + @Override protected void doIt(@NotNull Editor editor, @NotNull PsiExpression expression) { expression.replace(CodeInsightServicesUtil.invertCondition(expression)); diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/NotNullCheckPostfixTemplate.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/NotNullCheckPostfixTemplate.java index e4bde3241fee..ea2480f86a0e 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/NotNullCheckPostfixTemplate.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/NotNullCheckPostfixTemplate.java @@ -1,12 +1,30 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.intellij.codeInsight.template.postfix.templates; -import com.intellij.codeInsight.template.postfix.util.Aliases; import org.jetbrains.annotations.NotNull; -@Aliases(".nn") public class NotNullCheckPostfixTemplate extends NullCheckPostfixTemplate { + public NotNullCheckPostfixTemplate() { - super("notnull", "Checks expression to be not-null", "if (expr != null)"); + this("notnull"); + } + + public NotNullCheckPostfixTemplate(String alias) { + super(alias, "Checks expression to be not-null", "if (expr != null)"); } @NotNull diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/NullCheckPostfixTemplate.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/NullCheckPostfixTemplate.java index 6a9b6769e548..c7ad21b2fb11 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/NullCheckPostfixTemplate.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/NullCheckPostfixTemplate.java @@ -39,12 +39,12 @@ public abstract class NullCheckPostfixTemplate extends PostfixTemplate { @Override public boolean isApplicable(@NotNull PsiElement context, @NotNull Document copyDocument, int newOffset) { - return PostfixTemplatesUtils.isNotPrimitiveTypeExpression(getTopmostExpression(context)); + return PostfixTemplatesUtils.isNotPrimitiveTypeExpression(PostfixTemplatesUtils.getTopmostExpression(context)); } @Override public void expand(@NotNull PsiElement context, @NotNull Editor editor) { - PsiExpression expr = getTopmostExpression(context); + PsiExpression expr = PostfixTemplatesUtils.getTopmostExpression(context); if (!PostfixTemplatesUtils.isNotPrimitiveTypeExpression(expr)) return; Project project = expr.getProject(); diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixLiveTemplate.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixLiveTemplate.java index a165dcb0ed17..8fd2e725d560 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixLiveTemplate.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixLiveTemplate.java @@ -15,17 +15,15 @@ */ package com.intellij.codeInsight.template.postfix.templates; -import com.intellij.codeInsight.completion.CompletionInitializationContext; -import com.intellij.codeInsight.completion.JavaCompletionContributor; +import com.google.common.collect.Sets; import com.intellij.codeInsight.template.CustomLiveTemplateBase; import com.intellij.codeInsight.template.CustomTemplateCallback; import com.intellij.codeInsight.template.impl.CustomLiveTemplateLookupElement; import com.intellij.codeInsight.template.impl.TemplateSettings; import com.intellij.codeInsight.template.postfix.completion.PostfixTemplateLookupElement; import com.intellij.codeInsight.template.postfix.settings.PostfixTemplatesSettings; -import com.intellij.codeInsight.template.postfix.util.Aliases; import com.intellij.featureStatistics.FeatureUsageTracker; -import com.intellij.lang.java.JavaLanguage; +import com.intellij.lang.Language; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.command.undo.UndoConstants; @@ -33,7 +31,6 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.util.Condition; -import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDocumentManager; @@ -42,71 +39,41 @@ import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFileFactory; import com.intellij.psi.util.PsiUtilCore; import com.intellij.util.containers.ContainerUtil; -import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; -import java.util.HashMap; -import java.util.Map; import java.util.Set; +import static com.intellij.codeInsight.template.postfix.templates.LanguagePostfixTemplate.LANG_EP; + public class PostfixLiveTemplate extends CustomLiveTemplateBase { public static final String POSTFIX_TEMPLATE_ID = "POSTFIX_TEMPLATE_ID"; - private static final Logger LOG = Logger.getInstance(PostfixLiveTemplate.class); - private final HashMap<String, PostfixTemplate> myTemplates = ContainerUtil.newHashMap(); - - public PostfixLiveTemplate() { - for (PostfixTemplate template : PostfixTemplate.EP_NAME.getExtensions()) { - register(template.getKey(), template); - Aliases aliases = template.getClass().getAnnotation(Aliases.class); - if (aliases != null) { - for (String key : aliases.value()) { - register(key, template); - } - } - } - } - private void register(@NotNull String key, @NotNull PostfixTemplate template) { - PostfixTemplate registered = myTemplates.put(key, template); - if (registered != null) { - LOG.error("Can't register postfix template. Duplicated key: " + template.getKey()); + @NotNull + public Set<String> getAllTemplateKeys(PsiFile file, int offset) { + Set<String> keys = Sets.newHashSet(); + Language language = PsiUtilCore.getLanguageAtOffset(file, offset); + for (PostfixTemplateProvider provider : LANG_EP.allForLanguage(language)) { + keys.addAll(getKeys(provider)); } + return keys; } @Nullable - @Override - public String computeTemplateKey(@NotNull CustomTemplateCallback callback) { - Editor editor = callback.getEditor(); - String key = computeTemplateKeyWithoutContextChecking(editor.getDocument().getCharsSequence(), editor.getCaretModel().getOffset()); - if (key == null) return null; - return isApplicableTemplate(getTemplateByKey(key), key, callback.getContext().getContainingFile(), editor) ? key : null; - } - - @Nullable - @Override - public String computeTemplateKeyWithoutContextChecking(@NotNull CustomTemplateCallback callback) { - Editor editor = callback.getEditor(); - return computeTemplateKeyWithoutContextChecking(editor.getDocument().getCharsSequence(), editor.getCaretModel().getOffset()); - } - - @Override - public boolean supportsMultiCaret() { - return false; - } - - @Nullable - public String computeTemplateKeyWithoutContextChecking(@NotNull CharSequence documentContent, int currentOffset) { + private static String computeTemplateKeyWithoutContextChecking(@NotNull PostfixTemplateProvider provider, + @NotNull CharSequence documentContent, + int currentOffset) { int startOffset = currentOffset; if (documentContent.length() < startOffset) { return null; } + while (startOffset > 0) { char currentChar = documentContent.charAt(startOffset - 1); if (!Character.isJavaIdentifierPart(currentChar)) { - if (currentChar != '.' && currentChar != '!') { + if (!provider.isTerminalSymbol(currentChar)) { return null; } startOffset--; @@ -117,23 +84,64 @@ public class PostfixLiveTemplate extends CustomLiveTemplateBase { return String.valueOf(documentContent.subSequence(startOffset, currentOffset)); } + @Nullable + @Override + public String computeTemplateKey(@NotNull CustomTemplateCallback callback) { + Editor editor = callback.getEditor(); + CharSequence charsSequence = editor.getDocument().getCharsSequence(); + int offset = editor.getCaretModel().getOffset(); + for (PostfixTemplateProvider provider : LANG_EP.allForLanguage(getLanguage(callback))) { + String key = computeTemplateKeyWithoutContextChecking(provider, charsSequence, offset); + if (key != null && isApplicableTemplate(provider, key, callback.getFile(), editor)) { + return key; + } + } + return null; + } + + @Nullable + @Override + public String computeTemplateKeyWithoutContextChecking(@NotNull CustomTemplateCallback callback) { + Editor editor = callback.getEditor(); + int currentOffset = editor.getCaretModel().getOffset(); + for (PostfixTemplateProvider provider : LANG_EP.allForLanguage(getLanguage(callback))) { + String key = computeTemplateKeyWithoutContextChecking(provider, editor.getDocument().getCharsSequence(), currentOffset); + if (key != null) return key; + } + return null; + } + + @Override + public boolean supportsMultiCaret() { + return false; + } + @Override public void expand(@NotNull final String key, @NotNull final CustomTemplateCallback callback) { ApplicationManager.getApplication().assertIsDispatchThread(); - FeatureUsageTracker.getInstance().triggerFeatureUsed("editing.completion.postfix"); - final PostfixTemplate template = getTemplateByKey(key); - final Editor editor = callback.getEditor(); - final PsiFile file = callback.getContext().getContainingFile(); - if (isApplicableTemplate(template, key, file, editor)) { - int currentOffset = editor.getCaretModel().getOffset(); - PsiElement newContext = deleteTemplateKey(file, editor.getDocument(), currentOffset, key); - newContext = addSemicolonIfNeeded(editor, editor.getDocument(), newContext, currentOffset - key.length()); - expandTemplate(template, editor, newContext); + Editor editor = callback.getEditor(); + for (PostfixTemplateProvider provider : LANG_EP.allForLanguage(getLanguage(callback))) { + PostfixTemplate postfixTemplate = getTemplate(provider, key); + if (postfixTemplate != null) { + final PsiFile file = callback.getContext().getContainingFile(); + if (isApplicableTemplate(provider, key, file, editor)) { + int currentOffset = editor.getCaretModel().getOffset(); + PsiElement newContext = deleteTemplateKey(file, editor.getDocument(), currentOffset, key); + newContext = provider.preExpand(editor, newContext, currentOffset, key); + expandTemplate(postfixTemplate, editor, newContext); + } + // don't care about errors in multiCaret mode + else if (editor.getCaretModel().getAllCarets().size() == 1) { + LOG.error("Template not found by key: " + key); + } + return; + } } + // don't care about errors in multiCaret mode - else if (editor.getCaretModel().getAllCarets().size() == 1) { + if (editor.getCaretModel().getAllCarets().size() == 1) { LOG.error("Template not found by key: " + key); } } @@ -141,11 +149,16 @@ public class PostfixLiveTemplate extends CustomLiveTemplateBase { @Override public boolean isApplicable(PsiFile file, int offset, boolean wrapping) { PostfixTemplatesSettings settings = PostfixTemplatesSettings.getInstance(); - if (wrapping || file == null || settings == null || !settings.isPostfixTemplatesEnabled() || - PsiUtilCore.getLanguageAtOffset(file, offset) != JavaLanguage.INSTANCE) { + if (wrapping || file == null || settings == null || !settings.isPostfixTemplatesEnabled()) { return false; } - return StringUtil.isNotEmpty(computeTemplateKeyWithoutContextChecking(file.getText(), offset + 1)); + Language language = PsiUtilCore.getLanguageAtOffset(file, offset); + for (PostfixTemplateProvider provider : LANG_EP.allForLanguage(language)) { + if (StringUtil.isNotEmpty(computeTemplateKeyWithoutContextChecking(provider, file.getText(), offset + 1))) { + return true; + } + } + return false; } @Override @@ -177,30 +190,24 @@ public class PostfixLiveTemplate extends CustomLiveTemplateBase { @NotNull @Override - public Collection<? extends CustomLiveTemplateLookupElement> getLookupElements(@NotNull PsiFile file, @NotNull Editor editor, int offset) { - String key = computeTemplateKeyWithoutContextChecking(editor.getDocument().getCharsSequence(), offset); - if (key != null && editor.getCaretModel().getCaretCount() == 1) { - Map<String, CustomLiveTemplateLookupElement> result = ContainerUtil.newHashMap(); - Condition<PostfixTemplate> isApplicationTemplateFunction = createIsApplicationTemplateFunction(key, file, editor); - for (Map.Entry<String, PostfixTemplate> entry : myTemplates.entrySet()) { - PostfixTemplate postfixTemplate = entry.getValue(); - if (entry.getKey().startsWith(key) && isApplicationTemplateFunction.value(postfixTemplate)) { - result.put(postfixTemplate.getKey(), new PostfixTemplateLookupElement(this, postfixTemplate, entry.getKey(), false)); + public Collection<? extends CustomLiveTemplateLookupElement> getLookupElements(@NotNull PsiFile file, + @NotNull Editor editor, + int offset) { + Collection<CustomLiveTemplateLookupElement> result = ContainerUtil.newHashSet(); + CustomTemplateCallback callback = new CustomTemplateCallback(editor, file, false); + for (PostfixTemplateProvider provider : LANG_EP.allForLanguage(getLanguage(callback))) { + String key = computeTemplateKeyWithoutContextChecking(callback); + if (key != null && editor.getCaretModel().getCaretCount() == 1) { + Condition<PostfixTemplate> isApplicationTemplateFunction = createIsApplicationTemplateFunction(provider, key, file, editor); + for (PostfixTemplate postfixTemplate : provider.getTemplates()) { + if (isApplicationTemplateFunction.value(postfixTemplate)) { + result.add(new PostfixTemplateLookupElement(this, postfixTemplate, postfixTemplate.getKey(), false)); + } } } - return result.values(); } - return super.getLookupElements(file, editor, offset); - } - - @NotNull - public Set<String> getAllTemplateKeys() { - return myTemplates.keySet(); - } - @Nullable - public PostfixTemplate getTemplateByKey(@NotNull String key) { - return myTemplates.get(key); + return result; } private static void expandTemplate(@NotNull final PostfixTemplate template, @@ -218,16 +225,36 @@ public class PostfixLiveTemplate extends CustomLiveTemplateBase { }); } - @Contract("null, _, _, _ -> false") - private static boolean isApplicableTemplate(@Nullable PostfixTemplate template, @NotNull String key, @NotNull PsiFile file, @NotNull Editor editor) { - return createIsApplicationTemplateFunction(key, file, editor).value(template); + + @NotNull + private static PsiElement deleteTemplateKey(@NotNull final PsiFile file, + @NotNull final Document document, + final int currentOffset, + @NotNull final String key) { + ApplicationManager.getApplication().assertIsDispatchThread(); + + final int startOffset = currentOffset - key.length(); + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + CommandProcessor.getInstance().runUndoTransparentAction(new Runnable() { + public void run() { + document.deleteString(startOffset, currentOffset); + PsiDocumentManager.getInstance(file.getProject()).commitDocument(document); + } + }); + } + }); + return CustomTemplateCallback.getContext(file, startOffset > 0 ? startOffset - 1 : startOffset); } - - private static Condition<PostfixTemplate> createIsApplicationTemplateFunction(@NotNull String key, @NotNull PsiFile file, @NotNull Editor editor) { + + private static Condition<PostfixTemplate> createIsApplicationTemplateFunction(@NotNull PostfixTemplateProvider provider, + @NotNull String key, + @NotNull PsiFile file, + @NotNull Editor editor) { int currentOffset = editor.getCaretModel().getOffset(); final int newOffset = currentOffset - key.length(); CharSequence fileContent = editor.getDocument().getCharsSequence(); - StringBuilder fileContentWithoutKey = new StringBuilder(); fileContentWithoutKey.append(fileContent.subSequence(0, newOffset)); fileContentWithoutKey.append(fileContent.subSequence(currentOffset, fileContent.length())); @@ -238,14 +265,11 @@ public class PostfixLiveTemplate extends CustomLiveTemplateBase { return Condition.FALSE; } - if (isSemicolonNeeded(copyFile, editor)) { - fileContentWithoutKey.insert(newOffset, ';'); - copyFile = copyFile(file, fileContentWithoutKey); - copyDocument = copyFile.getViewProvider().getDocument(); - if (copyDocument == null) { - //noinspection unchecked - return Condition.FALSE; - } + copyFile = provider.preCheck(editor, copyFile, newOffset); + copyDocument = copyFile.getViewProvider().getDocument(); + if (copyDocument == null) { + //noinspection unchecked + return Condition.FALSE; } final PsiElement context = CustomTemplateCallback.getContext(copyFile, newOffset > 0 ? newOffset - 1 : newOffset); @@ -258,8 +282,9 @@ public class PostfixLiveTemplate extends CustomLiveTemplateBase { }; } + @NotNull - private static PsiFile copyFile(@NotNull PsiFile file, @NotNull StringBuilder fileContentWithoutKey) { + public static PsiFile copyFile(@NotNull PsiFile file, @NotNull StringBuilder fileContentWithoutKey) { final PsiFileFactory psiFileFactory = PsiFileFactory.getInstance(file.getProject()); PsiFile copy = psiFileFactory.createFileFromText(file.getName(), file.getFileType(), fileContentWithoutKey); VirtualFile vFile = copy.getVirtualFile(); @@ -269,55 +294,35 @@ public class PostfixLiveTemplate extends CustomLiveTemplateBase { return copy; } - @NotNull - private static PsiElement deleteTemplateKey(@NotNull final PsiFile file, - @NotNull final Document document, - final int currentOffset, - @NotNull final String key) { - ApplicationManager.getApplication().assertIsDispatchThread(); - - final int startOffset = currentOffset - key.length(); - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - CommandProcessor.getInstance().runUndoTransparentAction(new Runnable() { - public void run() { - document.deleteString(startOffset, currentOffset); - PsiDocumentManager.getInstance(file.getProject()).commitDocument(document); - } - }); - } - }); - return CustomTemplateCallback.getContext(file, startOffset > 0 ? startOffset - 1 : startOffset); + public static boolean isApplicableTemplate(@NotNull PostfixTemplateProvider provider, + @NotNull String key, + @NotNull PsiFile file, + @NotNull Editor editor) { + return createIsApplicationTemplateFunction(provider, key, file, editor).value(getTemplate(provider, key)); } + @NotNull - private static PsiElement addSemicolonIfNeeded(@NotNull final Editor editor, - @NotNull final Document document, - @NotNull final PsiElement context, - final int offset) { - ApplicationManager.getApplication().assertIsDispatchThread(); + private static Set<String> getKeys(@NotNull PostfixTemplateProvider provider) { + Set<String> result = ContainerUtil.newHashSet(); + for (PostfixTemplate template : provider.getTemplates()) { + result.add(template.getKey()); + } - final Ref<PsiElement> newContext = Ref.create(context); - final PsiFile file = context.getContainingFile(); - if (isSemicolonNeeded(file, editor)) { - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - CommandProcessor.getInstance().runUndoTransparentAction(new Runnable() { - public void run() { - document.insertString(offset, ";"); - PsiDocumentManager.getInstance(context.getProject()).commitDocument(document); - newContext.set(CustomTemplateCallback.getContext(file, offset - 1)); - } - }); - } - }); + return result; + } + + @Nullable + private static PostfixTemplate getTemplate(@NotNull PostfixTemplateProvider provider, @Nullable String key) { + for (PostfixTemplate template : provider.getTemplates()) { + if (template.getKey().equals(key)) { + return template; + } } - return newContext.get(); + return null; } - private static boolean isSemicolonNeeded(@NotNull PsiFile file, @NotNull Editor editor) { - return JavaCompletionContributor.semicolonNeeded(editor, file, CompletionInitializationContext.calcStartOffset(editor)); + private static Language getLanguage(@NotNull CustomTemplateCallback callback) { + return callback.getContext().getLanguage(); } } diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplate.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplate.java index 80a68ab26650..747366d1832f 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplate.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplate.java @@ -18,13 +18,8 @@ package com.intellij.codeInsight.template.postfix.templates; import com.intellij.codeInsight.template.postfix.settings.PostfixTemplatesSettings; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiExpression; -import com.intellij.psi.PsiExpressionStatement; -import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; public abstract class PostfixTemplate { @NotNull private final String myPresentableName; @@ -32,9 +27,6 @@ public abstract class PostfixTemplate { @NotNull private final String myDescription; @NotNull private final String myExample; - @NotNull - public static final ExtensionPointName<PostfixTemplate> EP_NAME = ExtensionPointName.create("com.intellij.postfixTemplate"); - protected PostfixTemplate(@NotNull String name, @NotNull String description, @NotNull String example) { this(name, "." + name, description, example); } @@ -71,12 +63,6 @@ public abstract class PostfixTemplate { return settings != null && settings.isPostfixTemplatesEnabled() && settings.isTemplateEnabled(this); } - @Nullable - public static PsiExpression getTopmostExpression(PsiElement context) { - PsiExpressionStatement statement = PsiTreeUtil.getNonStrictParentOfType(context, PsiExpressionStatement.class); - return statement != null ? PsiTreeUtil.getChildOfType(statement, PsiExpression.class) : null; - } - public abstract boolean isApplicable(@NotNull PsiElement context, @NotNull Document copyDocument, int newOffset); public abstract void expand(@NotNull PsiElement context, @NotNull Editor editor); diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplateProvider.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplateProvider.java new file mode 100644 index 000000000000..eeea9a19f62e --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplateProvider.java @@ -0,0 +1,52 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInsight.template.postfix.templates; + + +import com.intellij.openapi.editor.Editor; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import org.jetbrains.annotations.NotNull; + +import java.util.Set; + +public interface PostfixTemplateProvider { + + /** + * Return all templates registered in the provider + */ + @NotNull + Set<PostfixTemplate> getTemplates(); + + /** + * Check symbol can separate template keys + */ + boolean isTerminalSymbol(char currentChar); + + /** + * Prepare original file content for template expanding + * Return context after transformation + */ + @NotNull + PsiElement preExpand(@NotNull Editor editor, @NotNull PsiElement context, int offset, @NotNull String key); + + /** + * Do some actions with the file content before check applicable. + * Return copyFile or another copy of file after processing + */ + @NotNull + PsiFile preCheck(@NotNull Editor editor, @NotNull PsiFile copyFile, int currentOffset); +} diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/ReturnStatementPostfixTemplate.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/ReturnStatementPostfixTemplate.java index 277a6f2c0d30..afc3236b8319 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/ReturnStatementPostfixTemplate.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/ReturnStatementPostfixTemplate.java @@ -15,6 +15,7 @@ */ package com.intellij.codeInsight.template.postfix.templates; +import com.intellij.codeInsight.template.postfix.util.PostfixTemplatesUtils; import com.intellij.openapi.editor.Editor; import com.intellij.psi.*; import org.jetbrains.annotations.NotNull; @@ -26,7 +27,7 @@ public class ReturnStatementPostfixTemplate extends NonVoidPostfixTemplate { @Override public void expand(@NotNull PsiElement context, @NotNull Editor editor) { - PsiExpression expr = getTopmostExpression(context); + PsiExpression expr = PostfixTemplatesUtils.getTopmostExpression(context); PsiElement parent = expr != null ? expr.getParent() : null; if (!(parent instanceof PsiExpressionStatement)) return; PsiElementFactory factory = JavaPsiFacade.getInstance(expr.getProject()).getElementFactory(); diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/StatementPostfixTemplateBase.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/StatementPostfixTemplateBase.java index 4449c551dbfc..8f26ec08fae0 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/StatementPostfixTemplateBase.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/StatementPostfixTemplateBase.java @@ -1,6 +1,22 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.intellij.codeInsight.template.postfix.templates; import com.intellij.codeInsight.CodeInsightUtilCore; +import com.intellij.codeInsight.template.postfix.util.PostfixTemplatesUtils; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; @@ -17,7 +33,7 @@ public abstract class StatementPostfixTemplateBase extends PostfixTemplate { } protected void surroundWith(PsiElement context, Editor editor, String text) { - PsiExpression expr = getTopmostExpression(context); + PsiExpression expr = PostfixTemplatesUtils.getTopmostExpression(context); PsiElement parent = expr != null ? expr.getParent() : null; if (!(parent instanceof PsiExpressionStatement)) return; diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/SwitchStatementPostfixTemplate.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/SwitchStatementPostfixTemplate.java index 880c0455775b..20a7299af8ec 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/SwitchStatementPostfixTemplate.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/SwitchStatementPostfixTemplate.java @@ -15,6 +15,7 @@ */ package com.intellij.codeInsight.template.postfix.templates; +import com.intellij.codeInsight.template.postfix.util.PostfixTemplatesUtils; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; @@ -51,7 +52,7 @@ public class SwitchStatementPostfixTemplate extends StatementPostfixTemplateBase @Override public boolean isApplicable(@NotNull PsiElement context, @NotNull Document copyDocument, int newOffset) { - PsiExpression expr = getTopmostExpression(context); + PsiExpression expr = PostfixTemplatesUtils.getTopmostExpression(context); return expr != null && isSwitchCompatibleType(expr.getType(), context); } diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/SynchronizedStatementPostfixTemplate.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/SynchronizedStatementPostfixTemplate.java index 044dadc81c8c..eb46ba9f0540 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/SynchronizedStatementPostfixTemplate.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/SynchronizedStatementPostfixTemplate.java @@ -15,6 +15,7 @@ */ package com.intellij.codeInsight.template.postfix.templates; +import com.intellij.codeInsight.template.postfix.util.PostfixTemplatesUtils; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.psi.PsiElement; @@ -30,7 +31,7 @@ public class SynchronizedStatementPostfixTemplate extends StatementPostfixTempla @Override public boolean isApplicable(@NotNull PsiElement context, @NotNull Document copyDocument, int newOffset) { - PsiExpression expression = getTopmostExpression(context); + PsiExpression expression = PostfixTemplatesUtils.getTopmostExpression(context); PsiType type = expression != null ? expression.getType() : null; return type != null && !(type instanceof PsiPrimitiveType); } diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/ThrowExceptionPostfixTemplate.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/ThrowExceptionPostfixTemplate.java index b1bb654d6dff..7b0aaa00f4c1 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/ThrowExceptionPostfixTemplate.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/ThrowExceptionPostfixTemplate.java @@ -29,7 +29,7 @@ public class ThrowExceptionPostfixTemplate extends PostfixTemplate { @Override public boolean isApplicable(@NotNull PsiElement context, @NotNull Document copyDocument, int newOffset) { - PsiExpression expression = getTopmostExpression(context); + PsiExpression expression = PostfixTemplatesUtils.getTopmostExpression(context); return expression != null && PostfixTemplatesUtils.isThrowable(expression.getType()); } diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/TryWithResourcesPostfixTemplate.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/TryWithResourcesPostfixTemplate.java new file mode 100644 index 000000000000..843c19e072c0 --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/TryWithResourcesPostfixTemplate.java @@ -0,0 +1,110 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInsight.template.postfix.templates; + +import com.intellij.codeInsight.ExceptionUtil; +import com.intellij.codeInsight.intention.impl.TypeExpression; +import com.intellij.codeInsight.template.Template; +import com.intellij.codeInsight.template.TemplateManager; +import com.intellij.codeInsight.template.impl.MacroCallNode; +import com.intellij.codeInsight.template.impl.TextExpression; +import com.intellij.codeInsight.template.macro.SuggestVariableNameMacro; +import com.intellij.codeInsight.template.postfix.util.PostfixTemplatesUtils; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.search.ProjectScope; +import com.intellij.psi.util.InheritanceUtil; +import com.intellij.psi.util.PsiUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.Collections; + + +public class TryWithResourcesPostfixTemplate extends PostfixTemplate { + protected TryWithResourcesPostfixTemplate() { + super("twr", "Description", "Example"); + } + + @Override + public boolean isApplicable(@NotNull PsiElement element, @NotNull Document copyDocument, int newOffset) { + if (!PsiUtil.isLanguageLevel7OrHigher(element)) return false; + + PsiExpression initializer = PostfixTemplatesUtils.getTopmostExpression(element); + + if (initializer == null) return false; + + final PsiType type = initializer.getType(); + if (!(type instanceof PsiClassType)) return false; + final PsiClass aClass = ((PsiClassType)type).resolve(); + Project project = element.getProject(); + final JavaPsiFacade facade = JavaPsiFacade.getInstance(project); + final PsiClass autoCloseable = facade.findClass(CommonClassNames.JAVA_LANG_AUTO_CLOSEABLE, ProjectScope.getLibrariesScope(project)); + if (!InheritanceUtil.isInheritorOrSelf(aClass, autoCloseable, true)) return false; + + return true; + } + + @Override + public void expand(@NotNull PsiElement context, @NotNull Editor editor) { + PsiExpression expression = PostfixTemplatesUtils.getTopmostExpression(context); + assert expression != null; + + Project project = context.getProject(); + + editor.getDocument().deleteString(expression.getTextRange().getStartOffset(), expression.getTextRange().getEndOffset()); + + TemplateManager manager = TemplateManager.getInstance(project); + Template template = manager.createTemplate("", ""); + template.setToReformat(true); + template.addTextSegment("try ("); + MacroCallNode name = new MacroCallNode(new SuggestVariableNameMacro()); + + template.addVariable("type", new TypeExpression(project, new PsiType[]{expression.getType()}), false); + template.addTextSegment(" "); + template.addVariable("name", name, name, true); + template.addTextSegment(" = "); + template.addVariable("variable", new TextExpression(expression.getText()), false); + template.addTextSegment(") {\n"); + template.addEndVariable(); + template.addTextSegment("\n}"); + + Collection<PsiClassType> unhandled = getUnhandled(expression); + for (PsiClassType exception : unhandled) { + MacroCallNode variable = new MacroCallNode(new SuggestVariableNameMacro()); + template.addTextSegment("catch("); + template.addVariable("type " + exception.getClassName(), new TypeExpression(project, new PsiType[]{exception}), false); + template.addTextSegment(" "); + template.addVariable("name " + exception.getClassName(), variable, variable, false); + template.addTextSegment(") {}"); + } + + manager.startTemplate(editor, template); + } + + @NotNull + private static Collection<PsiClassType> getUnhandled(@NotNull PsiExpression expression) { + assert expression.getType() != null; + PsiMethod methodCloser = PsiUtil.getResourceCloserMethodForType((PsiClassType)expression.getType(), expression.getProject()); + PsiSubstitutor substitutor = PsiUtil.resolveGenericsClassInType(expression.getType()).getSubstitutor(); + + return methodCloser != null + ? ExceptionUtil.getUnhandledExceptions(methodCloser, expression, null, substitutor) + : Collections.<PsiClassType>emptyList(); + } +} diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/WhileStatementPostfixTemplate.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/WhileStatementPostfixTemplate.java index 03d91b2235af..4c856f7e8299 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/WhileStatementPostfixTemplate.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/WhileStatementPostfixTemplate.java @@ -1,5 +1,21 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.intellij.codeInsight.template.postfix.templates; +import com.intellij.codeInsight.template.postfix.util.PostfixTemplatesUtils; import com.intellij.openapi.editor.Editor; import com.intellij.psi.*; import org.jetbrains.annotations.NotNull; @@ -11,7 +27,7 @@ public class WhileStatementPostfixTemplate extends BooleanPostfixTemplate { @Override public void expand(@NotNull PsiElement context, @NotNull Editor editor) { - PsiExpression expression = getTopmostExpression(context); + PsiExpression expression = PostfixTemplatesUtils.getTopmostExpression(context); assert expression != null; PsiElementFactory factory = JavaPsiFacade.getElementFactory(context.getProject()); diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/util/Aliases.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/util/Aliases.java deleted file mode 100644 index 3de07c310f50..000000000000 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/util/Aliases.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.intellij.codeInsight.template.postfix.util; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * @author ignatov - */ -@Target(value = ElementType.TYPE) -@Retention(value = RetentionPolicy.RUNTIME) -public @interface Aliases { - String[] value(); -} diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/util/PostfixTemplatesUtils.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/util/PostfixTemplatesUtils.java index 370c986c5f48..7b92ffdb1ae0 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/util/PostfixTemplatesUtils.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/util/PostfixTemplatesUtils.java @@ -17,12 +17,12 @@ package com.intellij.codeInsight.template.postfix.util; import com.intellij.codeInsight.generation.surroundWith.JavaExpressionSurrounder; import com.intellij.codeInsight.generation.surroundWith.JavaWithIfExpressionSurrounder; -import com.intellij.codeInsight.template.postfix.templates.PostfixTemplate; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; import com.intellij.psi.*; import com.intellij.psi.util.InheritanceUtil; +import com.intellij.psi.util.PsiTreeUtil; import com.intellij.refactoring.util.CommonRefactoringUtil; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; @@ -45,7 +45,7 @@ public abstract class PostfixTemplatesUtils { } public static void createStatement(@NotNull PsiElement context, @NotNull Editor editor, @NotNull String prefix, @NotNull String suffix, int offset) { - PsiExpression expr = PostfixTemplate.getTopmostExpression(context); + PsiExpression expr = getTopmostExpression(context); PsiElement parent = expr != null ? expr.getParent() : null; assert parent instanceof PsiStatement; PsiElementFactory factory = JavaPsiFacade.getInstance(context.getProject()).getElementFactory(); @@ -121,5 +121,11 @@ public abstract class PostfixTemplatesUtils { } return null; } + + @Nullable + public static PsiExpression getTopmostExpression(PsiElement context) { + PsiExpressionStatement statement = PsiTreeUtil.getNonStrictParentOfType(context, PsiExpressionStatement.class); + return statement != null ? PsiTreeUtil.getChildOfType(statement, PsiExpression.class) : null; + } } |