diff options
author | Tor Norbye <tnorbye@google.com> | 2014-01-27 09:36:41 -0800 |
---|---|---|
committer | Tor Norbye <tnorbye@google.com> | 2014-01-27 09:36:49 -0800 |
commit | 809cb3e73653399e59e45e0b10749a8e37b85a75 (patch) | |
tree | 8aa7262925cc4eed902baa00f2193e98f6bb64e0 | |
parent | e2d6089d43d7ac1f62bafe06638d5ac2c21f5283 (diff) | |
download | idea-809cb3e73653399e59e45e0b10749a8e37b85a75.tar.gz |
Snapshot 9e6329d622cc9649c9c035f28faddc29564a5b7a from idea/133.696 of git://git.jetbrains.org/idea/community.git
9e6329d: IDEA-119035 Select All shortcut for any table on mac with Darcula
7642139: [log] IDEA-116322 Fix structure filter for multiple roots
5fe2227: [log] refactor: group filters before passing them to providers
a9bda1e: Problem with several users in log filter fixed.
bb353f3: [log] IDEA-119316 Fix "go to commit" if there are log filters
2825dd1: IDEA-119467 Gradle: auto discovery of buildSrc project (cherry picked from commit 3cb5420)
ca05350: Gradle: respect the order of dependencies (cherry picked from commit dca7107)
8a1d099: IDEA-115351 Idea UI hangs after performing Move Module to group (cherry picked from commit 673ed61) [r=Maxim.Mossienko]
9f006a4: NPE fix (cherry picked from commit 1eb3644)
2089b40: Gradle: test data fix (cherry picked from commit c864c4b)
6208324: IDEA-119336 Gradle build files: build.gradle scripts are checkout from Perforce even if the file is not changed (cherry picked from commit 3234c0b)
d1d776c: IDEA-119467 Gradle: auto discovery of buildSrc project IDEA-98930 IDEA does not resolve dependencies in Gradle buildSrc/ project (cherry picked from commit 0982afc)
6f6c403: Gradle: dependencies scope merge and sourceSets type handling updated related issues: IDEA-119365 Gradle import does not respect model customisations IDEA-118280 Gradle import: IDEA detect java folder as a resource folder IDEA-117668 IDEA v13 spontaneously changes additional test source root to source root (cherry picked from commit 68bfa5e)
a7f9d6f: Gradle: respect module build classpath for build scripts resolving review: http://crucible.labs.intellij.net/cru/CR-IC-4038
742c670: isEAP = false, time to release 13.0.2
3a5cfc7: add profiling parameters to the right part of the classpath (has been broken after Launcher was introduced)
ca42a66: fixed empty headers and import (dependant in language level) for qt, gtk... skeletons.
ba68876: update copyright in artworks
7c12cb3: IDEA-119619 Settings / Language Injections: project level XML tag injection loses Sub-Tags value on IDE restart
f7bd727: don't call robot on alt on Windows and when window is inactive (cherry picked from commit 0fe2cac)
8b62b1e: add WinXP definition (cherry picked from commit dde1494)
62c528a: consume Alt events on WinXP in default handler to avoid WindowsRootPaneUI.AltProcessor (cherry picked from commit f14aab9)
20c7faa: support table decoration on IntelliJ laf (cherry picked from commit 04425c9)
7dc2f34: IDEA-118211 (cherry picked from commit 55ee980) [r=Peter.Gromov]
b15f65d: fixed PY-11823 Test Runner detection in settings doesn't pick up just installed test runner
7a2bec9: Fixed dedent in case of tabs ( PY-10120).
56b5f81: Python keywords extracted. Cleanup.
f195253: Fixed test data to satisfy PEP8.
6b6bbc1: Make right par indent=none in import statement (PY-9075).
6a39911: Make continuation indent after continuation in indenting statement (PY-9573).
cf19307: Test for PY-9573
fb5186fa: Handle 'mode' as a keyword argument to open() as well
27a4d26: Detect text or binary I/O in pathlib.Path.open()
a5600e3: Return instance types for instance types of superclasses
f56e9b1: fixed PY-11837 nose test runner errors while formatting a test error
7420dd6: Nullity annotations
97ffd71: fixed test data
d0eb725: fixed test data
326185d: generator: do not drop the whole module in case there are broken __repr__ defined
982687a: performance for generator: Split big generated modules (like _Gtk, PyQt) into smaller ones
af679a5: Simplified always true condition
3174b9c: Extracted PyClassTypeImpl.findProperty()
777280e: Added 'inherited' parameter to PyClass.findProperty()
d5d8b23: Python skeletons class members provider now can provide new overridden members
6775ed3: Fixed code insight for returning 'self' in base class methods (PY-10977, PY-11413)
d4c641f: Fix formatter to add two blank lines between declarations with comment (PY-9923).
9c8134a: add field after super call
37813c6: Revert "Detect SQL fragments only in the beginning of string literals" (PY-11828)
9d5973f: Use default charset as python console encoding.
7fed382: Added Python 3.4 modules to the stdlib modules list
c772246: Don't ignore unused attributes of empty constructors (PY-7527)
91ae6bf: Fixed NPE in PyUnusedLocalInspectionVisitor.visitPyCallExpression()
464ae1c: fixed PY-11800 Parameter unfilled false positive for decorators with '*' arguments.
e3ec532: fixed test data
31bb1dd: fixed add field declaration to the beginning of __init__
2910f7d: fixed EA-52897 - CCE: ImportToggleAliasIntention$.execute
cca9133: fixed EA-53046 - NPE: PyExtractMethodUtil.a
2fcf769: fixed quickdoc test
fcf329f: fixed editing test
859c599: fixed testdata
ab7782a: fixed PY-11765 @ivar and @type in class documentation cause PyUnresolvedReferences inspection to file
8303e4f: IDEA-112303 Tool Windows Quick Access button: impossible to select item in list by mouse (cherry picked from commit 8e2ce03)
b206d2d: fix NPE
d923a83: Trying to fix EA-51665 - assert: FTManager.createAndStoreBundledTemplate (cherry picked from commit 47dee08) +review CR-IC
72fa58b: Add additional diagnostic to investigate EA-51665 (assert: FTManager.createAndStoreBundledTemplate). (cherry picked from commit a5e4cf4) +review CR-IC
6548f1a: java: incorrect parsing of bounds in class files fixed
8f5b22c: EA-42899 - CCE: XmlLanguageInjectionSupport.doEditInjection & cleanup
2a882e0: EA-53406 - IAE: ServiceManager.getService
ed41c77: WI-13685 PhpStorm doesn't save project name CR-PS-181
2c3ccec: IDEA-119445 Remove first slash in "copy reference" (cherry picked from commit 298bb04)
50bf901: IDEA-119153 file search too wide for users folder (cherry picked from commit 37521f0)
8ab790b: IDEA-119470 File and code templates: changes gone when switching tabs (cherry picked from commit 21cdea3)
7402508: EA-53393 - IOOBE: DomAnchorImpl.createAnchor (missing cherry pick)
f6ab2da: Bug fix: IDEA incorrect handle escaping.
c78400d: IDEA-105253 Missing icon for Thread dumps view
21b6f91: IDEA-104735 Dracula: INVALID string have not dracula style red color (cherry picked from commit 4a5e793)
53885c2: Don't show active editor in recent files
6cf74eb: IDEA-104706 Remove currently active file from "Recent Files" popup (cherry pick from master)
ffef358: IDEA-119406 IDEA make corrupts files when performing Maven resource filtering (default value of escapeString is null)
b8e9ab1: IDEA-119406 IDEA make corrupts files when performing Maven resource filtering (cherry picked from commit 5b6b3f6) +review CR-IC
d3dd646: EA-53308 - CCE: DfaVariableValue.<init> (cherry picked from commit 32a579d)
10f83e0: external build for artifacts: added API to filter contents of directory extracted from jar file [rev by Michael Golubev]
b7f4af0: make nonDefaultProject="true" really work
0bebc13: cosmetics
94e0a24: Merge remote-tracking branch 'origin/133' into 133
85cae29: IDEA-119347 ../jre64 JDK not being picked up by idea64.exe
2dd77af: IDEA-117127 Editor: Throwable on Select word at caret inside plain text (cherry picked from commit 2e3a0d8)
0a5e3ad: IDEA-117555 Search everywhere dialog is being closed immediately (cherry picked from commit ba8037d)
520da57: fix getDisplayName nullability assertion
0663734: IDEA-111122 remove attached jar artifact coming from "apklib" dependency from the dependencies of app module: users add it to deploy jar to Maven repository in addition to "apklib" file, but we don't need to add it to the classpath, because it leads to class duplication [rev=sergey.evdokimov]
c1fb468: IDEA-79522 need ability to set display names for xml attribute and xml tag language injections
3ae70b9: IDEA-119163 "Language Injections" settings should use toolbar decorator in the same way as other
50c78e9: IDEA-117327 Add a setting to switch off autopopup completion item selection by Enter (cherry picked from commit 84ddafc)
ae5ce9b: groovy debugging agent that produces less garbage (cherry picked from commit c7af9fe)
f6682f7: Roll-back FileChooserDescriptor API change.
1c64249: Merge remote-tracking branch 'origin/133' into 133
b293f8c: SearchEverywhere doesn't work on Linux
07b98c1: IDEA-74428 Ability to turn on log debug categories from the GUI
4257f6f: CR-IU-511 make abstract class abstract & leave getPresentableText mandatory
7cac7b7: allow to turn off suggestion to create a file when creating a directory with file-like name (IDEA-118250) (cherry picked from commit 5d81e8d)
acb9db2: don't show parameter info for invisible editors (EA-53161 - NPE: ParameterInfoComponent.<init>) (cherry picked from commit fb24d98)
e6bfbbf: the users don't care if we're preparing editors to open (IDEA-115130) (cherry picked from commit 9a116de)
3f1ebf3: rethrow PCE from KeyedExtensionFactory reflection (cherry picked from commit df8967b)
Change-Id: I8083d21f3faff4f899c53a5dea2710713dc1a2a9
253 files changed, 4232 insertions, 1452 deletions
diff --git a/bin/WinLauncher/WinLauncher64.exe b/bin/WinLauncher/WinLauncher64.exe Binary files differindex 447170845665..e2e4348505ce 100644 --- a/bin/WinLauncher/WinLauncher64.exe +++ b/bin/WinLauncher/WinLauncher64.exe diff --git a/community-resources/src/idea/IdeaApplicationInfo.xml b/community-resources/src/idea/IdeaApplicationInfo.xml index 9e07e0cbb73a..08f4adbdde86 100644 --- a/community-resources/src/idea/IdeaApplicationInfo.xml +++ b/community-resources/src/idea/IdeaApplicationInfo.xml @@ -1,5 +1,5 @@ <component> - <version codename="Community Edition" major="13" minor="0.2" eap="true"/> + <version codename="Community Edition" major="13" minor="0.2" eap="false"/> <company name="JetBrains s.r.o." url="http://www.jetbrains.com/?fromIDE"/> <build number="__BUILD_NUMBER__" date="__BUILD_DATE__"/> <install-over minbuild="129.1" maxbuild="132.9999" version="12.1"/> diff --git a/community-resources/src/idea_community_about.png b/community-resources/src/idea_community_about.png Binary files differindex 5bed0ec33a17..2b5a763fd9c5 100644 --- a/community-resources/src/idea_community_about.png +++ b/community-resources/src/idea_community_about.png diff --git a/community-resources/src/idea_community_about@2x.png b/community-resources/src/idea_community_about@2x.png Binary files differindex 428aaa5ed1fb..f159fb0a2e56 100644 --- a/community-resources/src/idea_community_about@2x.png +++ b/community-resources/src/idea_community_about@2x.png diff --git a/community-resources/src/idea_community_logo.png b/community-resources/src/idea_community_logo.png Binary files differindex 87543ff9b401..91a8153fe2dc 100644 --- a/community-resources/src/idea_community_logo.png +++ b/community-resources/src/idea_community_logo.png diff --git a/community-resources/src/idea_community_logo@2x.png b/community-resources/src/idea_community_logo@2x.png Binary files differindex 57603bed5536..1f2cfb1dcceb 100644 --- a/community-resources/src/idea_community_logo@2x.png +++ b/community-resources/src/idea_community_logo@2x.png diff --git a/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java b/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java index 0f13731ca723..49a0d9d93f57 100644 --- a/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java +++ b/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java @@ -100,8 +100,7 @@ import org.jetbrains.jps.cmdline.ClasspathBootstrap; import org.jetbrains.jps.incremental.Utils; import org.jetbrains.jps.model.serialization.JpsGlobalLoader; -import javax.tools.JavaCompiler; -import javax.tools.ToolProvider; +import javax.tools.*; import java.awt.*; import java.io.File; import java.io.IOException; @@ -878,7 +877,11 @@ public class BuildManager implements ApplicationComponent{ cmdLine.addParameter(option); } } - + + if (isProfilingMode) { + cmdLine.addParameter("-agentlib:yjpagent=disablej2ee,disablealloc,delay=10000,sessionname=ExternalBuild"); + } + // debugging final int debugPort = Registry.intValue("compiler.process.debug.port"); if (debugPort > 0) { @@ -933,7 +936,6 @@ public class BuildManager implements ApplicationComponent{ cp.addAll(myClasspathManager.getBuildProcessPluginsClasspath(project)); if (isProfilingMode) { cp.add(new File(workDirectory, "yjp-controller-api-redist.jar").getPath()); - cmdLine.addParameter("-agentlib:yjpagent=disablej2ee,disablealloc,delay=10000,sessionname=ExternalBuild"); } cmdLine.addParameter(classpathToString(cp)); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaVariableValue.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaVariableValue.java index 70f2c483a1d4..5ec56b9c2029 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaVariableValue.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaVariableValue.java @@ -91,7 +91,8 @@ public class DfaVariableValue extends DfaValue { myIsNegated = isNegated; myQualifier = qualifier; myVarType = varType; - myTypeValue = varType == null ? null : (DfaTypeValue)myFactory.createTypeValue(varType, Nullness.UNKNOWN); + DfaValue typeValue = myFactory.createTypeValue(varType, Nullness.UNKNOWN); + myTypeValue = typeValue instanceof DfaTypeValue ? (DfaTypeValue)typeValue : null; } @Nullable diff --git a/java/java-impl/src/com/intellij/psi/NonClasspathClassFinder.java b/java/java-impl/src/com/intellij/psi/NonClasspathClassFinder.java index af521568774b..6660080be549 100644 --- a/java/java-impl/src/com/intellij/psi/NonClasspathClassFinder.java +++ b/java/java-impl/src/com/intellij/psi/NonClasspathClassFinder.java @@ -44,19 +44,21 @@ public abstract class NonClasspathClassFinder extends PsiElementFinder { private volatile List<VirtualFile> myCache; private final PsiManager myManager; private final boolean myCheckForSources; - private final boolean myUseExtendedScope; public NonClasspathClassFinder(Project project) { - this(project, false, false); + this(project, false); } - protected NonClasspathClassFinder(Project project, boolean checkForSources, boolean useExtendedScope) { + protected NonClasspathClassFinder(Project project, boolean checkForSources) { myProject = project; myManager = PsiManager.getInstance(myProject); - myUseExtendedScope = useExtendedScope; myCheckForSources = checkForSources; } + protected List<VirtualFile> getClassRoots(@Nullable GlobalSearchScope scope) { + return getClassRoots(); + } + protected List<VirtualFile> getClassRoots() { List<VirtualFile> cache = myCache; long stamp = myManager.getModificationTracker().getModificationCount(); @@ -82,14 +84,11 @@ public abstract class NonClasspathClassFinder extends PsiElementFinder { @Override public PsiClass findClass(@NotNull String qualifiedName, @NotNull GlobalSearchScope scope) { - final List<VirtualFile> classRoots = getClassRoots(); + final List<VirtualFile> classRoots = getClassRoots(scope); if (classRoots.isEmpty()) { return null; } - if(myUseExtendedScope) { - scope = addNonClasspathScope(myProject, scope); - } final String relPath = qualifiedName.replace('.', '/'); for (final VirtualFile classRoot : classRoots) { if (scope.contains(classRoot)) { @@ -131,7 +130,7 @@ public abstract class NonClasspathClassFinder extends PsiElementFinder { @NotNull @Override public PsiClass[] getClasses(@NotNull PsiPackage psiPackage, @NotNull GlobalSearchScope scope) { - final List<VirtualFile> classRoots = getClassRoots(); + final List<VirtualFile> classRoots = getClassRoots(scope); if (classRoots.isEmpty()) { return PsiClass.EMPTY_ARRAY; } @@ -160,7 +159,7 @@ public abstract class NonClasspathClassFinder extends PsiElementFinder { @NotNull @Override public Set<String> getClassNames(@NotNull PsiPackage psiPackage, @NotNull GlobalSearchScope scope) { - final List<VirtualFile> classRoots = getClassRoots(); + final List<VirtualFile> classRoots = getClassRoots(scope); if (classRoots.isEmpty()) { return Collections.emptySet(); } @@ -172,7 +171,8 @@ public abstract class NonClasspathClassFinder extends PsiElementFinder { final VirtualFile dir = classRoot.findFileByRelativePath(pkgName.replace('.', '/')); if (dir != null && dir.isDirectory()) { for (final VirtualFile file : dir.getChildren()) { - if (!file.isDirectory() && "class".equals(file.getExtension())) { + if ((myCheckForSources && !file.isDirectory() && JavaFileType.DEFAULT_EXTENSION.equals(file.getExtension())) + || (!file.isDirectory() && "class".equals(file.getExtension()))) { result.add(file.getNameWithoutExtension()); } } @@ -207,7 +207,7 @@ public abstract class NonClasspathClassFinder extends PsiElementFinder { @NotNull GlobalSearchScope scope, @NotNull Processor<PsiDirectory> consumer, boolean includeLibrarySources) { - final List<VirtualFile> classRoots = getClassRoots(); + final List<VirtualFile> classRoots = getClassRoots(scope); if (classRoots.isEmpty()) { return true; } @@ -237,7 +237,7 @@ public abstract class NonClasspathClassFinder extends PsiElementFinder { @NotNull @Override public PsiPackage[] getSubPackages(@NotNull PsiPackage psiPackage, @NotNull GlobalSearchScope scope) { - final List<VirtualFile> classRoots = getClassRoots(); + final List<VirtualFile> classRoots = getClassRoots(scope); if (classRoots.isEmpty()) { return super.getSubPackages(psiPackage, scope); } diff --git a/java/java-impl/src/com/intellij/psi/NonClasspathResolveScopeEnlarger.java b/java/java-impl/src/com/intellij/psi/NonClasspathResolveScopeEnlarger.java index f6c8f8eb213e..01114788ac9d 100644 --- a/java/java-impl/src/com/intellij/psi/NonClasspathResolveScopeEnlarger.java +++ b/java/java-impl/src/com/intellij/psi/NonClasspathResolveScopeEnlarger.java @@ -1,5 +1,6 @@ package com.intellij.psi; +import com.intellij.ide.highlighter.JavaFileType; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VfsUtil; @@ -17,7 +18,8 @@ public class NonClasspathResolveScopeEnlarger extends ResolveScopeEnlarger { @Override public SearchScope getAdditionalResolveScope(@NotNull VirtualFile file, Project project) { - if ("class".equals(file.getExtension())) { + String fileExtension = file.getExtension(); + if ("class".equals(fileExtension) || JavaFileType.DEFAULT_EXTENSION.equals(fileExtension)) { for (PsiElementFinder finder : Extensions.getExtensions(PsiElementFinder.EP_NAME, project)) { if (finder instanceof NonClasspathClassFinder) { final List<VirtualFile> roots = ((NonClasspathClassFinder)finder).getClassRoots(); diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClassFileStubBuilder.java b/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClassFileStubBuilder.java index 43b57aa4e607..96c48ece31b1 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClassFileStubBuilder.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClassFileStubBuilder.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. @@ -34,7 +34,7 @@ import java.util.*; public class ClassFileStubBuilder implements BinaryFileStubBuilder { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.compiled.ClassFileStubBuilder"); - public static final int STUB_VERSION = 7 + JavaFileElementType.STUB_VERSION; + public static final int STUB_VERSION = 8 + JavaFileElementType.STUB_VERSION; @Override public boolean acceptsFile(final VirtualFile file) { diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/compiled/SignatureParsing.java b/java/java-psi-impl/src/com/intellij/psi/impl/compiled/SignatureParsing.java index 7c40209ff9cd..89ea3bcdf051 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/compiled/SignatureParsing.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/compiled/SignatureParsing.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 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. @@ -29,12 +29,13 @@ import com.intellij.psi.impl.java.stubs.impl.PsiTypeParameterStubImpl; import com.intellij.psi.stubs.StubElement; import com.intellij.util.ArrayUtil; import com.intellij.util.cls.ClsFormatException; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.io.StringRef; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.Nullable; import java.text.CharacterIterator; -import java.util.ArrayList; +import java.util.List; @SuppressWarnings({"HardCodedStringLiteral"}) public class SignatureParsing { @@ -69,16 +70,20 @@ public class SignatureParsing { //todo parse annotations on type param PsiTypeParameterStub parameterStub = new PsiTypeParameterStubImpl(parent, StringRef.fromString(name.toString())); - ArrayList<String> bounds = null; + List<String> bounds = ContainerUtil.newSmartList(); while (signatureIterator.current() == ':') { signatureIterator.next(); String bound = parseTopLevelClassRefSignature(signatureIterator); - if (bound != null && !bound.equals(CommonClassNames.JAVA_LANG_OBJECT)) { - if (bounds == null) bounds = new ArrayList<String>(); + if (bound != null) { bounds.add(bound); } } + int size = bounds.size(); + if (size > 0 && CommonClassNames.JAVA_LANG_OBJECT.equals(bounds.get(size - 1))) { + bounds.remove(size - 1); + } + StubBuildingVisitor.newReferenceList(JavaStubElementTypes.EXTENDS_BOUND_LIST, parameterStub, ArrayUtil.toStringArray(bounds)); return parameterStub; diff --git a/java/java-tests/testData/codeInsight/copyReference/CopyFile_after.java b/java/java-tests/testData/codeInsight/copyReference/CopyFile_after.java index b0c0783cccee..f1120f4828eb 100644 --- a/java/java-tests/testData/codeInsight/copyReference/CopyFile_after.java +++ b/java/java-tests/testData/codeInsight/copyReference/CopyFile_after.java @@ -1,3 +1,3 @@ class X { - String s = "/x/x.txt<caret>"; + String s = "x/x.txt<caret>"; } diff --git a/java/java-tests/testData/psi/cls/mirror/Bounds.txt b/java/java-tests/testData/psi/cls/mirror/Bounds.txt new file mode 100644 index 000000000000..ab190fdddf30 --- /dev/null +++ b/java/java-tests/testData/psi/cls/mirror/Bounds.txt @@ -0,0 +1,13 @@ + + // IntelliJ API Decompiler stub source generated from a class file + // Implementation of methods is not available + +package pkg; + +class Bounds { + Bounds() { /* compiled code */ } + + public static <T extends java.lang.Object & java.lang.Comparable<? super T>> T max(java.util.Collection<? extends T> collection) { /* compiled code */ } + + public static <T> T max(java.util.Collection<? extends T> collection, java.util.Comparator<? super T> comparator) { /* compiled code */ } +}
\ No newline at end of file diff --git a/java/java-tests/testData/psi/cls/mirror/pkg/Bounds.class b/java/java-tests/testData/psi/cls/mirror/pkg/Bounds.class Binary files differnew file mode 100644 index 000000000000..ce57f184578b --- /dev/null +++ b/java/java-tests/testData/psi/cls/mirror/pkg/Bounds.class diff --git a/java/java-tests/testData/psi/cls/mirror/src/pkg/Bounds.java b/java/java-tests/testData/psi/cls/mirror/src/pkg/Bounds.java new file mode 100644 index 000000000000..81bd3a012750 --- /dev/null +++ b/java/java-tests/testData/psi/cls/mirror/src/pkg/Bounds.java @@ -0,0 +1,29 @@ +/* + * 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 pkg; + +import java.util.Collection; +import java.util.Comparator; + +class Bounds { + public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll) { + return null; + } + + public static <T> T max(Collection<? extends T> coll, Comparator<? super T> comp) { + return null; + } +} diff --git a/java/java-tests/testData/psi/cls/stubBuilder/UtilCollections.txt b/java/java-tests/testData/psi/cls/stubBuilder/UtilCollections.txt index d58e2e68de90..82d04fec4659 100644 --- a/java/java-tests/testData/psi/cls/stubBuilder/UtilCollections.txt +++ b/java/java-tests/testData/psi/cls/stubBuilder/UtilCollections.txt @@ -1693,7 +1693,7 @@ PsiJavaFileStub [java.util] PsiModifierListStub[mask=9] PsiTypeParameterListStub PsiTypeParameter[T] - PsiRefListStub[EXTENDS_BOUNDS_LIST:java.lang.Comparable<? super T>] + PsiRefListStub[EXTENDS_BOUNDS_LIST:java.lang.Object, java.lang.Comparable<? super T>] PsiParameterListStub PsiParameterStub[p1:java.util.List<? extends T>] PsiModifierListStub[mask=0] @@ -1704,7 +1704,7 @@ PsiJavaFileStub [java.util] PsiModifierListStub[mask=10] PsiTypeParameterListStub PsiTypeParameter[T] - PsiRefListStub[EXTENDS_BOUNDS_LIST:java.lang.Comparable<? super T>] + PsiRefListStub[EXTENDS_BOUNDS_LIST:java.lang.Object, java.lang.Comparable<? super T>] PsiParameterListStub PsiParameterStub[p1:java.util.List<? extends T>] PsiModifierListStub[mask=0] @@ -1715,7 +1715,7 @@ PsiJavaFileStub [java.util] PsiModifierListStub[mask=10] PsiTypeParameterListStub PsiTypeParameter[T] - PsiRefListStub[EXTENDS_BOUNDS_LIST:java.lang.Comparable<? super T>] + PsiRefListStub[EXTENDS_BOUNDS_LIST:java.lang.Object, java.lang.Comparable<? super T>] PsiParameterListStub PsiParameterStub[p1:java.util.List<? extends T>] PsiModifierListStub[mask=0] @@ -1843,7 +1843,7 @@ PsiJavaFileStub [java.util] PsiModifierListStub[mask=9] PsiTypeParameterListStub PsiTypeParameter[T] - PsiRefListStub[EXTENDS_BOUNDS_LIST:java.lang.Comparable<? super T>] + PsiRefListStub[EXTENDS_BOUNDS_LIST:java.lang.Object, java.lang.Comparable<? super T>] PsiParameterListStub PsiParameterStub[p1:java.util.Collection<? extends T>] PsiModifierListStub[mask=0] @@ -1863,7 +1863,7 @@ PsiJavaFileStub [java.util] PsiModifierListStub[mask=9] PsiTypeParameterListStub PsiTypeParameter[T] - PsiRefListStub[EXTENDS_BOUNDS_LIST:java.lang.Comparable<? super T>] + PsiRefListStub[EXTENDS_BOUNDS_LIST:java.lang.Object, java.lang.Comparable<? super T>] PsiParameterListStub PsiParameterStub[p1:java.util.Collection<? extends T>] PsiModifierListStub[mask=0] diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/CopyReferenceTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/CopyReferenceTest.groovy index fbae4d18ba7e..ae14f4857e8d 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/CopyReferenceTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/CopyReferenceTest.groovy @@ -90,7 +90,7 @@ class Foo { performCopy() myFixture.configureByText 'a.txt', '' performPaste() - myFixture.checkResult "/a.java:2" + myFixture.checkResult "a.java:2" } public void _testMethodOverloadCopy() { diff --git a/java/java-tests/testSrc/com/intellij/psi/ClsMirrorBuildingTest.java b/java/java-tests/testSrc/com/intellij/psi/ClsMirrorBuildingTest.java index b9a4a189a6b0..728fd72951cc 100644 --- a/java/java-tests/testSrc/com/intellij/psi/ClsMirrorBuildingTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/ClsMirrorBuildingTest.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. @@ -44,6 +44,7 @@ public class ClsMirrorBuildingTest extends LightIdeaTestCase { public void testClassRefs() { doTest(); } public void testEA46236() { doTest("ValuedEnum"); } public void testKotlinFunList() { doTest(); } + public void testBounds() { doTest(); } private void doTest() { doTest(getTestName(false)); diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactCompilerInstructionCreator.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactCompilerInstructionCreator.java index eb2987e13e03..5648eb96f16a 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactCompilerInstructionCreator.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactCompilerInstructionCreator.java @@ -15,6 +15,7 @@ */ package org.jetbrains.jps.incremental.artifacts.instructions; +import com.intellij.openapi.util.Condition; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -31,8 +32,23 @@ public interface ArtifactCompilerInstructionCreator { void addDirectoryCopyInstructions(@NotNull File directory, @Nullable SourceFileFilter filter); + /** + * Add instruction to extract directory from a jar file into the current place in the artifact layout. + * + * @param jarFile jar file to extract + * @param pathInJar relative path to directory inside {@code jarFile} which need to be extracted. Use "/" to extract the whole jar contents + */ void addExtractDirectoryInstruction(@NotNull File jarFile, @NotNull String pathInJar); + /** + * Add instruction to extract directory from a jar file into the current place in the artifact layout. + * @param jarFile jar file to extract + * @param pathInJar relative path to directory inside {@code jarFile} which need to be extracted. Use "/" to extract the whole jar contents + * @param pathInJarFilter a filter instance specifying which entries should be extracted. It should accept paths inside the jar file + * relative to {@code pathInJar} root and return {@code true} if the entry should be extracted and {@code false} otherwise + */ + void addExtractDirectoryInstruction(@NotNull File jarFile, @NotNull String pathInJar, @NotNull Condition<String> pathInJarFilter); + ArtifactCompilerInstructionCreator subFolder(@NotNull String directoryName); ArtifactCompilerInstructionCreator archive(@NotNull String archiveFileName); diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactCompilerInstructionCreatorBase.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactCompilerInstructionCreatorBase.java index ee1ee1798f55..2e5aa50ad45b 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactCompilerInstructionCreatorBase.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactCompilerInstructionCreatorBase.java @@ -15,14 +15,16 @@ */ package org.jetbrains.jps.incremental.artifacts.instructions; +import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Conditions; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.PathUtilRt; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.cmdline.ProjectDescriptor; +import org.jetbrains.jps.incremental.artifacts.JarPathUtil; import org.jetbrains.jps.indices.IgnoredFileIndex; import org.jetbrains.jps.indices.ModuleExcludeIndex; -import org.jetbrains.jps.incremental.artifacts.JarPathUtil; import java.io.File; import java.io.IOException; @@ -56,15 +58,22 @@ public abstract class ArtifactCompilerInstructionCreatorBase implements Artifact @Override public void addExtractDirectoryInstruction(@NotNull File jarFile, @NotNull String pathInJar) { + addExtractDirectoryInstruction(jarFile, pathInJar, Conditions.<String>alwaysTrue()); + } + + @Override + public void addExtractDirectoryInstruction(@NotNull File jarFile, + @NotNull String pathInJar, + @NotNull Condition<String> pathInJarFilter) { //an entry of a jar file is excluded if and only if the jar file itself is excluded. In that case we should unpack entries to the artifact - // because the jar itself is explicitly added to the artifact layout. + //because the jar itself is explicitly added to the artifact layout. boolean includeExcluded = true; final SourceFileFilterImpl filter = new SourceFileFilterImpl(null, myInstructionsBuilder.getRootsIndex(), myInstructionsBuilder.getIgnoredFileIndex(), includeExcluded); DestinationInfo destination = createDirectoryDestination(); if (destination != null) { - ArtifactRootDescriptor descriptor = myInstructionsBuilder.createJarBasedRoot(jarFile, pathInJar, filter, destination); + ArtifactRootDescriptor descriptor = myInstructionsBuilder.createJarBasedRoot(jarFile, pathInJar, filter, destination, pathInJarFilter); if (myInstructionsBuilder.addDestination(descriptor)) { onAdded(descriptor); } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactInstructionsBuilderImpl.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactInstructionsBuilderImpl.java index ae9993d7214e..0352417b0e24 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactInstructionsBuilderImpl.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactInstructionsBuilderImpl.java @@ -15,6 +15,7 @@ */ package org.jetbrains.jps.incremental.artifacts.instructions; +import com.intellij.openapi.util.Condition; import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.incremental.artifacts.ArtifactBuildTarget; import org.jetbrains.jps.indices.IgnoredFileIndex; @@ -79,7 +80,9 @@ public class ArtifactInstructionsBuilderImpl implements ArtifactInstructionsBuil public JarBasedArtifactRootDescriptor createJarBasedRoot(@NotNull File jarFile, @NotNull String pathInJar, - @NotNull SourceFileFilter filter, final DestinationInfo destinationInfo) { - return new JarBasedArtifactRootDescriptor(jarFile, pathInJar, filter, myRootIndex++, myBuildTarget, destinationInfo); + @NotNull SourceFileFilter filter, + @NotNull DestinationInfo destinationInfo, + @NotNull Condition<String> pathInJarFilter) { + return new JarBasedArtifactRootDescriptor(jarFile, pathInJar, filter, myRootIndex++, myBuildTarget, destinationInfo, pathInJarFilter); } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/JarBasedArtifactRootDescriptor.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/JarBasedArtifactRootDescriptor.java index 8c3f762238a0..c5eb04940b85 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/JarBasedArtifactRootDescriptor.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/JarBasedArtifactRootDescriptor.java @@ -15,6 +15,7 @@ */ package org.jetbrains.jps.incremental.artifacts.instructions; +import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import org.jetbrains.annotations.NotNull; @@ -39,14 +40,18 @@ import java.util.zip.ZipFile; */ public class JarBasedArtifactRootDescriptor extends ArtifactRootDescriptor { private final String myPathInJar; + private final Condition<String> myPathInJarFilter; public JarBasedArtifactRootDescriptor(@NotNull File jarFile, @NotNull String pathInJar, @NotNull SourceFileFilter filter, int index, - ArtifactBuildTarget target, DestinationInfo destinationInfo) { + @NotNull ArtifactBuildTarget target, + @NotNull DestinationInfo destinationInfo, + @NotNull Condition<String> pathInJarFilter) { super(jarFile, filter, index, target, destinationInfo); myPathInJar = pathInJar; + myPathInJarFilter = pathInJarFilter; } public void processEntries(EntryProcessor processor) throws IOException { @@ -68,7 +73,9 @@ public class JarBasedArtifactRootDescriptor extends ArtifactRootDescriptor { final String name = entry.getName(); if (name.startsWith(prefix)) { String relativePath = name.substring(prefix.length()); - processor.process(entry.isDirectory() ? null : zipFile.getInputStream(entry), relativePath, entry); + if (myPathInJarFilter.value(relativePath)) { + processor.process(entry.isDirectory() ? null : zipFile.getInputStream(entry), relativePath, entry); + } } } } diff --git a/native/WinLauncher/WinLauncher/WinLauncher.cpp b/native/WinLauncher/WinLauncher/WinLauncher.cpp index e085b186a1b8..8d5ff13a46ed 100644 --- a/native/WinLauncher/WinLauncher/WinLauncher.cpp +++ b/native/WinLauncher/WinLauncher/WinLauncher.cpp @@ -190,7 +190,7 @@ bool LocateJVM() return result; } - std::string jreDir = GetAdjacentDir("jre"); + std::string jreDir = GetAdjacentDir(need64BitJRE ? "jre64":"jre"); if (FindValidJVM(jreDir.c_str()) && Is64BitJRE(jvmPath) == need64BitJRE) { return true; diff --git a/platform/core-api/src/com/intellij/patterns/TreeElementPattern.java b/platform/core-api/src/com/intellij/patterns/TreeElementPattern.java index 084be74c0031..f955f357007b 100644 --- a/platform/core-api/src/com/intellij/patterns/TreeElementPattern.java +++ b/platform/core-api/src/com/intellij/patterns/TreeElementPattern.java @@ -110,7 +110,7 @@ public abstract class TreeElementPattern<ParentType, T extends ParentType, Self return withSuperParent(level, StandardPatterns.instanceOf(aClass)); } public Self withSuperParent(final int level, @NotNull final ElementPattern<? extends ParentType> pattern) { - return with(new PatternConditionPlus<T, ParentType>("withSuperParent", pattern) { + return with(new PatternConditionPlus<T, ParentType>(level == 1 ? "withParent" : "withSuperParent", pattern) { @Override public boolean processValues(T t, diff --git a/platform/extensions/src/com/intellij/openapi/util/KeyedExtensionFactory.java b/platform/extensions/src/com/intellij/openapi/util/KeyedExtensionFactory.java index 054cfe0d7f19..f815acf63c14 100644 --- a/platform/extensions/src/com/intellij/openapi/util/KeyedExtensionFactory.java +++ b/platform/extensions/src/com/intellij/openapi/util/KeyedExtensionFactory.java @@ -23,6 +23,7 @@ import org.jetbrains.annotations.NotNull; import org.picocontainer.PicoContainer; import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; @@ -94,6 +95,12 @@ public abstract class KeyedExtensionFactory<T, KeyT> { break; } } + catch (InvocationTargetException e) { + if (e.getCause() instanceof RuntimeException) { + throw (RuntimeException)e.getCause(); + } + throw new RuntimeException(e); + } catch (RuntimeException e) { throw e; } diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/ExternalModuleBuildClasspathPojo.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/ExternalModuleBuildClasspathPojo.java new file mode 100644 index 000000000000..5783f9444d07 --- /dev/null +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/ExternalModuleBuildClasspathPojo.java @@ -0,0 +1,85 @@ +/* + * 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.openapi.externalSystem.model.project; + +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * @author Vladislav.Soroka + * @since 1/14/14 + */ +public class ExternalModuleBuildClasspathPojo { + + @NotNull private List<String> myEntries; + @NotNull private String myPath; + + @SuppressWarnings("UnusedDeclaration") + public ExternalModuleBuildClasspathPojo() { + // Used by IJ serialization + this("___DUMMY___", ContainerUtil.<String>newArrayList()); + } + + public ExternalModuleBuildClasspathPojo(@NotNull String path, @NotNull List<String> entries) { + myPath = path; + myEntries = entries; + } + + @NotNull + public String getPath() { + return myPath; + } + + public void setPath(@NotNull String path) { + myPath = path; + } + + @NotNull + public List<String> getEntries() { + return myEntries; + } + + public void setEntries(@NotNull List<String> entries) { + myEntries = entries; + } + + @Override + public int hashCode() { + int result = myEntries.hashCode(); + result = 31 * result + myPath.hashCode(); + return result; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ExternalModuleBuildClasspathPojo pojo = (ExternalModuleBuildClasspathPojo)o; + + if (!myEntries.equals(pojo.myEntries)) return false; + if (!myPath.equals(pojo.myPath)) return false; + + return true; + } + + @Override + public String toString() { + return myPath; + } +} diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/ExternalProjectBuildClasspathPojo.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/ExternalProjectBuildClasspathPojo.java new file mode 100644 index 000000000000..9fb2ea65bb48 --- /dev/null +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/ExternalProjectBuildClasspathPojo.java @@ -0,0 +1,102 @@ +/* + * 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.openapi.externalSystem.model.project; + +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Map; + +/** + * @author Vladislav.Soroka + * @since 1/14/14 + */ +public class ExternalProjectBuildClasspathPojo { + + /** + * Common for all project modules build classpath. E.g. it can be build system SDK libraries, configured at project level. + */ + @NotNull private List<String> myProjectBuildClasspath; + @NotNull private Map<String, ExternalModuleBuildClasspathPojo> myModulesBuildClasspath; + @NotNull private String myName; + + @SuppressWarnings("UnusedDeclaration") + public ExternalProjectBuildClasspathPojo() { + // Used by IJ serialization + this("___DUMMY___", ContainerUtil.<String>newArrayList(), ContainerUtil.<String, ExternalModuleBuildClasspathPojo>newHashMap()); + } + + public ExternalProjectBuildClasspathPojo(@NotNull String name, + @NotNull List<String> projectBuildClasspath, + @NotNull Map<String, ExternalModuleBuildClasspathPojo> modulesBuildClasspath) { + myName = name; + myProjectBuildClasspath = projectBuildClasspath; + myModulesBuildClasspath = modulesBuildClasspath; + } + + @NotNull + public String getName() { + return myName; + } + + public void setName(@NotNull String name) { + myName = name; + } + + @NotNull + public Map<String, ExternalModuleBuildClasspathPojo> getModulesBuildClasspath() { + return myModulesBuildClasspath; + } + + public void setModulesBuildClasspath(@NotNull Map<String, ExternalModuleBuildClasspathPojo> modulesBuildClasspath) { + myModulesBuildClasspath = modulesBuildClasspath; + } + + @NotNull + public List<String> getProjectBuildClasspath() { + return myProjectBuildClasspath; + } + + public void setProjectBuildClasspath(@NotNull List<String> projectBuildClasspath) { + myProjectBuildClasspath = projectBuildClasspath; + } + + @Override + public int hashCode() { + int result = myModulesBuildClasspath.hashCode(); + result = 31 * result + myModulesBuildClasspath.hashCode(); + return result; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ExternalProjectBuildClasspathPojo pojo = (ExternalProjectBuildClasspathPojo)o; + + if (!myModulesBuildClasspath.equals(pojo.myModulesBuildClasspath)) return false; + if (!myName.equals(pojo.myName)) return false; + + return true; + } + + @Override + public String toString() { + return myName; + } +} diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/psi/search/ExternalModuleBuildGlobalSearchScope.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/psi/search/ExternalModuleBuildGlobalSearchScope.java new file mode 100644 index 000000000000..acd5c80c671c --- /dev/null +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/psi/search/ExternalModuleBuildGlobalSearchScope.java @@ -0,0 +1,40 @@ +/* + * 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.openapi.externalSystem.psi.search; + +import com.intellij.psi.search.DelegatingGlobalSearchScope; +import com.intellij.psi.search.GlobalSearchScope; +import org.jetbrains.annotations.NotNull; + +/** + * @author Vladislav.Soroka + * @since 1/15/14 + */ +public class ExternalModuleBuildGlobalSearchScope extends DelegatingGlobalSearchScope { + + @NotNull + private final String externalModulePath; + + public ExternalModuleBuildGlobalSearchScope(@NotNull GlobalSearchScope baseScope, @NotNull String externalModulePath) { + super(baseScope); + this.externalModulePath = externalModulePath; + } + + @NotNull + public String getExternalModulePath() { + return externalModulePath; + } +} diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/settings/AbstractExternalSystemLocalSettings.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/settings/AbstractExternalSystemLocalSettings.java index 86550eea5b22..aa6950933390 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/settings/AbstractExternalSystemLocalSettings.java +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/settings/AbstractExternalSystemLocalSettings.java @@ -19,6 +19,7 @@ import com.intellij.openapi.externalSystem.ExternalSystemManager; import com.intellij.openapi.externalSystem.model.ProjectSystemId; import com.intellij.openapi.externalSystem.model.execution.ExternalTaskExecutionInfo; import com.intellij.openapi.externalSystem.model.execution.ExternalTaskPojo; +import com.intellij.openapi.externalSystem.model.project.ExternalProjectBuildClasspathPojo; import com.intellij.openapi.externalSystem.model.project.ExternalProjectPojo; import com.intellij.openapi.externalSystem.service.project.PlatformFacade; import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil; @@ -64,6 +65,10 @@ public abstract class AbstractExternalSystemLocalSettings { new AtomicReference<Map<String, Collection<ExternalTaskPojo>>>( ContainerUtilRt.<String, Collection<ExternalTaskPojo>>newHashMap() ); + private final AtomicReference<Map<String/* external project config path */, ExternalProjectBuildClasspathPojo>> myProjectBuildClasspath = + new AtomicReference<Map<String, ExternalProjectBuildClasspathPojo>>( + ContainerUtilRt.<String, ExternalProjectBuildClasspathPojo>newHashMap() + ); private final AtomicReference<Map<String/* external project config path */, Long>> myExternalConfigModificationStamps = new AtomicReference<Map<String, Long>>(ContainerUtilRt.<String, Long>newHashMap()); @@ -114,6 +119,15 @@ public abstract class AbstractExternalSystemLocalSettings { } } + for (Iterator<Map.Entry<String, ExternalProjectBuildClasspathPojo>> it = myProjectBuildClasspath.get().entrySet().iterator(); it.hasNext(); ) { + Map.Entry<String, ExternalProjectBuildClasspathPojo> entry = it.next(); + if (linkedProjectPathsToForget.contains(entry.getKey()) + || linkedProjectPathsToForget.contains(ExternalSystemApiUtil.getRootProjectPath(entry.getKey(), myExternalSystemId, myProject))) + { + it.remove(); + } + } + Map<String, Long> modificationStamps = myExternalConfigModificationStamps.get(); for (String path : linkedProjectPathsToForget) { modificationStamps.remove(path); @@ -152,7 +166,7 @@ public abstract class AbstractExternalSystemLocalSettings { public void setRecentTasks(@NotNull List<ExternalTaskExecutionInfo> tasks) { myRecentTasks.set(tasks); } - + @NotNull public Map<String, Long> getExternalConfigModificationStamps() { return myExternalConfigModificationStamps.get(); @@ -163,7 +177,18 @@ public abstract class AbstractExternalSystemLocalSettings { // Required for IJ serialization. myExternalConfigModificationStamps.set(modificationStamps); } - + + @NotNull + public Map<String, ExternalProjectBuildClasspathPojo> getProjectBuildClasspath() { + return myProjectBuildClasspath.get(); + } + + @SuppressWarnings("UnusedDeclaration") + public void setProjectBuildClasspath(@NotNull Map<String, ExternalProjectBuildClasspathPojo> projectsBuildClasspath) { + // Required for IJ serialization. + myProjectBuildClasspath.set(projectsBuildClasspath); + } + public void fillState(@NotNull State state) { if (PRESERVE_EXPAND_STATE) { state.tasksExpandState = myExpandStates.get(); @@ -175,6 +200,7 @@ public abstract class AbstractExternalSystemLocalSettings { state.availableProjects = myAvailableProjects.get(); state.availableTasks = myAvailableTasks.get(); state.modificationStamps = myExternalConfigModificationStamps.get(); + state.projectBuildClasspath = myProjectBuildClasspath.get(); } public void loadState(@NotNull State state) { @@ -182,6 +208,7 @@ public abstract class AbstractExternalSystemLocalSettings { setIfNotNull(myAvailableProjects, state.availableProjects); setIfNotNull(myAvailableTasks, state.availableTasks); setIfNotNull(myExternalConfigModificationStamps, state.modificationStamps); + setIfNotNull(myProjectBuildClasspath, state.projectBuildClasspath); if (state.recentTasks != null) { List<ExternalTaskExecutionInfo> recentTasks = myRecentTasks.get(); if (recentTasks != state.recentTasks) { @@ -242,5 +269,6 @@ public abstract class AbstractExternalSystemLocalSettings { public Map<String/* linked project path */, Long/* last config modification stamp */> modificationStamps = ContainerUtilRt.newHashMap(); + public Map<String/* linked project path */, ExternalProjectBuildClasspathPojo> projectBuildClasspath = ContainerUtilRt.newHashMap(); } } diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/util/ExternalSystemApiUtil.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/util/ExternalSystemApiUtil.java index 7b078c7debfe..af0772dd7b31 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/util/ExternalSystemApiUtil.java +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/util/ExternalSystemApiUtil.java @@ -318,6 +318,24 @@ public class ExternalSystemApiUtil { } @SuppressWarnings("unchecked") + @Nullable + public static <T> DataNode<T> findParent(@NotNull DataNode<?> node, @NotNull Key<T> key) { + return findParent(node, key, null); + } + + + @SuppressWarnings("unchecked") + @Nullable + public static <T> DataNode<T> findParent(@NotNull DataNode<?> node, + @NotNull Key<T> key, + @Nullable BooleanFunction<DataNode<T>> predicate) { + DataNode<?> parent = node.getParent(); + if (parent == null) return null; + return key.equals(parent.getKey()) && (predicate == null || predicate.fun((DataNode<T>)parent)) + ? (DataNode<T>)parent : findParent(parent, key, predicate); + } + + @SuppressWarnings("unchecked") @NotNull public static <T> Collection<DataNode<T>> findAll(@NotNull DataNode<?> parent, @NotNull Key<T> key) { Collection<DataNode<T>> result = null; diff --git a/platform/lang-impl/src/com/intellij/codeInsight/AutoPopupController.java b/platform/lang-impl/src/com/intellij/codeInsight/AutoPopupController.java index 89413c5899b0..9f14e0021900 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/AutoPopupController.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/AutoPopupController.java @@ -171,8 +171,9 @@ public class AutoPopupController implements Disposable { final Runnable request = new Runnable(){ @Override public void run(){ - if (myProject.isDisposed() || DumbService.isDumb(myProject) || editor.isDisposed()) return; + if (myProject.isDisposed() || DumbService.isDumb(myProject)) return; documentManager.commitAllDocuments(); + if (editor.isDisposed() || !editor.getComponent().isShowing()) return; int lbraceOffset = editor.getCaretModel().getOffset() - 1; try { ShowParameterInfoHandler.invoke(myProject, editor, file1, lbraceOffset, highlightedMethod); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java index 1aa02d4cbe3d..39e0b5d32e2c 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java @@ -705,6 +705,10 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement } private static boolean shouldPreselectFirstSuggestion(CompletionParameters parameters) { + if (!Registry.is("ide.completion.autopopup.choose.by.enter")) { + return false; + } + if (!ApplicationManager.getApplication().isUnitTestMode()) { return true; } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/wordSelection/NaturalLanguageTextSelectioner.java b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/wordSelection/NaturalLanguageTextSelectioner.java index a574ce0c5465..dc5d9f32072e 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/wordSelection/NaturalLanguageTextSelectioner.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/wordSelection/NaturalLanguageTextSelectioner.java @@ -44,6 +44,9 @@ public class NaturalLanguageTextSelectioner extends ExtendWordSelectionHandlerBa private static TextRange findParagraphRange(String text, int start, int end) { int paragraphStart = text.lastIndexOf("\n\n", start); int paragraphEnd = text.indexOf("\n\n", end); + if (paragraphStart >= paragraphEnd) { + return new TextRange(0, text.length()); + } return new TextRange(paragraphStart >= 0 ? paragraphStart + 2 : 0, paragraphEnd < 0 ? text.length() : paragraphEnd); } diff --git a/platform/lang-impl/src/com/intellij/execution/ui/layout/actions/RestoreViewAction.java b/platform/lang-impl/src/com/intellij/execution/ui/layout/actions/RestoreViewAction.java index cff9749eaf04..61676bbc7044 100644 --- a/platform/lang-impl/src/com/intellij/execution/ui/layout/actions/RestoreViewAction.java +++ b/platform/lang-impl/src/com/intellij/execution/ui/layout/actions/RestoreViewAction.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 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. @@ -17,12 +17,15 @@ package com.intellij.execution.ui.layout.actions; import com.intellij.execution.ui.layout.CellTransform; +import com.intellij.icons.AllIcons; import com.intellij.idea.ActionsBundle; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.Presentation; import com.intellij.ui.content.Content; +import javax.swing.*; + public class RestoreViewAction extends AnAction { private final Content myContent; @@ -38,7 +41,8 @@ public class RestoreViewAction extends AnAction { Presentation p = e.getPresentation(); p.setText(ActionsBundle.message("action.Runner.RestoreView.text", myContent.getDisplayName())); p.setDescription(ActionsBundle.message("action.Runner.RestoreView.description")); - p.setIcon(myContent.getIcon()); + Icon icon = myContent.getIcon(); + p.setIcon(icon == null ? AllIcons.Debugger.RestoreLayout : icon); } @Override diff --git a/platform/lang-impl/src/com/intellij/ide/actions/CopyReferenceAction.java b/platform/lang-impl/src/com/intellij/ide/actions/CopyReferenceAction.java index 6cc5dda69dff..7d53a7d3a5d9 100644 --- a/platform/lang-impl/src/com/intellij/ide/actions/CopyReferenceAction.java +++ b/platform/lang-impl/src/com/intellij/ide/actions/CopyReferenceAction.java @@ -20,7 +20,10 @@ import com.intellij.codeInsight.daemon.impl.IdentifierUtil; import com.intellij.codeInsight.highlighting.HighlightManager; import com.intellij.ide.IdeBundle; import com.intellij.ide.dnd.FileCopyPasteUtil; -import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.actionSystem.ActionPlaces; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.colors.EditorColors; @@ -33,14 +36,12 @@ import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.wm.WindowManager; import com.intellij.openapi.wm.ex.StatusBarEx; import com.intellij.psi.*; -import com.intellij.util.ArrayUtil; -import com.intellij.util.LogicalRoot; -import com.intellij.util.LogicalRootsManager; +import com.intellij.util.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -84,7 +85,7 @@ public class CopyReferenceAction extends DumbAwareAction { Project project = CommonDataKeys.PROJECT.getData(dataContext); PsiElement element = getElementToCopy(editor, dataContext); - if (!doCopy(element, project, editor) && editor != null) { + if (!doCopy(element, project, editor) && editor != null && project != null) { Document document = editor.getDocument(); PsiFile file = PsiDocumentManager.getInstance(project).getCachedPsiFile(document); if (file != null) { @@ -98,7 +99,7 @@ public class CopyReferenceAction extends DumbAwareAction { HighlightManager highlightManager = HighlightManager.getInstance(project); EditorColorsManager manager = EditorColorsManager.getInstance(); TextAttributes attributes = manager.getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES); - if (element != null && editor != null) { + if (element != null && editor != null && project != null) { PsiElement nameIdentifier = IdentifierUtil.getNameIdentifier(element); if (nameIdentifier != null) { highlightManager.addOccurrenceHighlights(editor, new PsiElement[]{nameIdentifier}, attributes, true, null); @@ -181,7 +182,7 @@ public class CopyReferenceAction extends DumbAwareAction { @Override public boolean isDataFlavorSupported(DataFlavor flavor) { - return ArrayUtil.find(getTransferDataFlavors(), flavor) != -1; + return ArrayUtilRt.find(getTransferDataFlavors(), flavor) != -1; } @Override @@ -238,16 +239,16 @@ public class CopyReferenceAction extends DumbAwareAction { } final Project project = file.getProject(); final LogicalRoot logicalRoot = LogicalRootsManager.getLogicalRootsManager(project).findLogicalRoot(virtualFile); - if (logicalRoot != null) { - String logical = FileUtil.toSystemIndependentName(VfsUtil.virtualToIoFile(logicalRoot.getVirtualFile()).getPath()); - String path = FileUtil.toSystemIndependentName(VfsUtil.virtualToIoFile(virtualFile).getPath()); - return "/" + FileUtil.getRelativePath(logical, path, '/'); + if (logicalRoot != null && logicalRoot.getVirtualFile() != null) { + String logical = FileUtil.toSystemIndependentName(VfsUtilCore.virtualToIoFile(logicalRoot.getVirtualFile()).getPath()); + String path = FileUtil.toSystemIndependentName(VfsUtilCore.virtualToIoFile(virtualFile).getPath()); + return ObjectUtils.assertNotNull(FileUtil.getRelativePath(logical, path, '/')); } final VirtualFile contentRoot = ProjectRootManager.getInstance(project).getFileIndex().getContentRootForFile(virtualFile); if (contentRoot != null) { - return "/" + FileUtil.getRelativePath(VfsUtil.virtualToIoFile(contentRoot), VfsUtil.virtualToIoFile(virtualFile)); - } + return ObjectUtils.assertNotNull(FileUtil.getRelativePath(VfsUtilCore.virtualToIoFile(contentRoot), VfsUtilCore.virtualToIoFile(virtualFile))); + } return virtualFile.getPath(); } } diff --git a/platform/lang-impl/src/com/intellij/ide/actions/CreateDirectoryOrPackageHandler.java b/platform/lang-impl/src/com/intellij/ide/actions/CreateDirectoryOrPackageHandler.java index e95196cecd54..9b8fc331a471 100644 --- a/platform/lang-impl/src/com/intellij/ide/actions/CreateDirectoryOrPackageHandler.java +++ b/platform/lang-impl/src/com/intellij/ide/actions/CreateDirectoryOrPackageHandler.java @@ -28,6 +28,7 @@ import com.intellij.openapi.fileTypes.UnknownFileType; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.InputValidatorEx; import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiFileSystemItem; @@ -104,7 +105,7 @@ public class CreateDirectoryOrPackageHandler implements InputValidatorEx { } boolean createFile = false; - if (StringUtil.countChars(subDirName, '.') == 1) { + if (StringUtil.countChars(subDirName, '.') == 1 && Registry.is("ide.suggest.file.when.creating.filename.like.directory")) { FileType fileType = findFileTypeBoundToName(subDirName); if (fileType != null) { String message = "The name you entered looks like a file name. Do you want to create a file named " + subDirName + " instead?"; diff --git a/platform/lang-impl/src/com/intellij/ide/actions/SearchEverywhereAction.java b/platform/lang-impl/src/com/intellij/ide/actions/SearchEverywhereAction.java index 558a29c4aae1..6da322a9ad11 100644 --- a/platform/lang-impl/src/com/intellij/ide/actions/SearchEverywhereAction.java +++ b/platform/lang-impl/src/com/intellij/ide/actions/SearchEverywhereAction.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. @@ -42,6 +42,7 @@ import com.intellij.openapi.actionSystem.impl.ActionToolbarImpl; import com.intellij.openapi.application.AccessToken; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.actions.TextComponentEditorAction; +import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileEditor.OpenFileDescriptor; import com.intellij.openapi.fileEditor.impl.EditorHistoryManager; import com.intellij.openapi.keymap.KeymapManager; @@ -438,10 +439,10 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA parent.repaint(); } }); - if (myPopup != null && myPopup.isVisible()) { - myPopup.cancel(); - myPopup = null; - } + //if (myPopup != null && myPopup.isVisible()) { + // myPopup.cancel(); + // myPopup = null; + //} rebuildList(""); } @@ -674,6 +675,7 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA myBalloon = builder .setCancelOnClickOutside(true) .setModalContext(false) + .setRequestFocus(true) .setCancelCallback(new Computable<Boolean>() { @Override public Boolean compute() { @@ -1393,7 +1395,14 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA private void buildRecentFiles(String pattern) { final MinusculeMatcher matcher = new MinusculeMatcher("*" + pattern, NameUtil.MatchingCaseSensitivity.NONE); final ArrayList<VirtualFile> files = new ArrayList<VirtualFile>(); + FileEditorManager editorManager = FileEditorManager.getInstance(project); + //we skip active editor + boolean skip = editorManager.getOpenFiles().length > 1; for (VirtualFile file : ArrayUtil.reverseArray(EditorHistoryManager.getInstance(project).getFiles())) { + if (skip) { + skip = false; + continue; + } if (StringUtil.isEmptyOrSpaces(pattern) || matcher.matches(file.getName())) { if (!files.contains(file)) { files.add(file); diff --git a/platform/lang-impl/src/com/intellij/ide/fileTemplates/impl/AllFileTemplatesConfigurable.java b/platform/lang-impl/src/com/intellij/ide/fileTemplates/impl/AllFileTemplatesConfigurable.java index c9cb262b46af..0b0055748132 100644 --- a/platform/lang-impl/src/com/intellij/ide/fileTemplates/impl/AllFileTemplatesConfigurable.java +++ b/platform/lang-impl/src/com/intellij/ide/fileTemplates/impl/AllFileTemplatesConfigurable.java @@ -358,6 +358,8 @@ public class AllFileTemplatesConfigurable implements SearchableConfigurable, Con } private void onTabChanged() { + applyEditor(myCurrentTab.getSelectedTemplate()); + final int selectedIndex = myTabbedPane.getSelectedIndex(); if (0 <= selectedIndex && selectedIndex < myTabs.length) { myCurrentTab = myTabs[selectedIndex]; @@ -371,17 +373,8 @@ public class AllFileTemplatesConfigurable implements SearchableConfigurable, Con if (prevTemplate != selectedValue) { LOG.assertTrue(myEditor != null, "selected:" + selectedValue + "; prev:" + prevTemplate); //selection has changed - if (myEditor.isModified() && Arrays.asList(myCurrentTab.getTemplates()).contains(prevTemplate)) { - try { - myModified = true; - myEditor.apply(); - fireListChanged(); - } - catch (ConfigurationException e) { - myCurrentTab.selectTemplate(prevTemplate); - Messages.showErrorDialog(myMainPanel, e.getMessage(), IdeBundle.message("title.cannot.save.current.template")); - return; - } + if (Arrays.asList(myCurrentTab.getTemplates()).contains(prevTemplate) && !applyEditor(prevTemplate)) { + return; } if (selectedValue == null) { myEditor.setTemplate(null, FileTemplateManagerImpl.getInstanceImpl().getDefaultTemplateDescription()); @@ -393,6 +386,24 @@ public class AllFileTemplatesConfigurable implements SearchableConfigurable, Con } } + private boolean applyEditor(FileTemplate prevTemplate) { + if (myEditor.isModified()) { + try { + myModified = true; + myEditor.apply(); + fireListChanged(); + } + catch (ConfigurationException e) { + if (Arrays.asList(myCurrentTab.getTemplates()).contains(prevTemplate)) { + myCurrentTab.selectTemplate(prevTemplate); + } + Messages.showErrorDialog(myMainPanel, e.getMessage(), IdeBundle.message("title.cannot.save.current.template")); + return false; + } + } + return true; + } + private void selectTemplate(FileTemplate template) { URL defDesc = null; if (myCurrentTab == myTemplatesList) { diff --git a/platform/lang-impl/src/com/intellij/ide/fileTemplates/impl/BundledFileTemplate.java b/platform/lang-impl/src/com/intellij/ide/fileTemplates/impl/BundledFileTemplate.java index 43833e79b3e1..0a1b4a5cb579 100644 --- a/platform/lang-impl/src/com/intellij/ide/fileTemplates/impl/BundledFileTemplate.java +++ b/platform/lang-impl/src/com/intellij/ide/fileTemplates/impl/BundledFileTemplate.java @@ -101,4 +101,9 @@ public final class BundledFileTemplate extends FileTemplateBase { public boolean isTextModified() { return !getText().equals(getDefaultText()); } + + @Override + public String toString() { + return myDefaultTemplate.getTemplateURL() == null ? "" : myDefaultTemplate.getTemplateURL().toString(); + } } diff --git a/platform/lang-impl/src/com/intellij/ide/fileTemplates/impl/FTManager.java b/platform/lang-impl/src/com/intellij/ide/fileTemplates/impl/FTManager.java index 1938241e6dab..b1b6cec2d2ad 100644 --- a/platform/lang-impl/src/com/intellij/ide/fileTemplates/impl/FTManager.java +++ b/platform/lang-impl/src/com/intellij/ide/fileTemplates/impl/FTManager.java @@ -188,7 +188,8 @@ class FTManager { final FileTemplateBase previous = myTemplates.put(qName, bundled); mySortedTemplates = null; - LOG.assertTrue(previous == null, "Duplicate bundled template " + qName); + LOG.assertTrue(previous == null, "Duplicate bundled template " + qName + + " [" + template.getTemplateURL() + ", " + previous + ']'); return bundled; } diff --git a/platform/lang-impl/src/com/intellij/ide/navigationToolbar/AbstractNavBarModelExtension.java b/platform/lang-impl/src/com/intellij/ide/navigationToolbar/AbstractNavBarModelExtension.java index 91202fbce271..50079c02b027 100644 --- a/platform/lang-impl/src/com/intellij/ide/navigationToolbar/AbstractNavBarModelExtension.java +++ b/platform/lang-impl/src/com/intellij/ide/navigationToolbar/AbstractNavBarModelExtension.java @@ -27,12 +27,10 @@ import java.util.Collections; /** * @author gregsh */ -public class AbstractNavBarModelExtension implements NavBarModelExtension { +public abstract class AbstractNavBarModelExtension implements NavBarModelExtension { @Nullable @Override - public String getPresentableText(Object object) { - return null; - } + public abstract String getPresentableText(Object object); @Nullable @Override diff --git a/platform/lang-impl/src/com/intellij/ide/projectView/BaseProjectTreeBuilder.java b/platform/lang-impl/src/com/intellij/ide/projectView/BaseProjectTreeBuilder.java index 4f7cae42da78..dd46f6496c46 100644 --- a/platform/lang-impl/src/com/intellij/ide/projectView/BaseProjectTreeBuilder.java +++ b/platform/lang-impl/src/com/intellij/ide/projectView/BaseProjectTreeBuilder.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. @@ -406,7 +406,8 @@ public abstract class BaseProjectTreeBuilder extends AbstractTreeBuilder { } } } else { - async.setRejected(); + //filter tells us to stop here (for instance, in case of module nodes) + break; } } async.setRejected(); diff --git a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/GotoFileCellRenderer.java b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/GotoFileCellRenderer.java index 51f93300ca70..108868ccd192 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/GotoFileCellRenderer.java +++ b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/GotoFileCellRenderer.java @@ -82,7 +82,7 @@ public class GotoFileCellRenderer extends PsiElementListCellRenderer<PsiFileSyst } @Nullable - private static String getRelativePath(final VirtualFile virtualFile, final Project project) { + static String getRelativePath(final VirtualFile virtualFile, final Project project) { String url = virtualFile.getPresentableUrl(); if (project == null) { return url; diff --git a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/GotoFileModel.java b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/GotoFileModel.java index 49081c710bde..de43de525209 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/GotoFileModel.java +++ b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/GotoFileModel.java @@ -128,7 +128,7 @@ public class GotoFileModel extends FilteringGotoByModel<FileType> { public String getFullName(final Object element) { if (element instanceof PsiFileSystemItem) { final VirtualFile virtualFile = ((PsiFileSystemItem)element).getVirtualFile(); - return virtualFile != null ? virtualFile.getPath() : null; + return virtualFile != null ? GotoFileCellRenderer.getRelativePath(virtualFile, myProject) : null; } return getElementName(element); diff --git a/platform/lang-impl/src/com/intellij/platform/ModuleAttachProcessor.java b/platform/lang-impl/src/com/intellij/platform/ModuleAttachProcessor.java index 6053d01cded8..e5f458af9293 100644 --- a/platform/lang-impl/src/com/intellij/platform/ModuleAttachProcessor.java +++ b/platform/lang-impl/src/com/intellij/platform/ModuleAttachProcessor.java @@ -161,6 +161,11 @@ public class ModuleAttachProcessor extends ProjectAttachProcessor { if (!canAttachToProject()) { return null; } + return findModuleInBaseDir(project); + } + + @Nullable + public static Module findModuleInBaseDir(Project project) { for (Module module : ModuleManager.getInstance(project).getModules()) { final VirtualFile[] roots = ModuleRootManager.getInstance(module).getContentRoots(); for (VirtualFile root : roots) { diff --git a/platform/lang-impl/src/com/intellij/platform/renameProject/RenameProjectHandler.java b/platform/lang-impl/src/com/intellij/platform/renameProject/RenameProjectHandler.java index 9baec60bca13..2d44c86723e9 100644 --- a/platform/lang-impl/src/com/intellij/platform/renameProject/RenameProjectHandler.java +++ b/platform/lang-impl/src/com/intellij/platform/renameProject/RenameProjectHandler.java @@ -33,6 +33,7 @@ import com.intellij.openapi.ui.InputValidator; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Ref; import com.intellij.platform.ModuleAttachProcessor; +import com.intellij.projectImport.ProjectAttachProcessor; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.refactoring.RefactoringBundle; @@ -101,7 +102,7 @@ public class RenameProjectHandler implements RenameHandler, TitledHandler { @Override public boolean canClose(final String inputString) { - if (!inputString.equals(myProject.getName()) && (myModule == null || myModule == ModuleAttachProcessor.getPrimaryModule(myProject))) { + if (shouldRenameProject(inputString)) { myProject.setProjectName(inputString); myProject.save(); } @@ -132,5 +133,21 @@ public class RenameProjectHandler implements RenameHandler, TitledHandler { } return true; } + + private boolean shouldRenameProject(String inputString) { + if (inputString.equals(myProject.getName())) { + return false; + } + + if (myModule == null) { + return true; + } + + if (ProjectAttachProcessor.canAttachToProject()) { + return myModule == ModuleAttachProcessor.getPrimaryModule(myProject); + } + + return myModule == ModuleAttachProcessor.findModuleInBaseDir(myProject); + } } } diff --git a/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java b/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java index bb98b700cf1a..1e8c431a5c0d 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java +++ b/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java @@ -2457,7 +2457,8 @@ public class FileBasedIndexImpl extends FileBasedIndex { @Override public void removeIndexableSet(@NotNull IndexableFileSet set) { - myChangedFilesCollector.forceUpdate(null, null, null, true); + if (!myIndexableSetToProjectMap.containsKey(set)) return; // avoid second unfiltered forceUpdate + myChangedFilesCollector.forceUpdate(myIndexableSetToProjectMap.get(set), null, null, true); IndexingStamp.flushCaches(); myIndexableSets.remove(set); myIndexableSetToProjectMap.remove(set); diff --git a/platform/platform-api/src/com/intellij/openapi/fileChooser/FileChooserDescriptor.java b/platform/platform-api/src/com/intellij/openapi/fileChooser/FileChooserDescriptor.java index a024b5b9d395..edcc97ec7cd5 100644 --- a/platform/platform-api/src/com/intellij/openapi/fileChooser/FileChooserDescriptor.java +++ b/platform/platform-api/src/com/intellij/openapi/fileChooser/FileChooserDescriptor.java @@ -81,27 +81,24 @@ public class FileChooserDescriptor implements Cloneable { return myTitle; } - public final FileChooserDescriptor setTitle(String title) { + public final void setTitle(String title) { myTitle = title; - return this; } public boolean isShowFileSystemRoots() { return myShowFileSystemRoots; } - public FileChooserDescriptor setShowFileSystemRoots(boolean showFileSystemRoots) { + public void setShowFileSystemRoots(boolean showFileSystemRoots) { myShowFileSystemRoots = showFileSystemRoots; - return this; } public final String getDescription() { return myDescription; } - public final FileChooserDescriptor setDescription(String description) { + public final void setDescription(String description) { myDescription = description; - return this; } public final boolean isChooseJarContents() { @@ -185,7 +182,6 @@ public class FileChooserDescriptor implements Cloneable { /** * the method is called upon pressing Ok in the FileChooserDialog * Override the method in order to customize validation of user input - * * @param files - selected files to be checked * @throws Exception if the the files cannot be accepted */ @@ -220,23 +216,21 @@ public class FileChooserDescriptor implements Cloneable { return JarFileSystem.getInstance().findFileByPath(path + JarFileSystem.JAR_SEPARATOR); } - public final FileChooserDescriptor setHideIgnored(boolean hideIgnored) { + public final void setHideIgnored(boolean hideIgnored) { myHideIgnored = hideIgnored; - return this; } public final List<VirtualFile> getRoots() { return Collections.unmodifiableList(myRoots); } - public final FileChooserDescriptor setRoots(final VirtualFile... roots) { - return setRoots(Arrays.asList(roots)); + public final void setRoots(final VirtualFile... roots) { + setRoots(Arrays.asList(roots)); } - public final FileChooserDescriptor setRoots(@NotNull final List<VirtualFile> roots) { + public final void setRoots(@NotNull final List<VirtualFile> roots) { myRoots.clear(); myRoots.addAll(roots); - return this; } public boolean isTreeRootVisible() { diff --git a/platform/platform-api/src/com/intellij/ui/AnActionButton.java b/platform/platform-api/src/com/intellij/ui/AnActionButton.java index ca695085c33a..0e84519c3bf8 100644 --- a/platform/platform-api/src/com/intellij/ui/AnActionButton.java +++ b/platform/platform-api/src/com/intellij/ui/AnActionButton.java @@ -15,6 +15,7 @@ */ package com.intellij.ui; +import com.intellij.ide.DataManager; import com.intellij.openapi.actionSystem.*; import com.intellij.ui.awt.RelativePoint; import com.intellij.util.ui.UIUtil; @@ -130,6 +131,10 @@ public abstract class AnActionButton extends AnAction implements ShortcutProvide return myContextComponent; } + public DataContext getDataContext() { + return DataManager.getInstance().getDataContext(getContextComponent()); + } + private boolean isContextComponentOk() { return myContextComponent == null || (myContextComponent.isVisible() && UIUtil.getParentOfType(JLayeredPane.class, myContextComponent) != null); diff --git a/platform/platform-api/src/com/intellij/ui/DumbAwareActionButton.java b/platform/platform-api/src/com/intellij/ui/DumbAwareActionButton.java new file mode 100644 index 000000000000..552ee968397f --- /dev/null +++ b/platform/platform-api/src/com/intellij/ui/DumbAwareActionButton.java @@ -0,0 +1,41 @@ +/* + * 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.ui; + +import com.intellij.openapi.project.DumbAware; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; + +/** + * @author gregsh + */ +public abstract class DumbAwareActionButton extends AnActionButton implements DumbAware { + public DumbAwareActionButton(String text) { + super(text); + } + + public DumbAwareActionButton(String text, String description, @Nullable Icon icon) { + super(text, description, icon); + } + + public DumbAwareActionButton(String text, Icon icon) { + super(text, icon); + } + + public DumbAwareActionButton() { + } +} diff --git a/platform/platform-api/src/com/intellij/ui/table/JBTable.java b/platform/platform-api/src/com/intellij/ui/table/JBTable.java index ea2eebb5a127..f7f183fedbf2 100644 --- a/platform/platform-api/src/com/intellij/ui/table/JBTable.java +++ b/platform/platform-api/src/com/intellij/ui/table/JBTable.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. @@ -449,6 +449,7 @@ public class JBTable extends JTable implements ComponentWithEmptyText, Component return UIUtil.isUnderAlloyLookAndFeel() || UIUtil.isUnderNativeMacLookAndFeel() || UIUtil.isUnderDarcula() + || UIUtil.isUnderIntelliJLaF() || UIUtil.isUnderNimbusLookAndFeel() || UIUtil.isUnderWindowsLookAndFeel(); } diff --git a/platform/platform-impl/src/com/intellij/diagnostic/DebugLogManager.java b/platform/platform-impl/src/com/intellij/diagnostic/DebugLogManager.java new file mode 100644 index 000000000000..39fa3c23eeb3 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/diagnostic/DebugLogManager.java @@ -0,0 +1,100 @@ +/* + * Copyright 2000-2013 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.diagnostic; + +import com.intellij.ide.util.PropertiesComponent; +import com.intellij.openapi.components.ApplicationComponent; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.Function; +import com.intellij.util.containers.ContainerUtil; +import org.apache.log4j.Level; +import org.apache.log4j.LogManager; +import com.intellij.openapi.diagnostic.Logger; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; + +/** + * Allows to apply & persist custom log debug categories which can be turned on by user via the {@link com.intellij.ide.actions.DebugLogConfigureAction}. <br/> + * Applies these custom categories on startup. + */ +public class DebugLogManager extends ApplicationComponent.Adapter { + + private static final Logger LOG = Logger.getInstance(DebugLogManager.class); + private static final String LOG_DEBUG_CATEGORIES = "log.debug.categories"; + + @Override + public void initComponent() { + List<String> categories = getSavedCategories(); + if (categories.isEmpty()) { + saveCategories(getCurrentCategories()); + } + else { + applyCategories(categories); + } + } + + @NotNull + public List<String> getSavedCategories() { + String value = PropertiesComponent.getInstance().getValue(LOG_DEBUG_CATEGORIES); + return value == null ? Collections.<String>emptyList() : fromString(value); + } + + public void applyCategories(@NotNull List<String> categories) { + for (String category : categories) { + org.apache.log4j.Logger logger = LogManager.getLogger(category); + if (logger != null) { + logger.setLevel(Level.DEBUG); + } + } + LOG.info("Set DEBUG for the following categories: " + categories); + } + + public void saveCategories(@NotNull List<String> categories) { + PropertiesComponent.getInstance().setValue(LOG_DEBUG_CATEGORIES, toString(categories)); + } + + @NotNull + private static List<String> fromString(@NotNull String text) { + return Arrays.asList(StringUtil.splitByLines(text, true)); + } + + @NotNull + private static String toString(@NotNull List<String> categories) { + return StringUtil.join(categories, "\n"); + } + + @NotNull + private static List<String> getCurrentCategories() { + Enumeration currentLoggers = LogManager.getCurrentLoggers(); + return ContainerUtil.mapNotNull(ContainerUtil.toList(currentLoggers), new Function<Object, String>() { + @Override + public String fun(Object o) { + if (o instanceof org.apache.log4j.Logger) { + String category = ((org.apache.log4j.Logger)o).getName(); + if (Logger.getInstance(category).isDebugEnabled()) { + return category; + } + } + return null; + } + }); + } + +} diff --git a/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.java b/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.java index 9e67590713ef..96d11aa2a2e3 100644 --- a/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.java +++ b/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.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. @@ -692,6 +692,7 @@ public class IdeEventQueue extends EventQueue { myDispatchingFocusEvent = e instanceof FocusEvent; maybeReady(); + fixStickyAlt(e); super.dispatchEvent(e); } @@ -705,6 +706,13 @@ public class IdeEventQueue extends EventQueue { } } + //IDEA-17359 + private static void fixStickyAlt(AWTEvent e) { + if (SystemInfo.isWindowsXP && e instanceof KeyEvent && ((KeyEvent)e).getKeyCode() == KeyEvent.VK_ALT) { + ((KeyEvent)e).consume(); + } + } + public boolean isDispatchingFocusEvent() { return myDispatchingFocusEvent; } @@ -889,6 +897,8 @@ public class IdeEventQueue extends EventQueue { boolean dispatch = true; if (e instanceof KeyEvent) { KeyEvent ke = (KeyEvent)e; + final Component component = ke.getComponent(); + final Window window = component == null ? null : SwingUtilities.windowForComponent(component); boolean pureAlt = ke.getKeyCode() == KeyEvent.VK_ALT && (ke.getModifiers() | InputEvent.ALT_MASK) == InputEvent.ALT_MASK; if (!pureAlt) { myPureAltWasPressed = false; @@ -917,10 +927,14 @@ public class IdeEventQueue extends EventQueue { } else { myWaiterScheduled = true; + //noinspection SSBasedInspection SwingUtilities.invokeLater(new Runnable() { @Override public void run() { try { + if (SystemInfo.isWindows || window == null || !window.isActive()) { + return; + } myWaitingForAltRelease = true; if (myRobot == null) { myRobot = new Robot(); diff --git a/platform/platform-impl/src/com/intellij/ide/actions/DebugLogConfigureAction.java b/platform/platform-impl/src/com/intellij/ide/actions/DebugLogConfigureAction.java new file mode 100644 index 000000000000..8cefd4eb320e --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ide/actions/DebugLogConfigureAction.java @@ -0,0 +1,91 @@ +/* + * Copyright 2000-2013 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.ide.actions; + +import com.intellij.diagnostic.DebugLogManager; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.project.DumbAwareAction; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.ProjectManager; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.ui.ScrollPaneFactory; +import com.intellij.ui.components.JBLabel; +import com.intellij.util.Function; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.util.List; + +public class DebugLogConfigureAction extends DumbAwareAction { + + @Override + public void actionPerformed(AnActionEvent e) { + Project project = e.getProject() == null ? ProjectManager.getInstance().getDefaultProject() : e.getProject(); + DebugLogManager logCustomizer = ServiceManager.getService(DebugLogManager.class); + DebugLogConfigureDialog dialog = new DebugLogConfigureDialog(project, logCustomizer.getSavedCategories()); + if (dialog.showAndGet()) { + List<String> categories = dialog.getLogCategories(); + logCustomizer.applyCategories(categories); + logCustomizer.saveCategories(categories); + } + } + + private static class DebugLogConfigureDialog extends DialogWrapper { + + private static final String ALL_POSSIBLE_SEPARATORS = "(\n|,|;)+"; + @NotNull private final JTextArea myTextArea; + + protected DebugLogConfigureDialog(@Nullable Project project, List<String> categories) { + super(project, false); + myTextArea = new JTextArea(10, 30); + myTextArea.setText(StringUtil.join(categories, "\n")); + setTitle("Custom Debug Log Configuration"); + init(); + } + + @Nullable + @Override + protected JComponent createNorthPanel() { + return new JBLabel("Add log categories separated by new lines"); + } + + @Nullable + @Override + protected JComponent createCenterPanel() { + return ScrollPaneFactory.createScrollPane(myTextArea); + } + + @NotNull + public List<String> getLogCategories() { + return parseCategories(myTextArea.getText()); + } + + @NotNull + private static List<String> parseCategories(@NotNull String text) { + return ContainerUtil.mapNotNull(text.split(ALL_POSSIBLE_SEPARATORS), new Function<String, String>() { + @Override + public String fun(String s) { + return StringUtil.isEmptyOrSpaces(s) ? null : s.trim(); + } + }); + } + } + +} diff --git a/platform/platform-impl/src/com/intellij/ide/actions/ShowRecentFilesAction.java b/platform/platform-impl/src/com/intellij/ide/actions/ShowRecentFilesAction.java index 47cb36d0f166..b05f14283689 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/ShowRecentFilesAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/ShowRecentFilesAction.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. @@ -23,7 +23,6 @@ import com.intellij.featureStatistics.FeatureUsageTracker; import com.intellij.ide.IdeBundle; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; -import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; @@ -36,10 +35,7 @@ public class ShowRecentFilesAction extends DumbAwareAction { final Project project = e.getData(CommonDataKeys.PROJECT); if (project != null) { FeatureUsageTracker.getInstance().triggerFeatureUsed("navigation.recent.files"); - final Switcher.SwitcherPanel switcher = Switcher.createAndShowSwitcher(project, IdeBundle.message("title.popup.recent.files"), true); - if (FileEditorManagerEx.getInstanceEx(project).hasOpenedFile()) { - switcher.goForward(); - } + Switcher.createAndShowSwitcher(project, IdeBundle.message("title.popup.recent.files"), true); } } } diff --git a/platform/platform-impl/src/com/intellij/ide/actions/Switcher.java b/platform/platform-impl/src/com/intellij/ide/actions/Switcher.java index 57b707b54947..8884e7d42ab5 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/Switcher.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/Switcher.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. @@ -345,7 +345,12 @@ public class Switcher extends AnAction implements DumbAware { final int maxFiles = Math.max(editors.size(), recentFiles.length); final int len = isPinnedMode() ? recentFiles.length : Math.min(toolWindows.getModel().getSize(), maxFiles); boolean firstRecentMarked = false; + final List<VirtualFile> selectedFiles = Arrays.asList(editorManager.getSelectedFiles()); for (int i = 0; i < len; i++) { + if (isPinnedMode() && selectedFiles.contains(recentFiles[i])) { + continue; + } + final FileInfo info = new FileInfo(recentFiles[i], null); boolean add = true; if (isPinnedMode()) { diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/DarculaLaf.java b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/DarculaLaf.java index a708844200b7..f858002f0743 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/DarculaLaf.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/DarculaLaf.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. @@ -227,6 +227,7 @@ public class DarculaLaf extends BasicLookAndFeel { //"ENTER", "selectNextRowCell", "shift ENTER", "selectPreviousRowCell", "ctrl A", "selectAll", + "meta A", "selectAll", //"ESCAPE", "cancel", "F2", "startEditing" })); diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaComboBoxUI.java b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaComboBoxUI.java index cc0903619e28..0eba3d3ce28a 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaComboBoxUI.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaComboBoxUI.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. @@ -309,14 +309,19 @@ public class DarculaComboBoxUI extends BasicComboBoxUI implements Border { comboBoxEditor.getEditorComponent().addFocusListener(new FocusAdapter() { @Override public void focusGained(FocusEvent e) { - comboBox.revalidate(); - comboBox.repaint(); + update(); + } + + void update() { + if (comboBox != null) { + comboBox.revalidate(); + comboBox.repaint(); + } } @Override public void focusLost(FocusEvent e) { - comboBox.revalidate(); - comboBox.repaint(); + update(); } }); } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/OpenFilesActivity.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/OpenFilesActivity.java index 94d0883c325c..62866d357dde 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/OpenFilesActivity.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/OpenFilesActivity.java @@ -16,8 +16,6 @@ package com.intellij.openapi.fileEditor.impl; import com.intellij.openapi.fileEditor.FileEditorManager; -import com.intellij.openapi.progress.ProgressIndicator; -import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; import com.intellij.openapi.startup.StartupActivity; @@ -44,15 +42,7 @@ public class OpenFilesActivity implements StartupActivity, DumbAware { } }; if (Registry.is("ide.open.editors.asynchronously")) { - ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); - if (indicator != null) { - indicator.pushState(); - indicator.setText("Preparing editors to open..."); - } runnable.run(); - if (indicator != null) { - indicator.popState(); - } } else { UIUtil.invokeLaterIfNeeded(runnable); diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/ex/dummy/DummyCachingFileSystem.java b/platform/platform-impl/src/com/intellij/openapi/vfs/ex/dummy/DummyCachingFileSystem.java index 516480781c80..5d27509a6702 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/ex/dummy/DummyCachingFileSystem.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/ex/dummy/DummyCachingFileSystem.java @@ -114,6 +114,7 @@ public abstract class DummyCachingFileSystem<T extends VirtualFile> extends Dumm throw new UnsupportedOperationException("not implemented"); } + @Nullable public Project getProject(String projectId) { List<Project> list = myProject2Id.getKeysByValue(projectId); return list == null || list.size() > 1 ? null : list.get(0); diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/ToolWindowsWidget.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/ToolWindowsWidget.java index 08504b9e2609..d4477a330542 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/ToolWindowsWidget.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/ToolWindowsWidget.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. @@ -26,6 +26,7 @@ import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.ui.popup.PopupChooserBuilder; import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.wm.*; import com.intellij.openapi.wm.impl.IdeFrameImpl; @@ -110,7 +111,7 @@ class ToolWindowsWidget extends JLabel implements CustomStatusBarWidget, StatusB popup.cancel(); } } - }, 150); + }, 300); return true; } } @@ -155,7 +156,7 @@ class ToolWindowsWidget extends JLabel implements CustomStatusBarWidget, StatusB final Dimension size = list.getPreferredSize(); final JComponent c = ToolWindowsWidget.this; final Insets padding = UIUtil.getListViewportPadding(); - final RelativePoint point = new RelativePoint(c, new Point(-4, -padding.top - padding.bottom -4 - size.height)); + final RelativePoint point = new RelativePoint(c, new Point(-4, -padding.top - padding.bottom -4 - size.height + (SystemInfo.isMac ? 2 : 0))); if (popup != null && popup.isVisible()) { return; diff --git a/platform/platform-resources-en/src/messages/ActionsBundle.properties b/platform/platform-resources-en/src/messages/ActionsBundle.properties index 87e2cb151662..a65aac00aa55 100644 --- a/platform/platform-resources-en/src/messages/ActionsBundle.properties +++ b/platform/platform-resources-en/src/messages/ActionsBundle.properties @@ -1062,6 +1062,8 @@ action.Graph.print.preview=Print Preview action.SendFeedback.text=Submit _Feedback action.SendFeedback.description=Submit feedback to the JetBrains Web site +action.LogDebugConfigure.text=Configure Debug Log Settings +action.LogDebugConfigure.description=Enable or disable additional log categories. Allows to provide more information to the support team by request. action.ShowLog.text=Open Log file action.ShowLog.description=Opens folder with log file action.TechnicalSupport.text=Contact _Support Team diff --git a/platform/platform-resources-en/src/misc/registry.properties b/platform/platform-resources-en/src/misc/registry.properties index a12f869d2b64..9dfe64a0dc28 100644 --- a/platform/platform-resources-en/src/misc/registry.properties +++ b/platform/platform-resources-en/src/misc/registry.properties @@ -216,6 +216,9 @@ psi.incremental.reparse.depth.limit=1000 psi.viewer.selection.color=0,153,153 psi.deferIconLoading=true +ide.suggest.file.when.creating.filename.like.directory=true +ide.suggest.file.when.creating.filename.like.directory.description=When creating a directory named foo.txt, suggest to create a file with such a name instead + find.search.in.project.files=false structureView.coalesceTime=500 @@ -235,6 +238,7 @@ projectView.showHierarchyErrors=true projectView.hide.dot.idea=true show.live.templates.in.completion=false ide.completion.autopopup.select.live.templates=false +ide.completion.autopopup.choose.by.enter=true documentation.component.editor.font=false ide.completion.show.better.matching.classes=true diff --git a/platform/platform-resources/src/componentSets/Platform.xml b/platform/platform-resources/src/componentSets/Platform.xml index 93daf4fceee2..ef911ec4b8b1 100644 --- a/platform/platform-resources/src/componentSets/Platform.xml +++ b/platform/platform-resources/src/componentSets/Platform.xml @@ -137,6 +137,10 @@ <implementation-class>com.intellij.ide.SystemHealthMonitor</implementation-class> <headless-implementation-class/> </component> + + <component> + <implementation-class>com.intellij.diagnostic.DebugLogManager</implementation-class> + </component> </application-components> <project-components> diff --git a/platform/platform-resources/src/idea/PlatformActions.xml b/platform/platform-resources/src/idea/PlatformActions.xml index f5ac0d5e0e6f..f31b83c52802 100644 --- a/platform/platform-resources/src/idea/PlatformActions.xml +++ b/platform/platform-resources/src/idea/PlatformActions.xml @@ -409,6 +409,7 @@ <separator/> <action id="TechnicalSupport" class="com.intellij.ide.actions.TechnicalSupportAction"/> <action id="SendFeedback" class="com.intellij.ide.actions.SendFeedbackAction"/> + <action id="LogDebugConfigure" class="com.intellij.ide.actions.DebugLogConfigureAction"/> <action id="ShowLog" class="com.intellij.ide.actions.ShowLogAction"/> <separator/> <action id="OnlineDocAction" class="com.intellij.ide.actions.OnlineDocAction"/> diff --git a/platform/usageView/src/com/intellij/usages/impl/UsageViewTreeCellRenderer.java b/platform/usageView/src/com/intellij/usages/impl/UsageViewTreeCellRenderer.java index 06913a5667e6..06815acf5d73 100644 --- a/platform/usageView/src/com/intellij/usages/impl/UsageViewTreeCellRenderer.java +++ b/platform/usageView/src/com/intellij/usages/impl/UsageViewTreeCellRenderer.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 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. @@ -18,19 +18,24 @@ package com.intellij.usages.impl; import com.intellij.navigation.ItemPresentation; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.colors.EditorColorsScheme; +import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.FileStatus; import com.intellij.ui.ColoredTreeCellRenderer; +import com.intellij.ui.DarculaColors; import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.speedSearch.SpeedSearchUtil; import com.intellij.usageView.UsageTreeColors; import com.intellij.usageView.UsageTreeColorsScheme; import com.intellij.usageView.UsageViewBundle; import com.intellij.usages.*; +import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; +import java.awt.*; /** * @author max @@ -40,6 +45,7 @@ class UsageViewTreeCellRenderer extends ColoredTreeCellRenderer { private static final EditorColorsScheme ourColorsScheme = UsageTreeColorsScheme.getInstance().getScheme(); private static final SimpleTextAttributes ourInvalidAttributes = SimpleTextAttributes.fromTextAttributes(ourColorsScheme.getAttributes(UsageTreeColors.INVALID_PREFIX)); private static final SimpleTextAttributes ourReadOnlyAttributes = SimpleTextAttributes.fromTextAttributes(ourColorsScheme.getAttributes(UsageTreeColors.READONLY_PREFIX)); + private static final SimpleTextAttributes ourInvalidAttributesDarcula = new SimpleTextAttributes(null, DarculaColors.RED, null, ourInvalidAttributes.getStyle()); private final SimpleTextAttributes myNumberOfUsagesAttribute; private final UsageViewPresentation myPresentation; @@ -57,7 +63,7 @@ class UsageViewTreeCellRenderer extends ColoredTreeCellRenderer { if (value instanceof Node && tree != null && value != tree.getModel().getRoot()) { Node node = (Node)value; if (!node.isValid()) { - append(UsageViewBundle.message("node.invalid") + " ", ourInvalidAttributes); + append(UsageViewBundle.message("node.invalid") + " ", UIUtil.isUnderDarcula() ? ourInvalidAttributesDarcula : ourInvalidAttributes); } if (myPresentation.isShowReadOnlyStatusAsRed() && node.isReadOnly()) { showAsReadOnly = true; diff --git a/platform/util-rt/src/com/intellij/util/containers/ContainerUtilRt.java b/platform/util-rt/src/com/intellij/util/containers/ContainerUtilRt.java index 453b3bda47e1..2304be050c11 100644 --- a/platform/util-rt/src/com/intellij/util/containers/ContainerUtilRt.java +++ b/platform/util-rt/src/com/intellij/util/containers/ContainerUtilRt.java @@ -21,6 +21,7 @@ import com.intellij.util.Function; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.Serializable; import java.util.*; import java.util.HashMap; import java.util.HashSet; @@ -236,7 +237,9 @@ public class ContainerUtilRt { * A variant of {@link java.util.Collections#emptyList()}, * except that {@link #toArray()} here does not create garbage <code>new Object[0]</code> constantly. */ - private static class EmptyList<T> extends AbstractList<T> implements RandomAccess { + private static class EmptyList<T> extends AbstractList<T> implements RandomAccess, Serializable { + private static final long serialVersionUID = 1L; + private static final EmptyList INSTANCE = new EmptyList(); @Override diff --git a/platform/util/src/com/intellij/openapi/util/SystemInfo.java b/platform/util/src/com/intellij/openapi/util/SystemInfo.java index c5ce5bb7e95d..aff4475faa94 100644 --- a/platform/util/src/com/intellij/openapi/util/SystemInfo.java +++ b/platform/util/src/com/intellij/openapi/util/SystemInfo.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. @@ -52,6 +52,7 @@ public class SystemInfo extends SystemInfoRt { public static final boolean isWin2kOrNewer = isWindows && isOsVersionAtLeast("5.0"); public static final boolean isWinVistaOrNewer = isWindows && isOsVersionAtLeast("6.0"); public static final boolean isWin7OrNewer = isWindows && isOsVersionAtLeast("6.1"); + public static final boolean isWindowsXP = isWindows && (OS_VERSION.equals("5.1") || OS_VERSION.equals("5.2")); public static final boolean isXWindow = isUnix && !isMac; diff --git a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogBranchFilter.java b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogBranchFilter.java new file mode 100644 index 000000000000..2b658cbd3520 --- /dev/null +++ b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogBranchFilter.java @@ -0,0 +1,28 @@ +/* + * 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.vcs.log; + +import org.jetbrains.annotations.NotNull; + +/** + * Tells to filter by some branch name. + */ +public interface VcsLogBranchFilter extends VcsLogFilter { + + @NotNull + String getBranchName(); + +} diff --git a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogDateFilter.java b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogDateFilter.java new file mode 100644 index 000000000000..d080b67a3e4c --- /dev/null +++ b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogDateFilter.java @@ -0,0 +1,40 @@ +/* + * 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.vcs.log; + +import org.jetbrains.annotations.Nullable; + +import java.util.Date; + +/** + * Tells to filter by date. <br/> + * Only before or after dates can be given, or both can be given. + */ +public interface VcsLogDateFilter { + + /** + * If not null, only commits made after the returned date (inclusively) should be accepted. + */ + @Nullable + Date getAfter(); + + /** + * If not null, only commits made before the returned date (inclusively) should be accepted. + */ + @Nullable + Date getBefore(); + +} diff --git a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogProvider.java b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogProvider.java index 0a730270c4a3..ae8ee646a898 100644 --- a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogProvider.java +++ b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogProvider.java @@ -84,7 +84,12 @@ public interface VcsLogProvider { */ @NotNull List<? extends VcsFullCommitDetails> getFilteredDetails(@NotNull VirtualFile root, - @NotNull Collection<VcsLogFilter> filters, int maxCount) throws VcsException; + @NotNull Collection<VcsLogBranchFilter> branchFilters, + @NotNull Collection<VcsLogUserFilter> userFilters, + @NotNull Collection<VcsLogDateFilter> dateFilters, + @NotNull Collection<VcsLogTextFilter> textFilters, + @NotNull Collection<VcsLogStructureFilter> structureFilters, + int maxCount) throws VcsException; /** * Returns the name of current user as specified for the given root, diff --git a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogStructureFilter.java b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogStructureFilter.java new file mode 100644 index 000000000000..dd0ed62857c4 --- /dev/null +++ b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogStructureFilter.java @@ -0,0 +1,37 @@ +/* + * 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.vcs.log; + +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; + +/** + * Tells the log to filter by files and folders. + */ +public interface VcsLogStructureFilter { + + /** + * <p>Returns files from the given VCS root, which are affected by matching commits, and folders containing such files.</p> + * + * <p>That is: the commit A (made in the given VCS root) modifying file f.txt matches this filter, + * if this method returns a set which includes a folder containing f.txt, or the file f.txt itself.</p> + */ + @NotNull + Collection<VirtualFile> getFiles(@NotNull VirtualFile root); + +} diff --git a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogTextFilter.java b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogTextFilter.java new file mode 100644 index 000000000000..06d84e997201 --- /dev/null +++ b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogTextFilter.java @@ -0,0 +1,31 @@ +/* + * 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.vcs.log; + +import org.jetbrains.annotations.NotNull; + +/** + * Specifies the log filter by text. + */ +public interface VcsLogTextFilter { + + /** + * Only commits containing the returned text it their commit messages should match the filter. + */ + @NotNull + String getText(); + +} diff --git a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogUserFilter.java b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogUserFilter.java new file mode 100644 index 000000000000..1cd6d95dd82d --- /dev/null +++ b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogUserFilter.java @@ -0,0 +1,18 @@ +package com.intellij.vcs.log; + +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; + +/** + * Filters log by user. + */ +public interface VcsLogUserFilter extends VcsLogFilter { + + /** + * Returns the user name selected in the filter for the given root. + * If it is a name-as-text filter, of course, values don't differ per root. The difference appears if the special "me" filter is used. + */ + @NotNull + String getUserName(@NotNull VirtualFile root); + +} diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogBranchFilter.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogBranchFilterImpl.java index f7a4a62d1b05..90ca1e3cc000 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogBranchFilter.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogBranchFilterImpl.java @@ -2,17 +2,18 @@ package com.intellij.vcs.log.data; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; +import com.intellij.vcs.log.VcsLogBranchFilter; import com.intellij.vcs.log.VcsRef; import org.jetbrains.annotations.NotNull; import java.util.Collection; -public class VcsLogBranchFilter implements VcsLogGraphFilter { +public class VcsLogBranchFilterImpl implements VcsLogBranchFilter, VcsLogGraphFilter { @NotNull private final Collection<Integer> myMatchingHeads; @NotNull private final String myBranchName; - public VcsLogBranchFilter(@NotNull Collection<VcsRef> allRefs, @NotNull final String selectedBranchName) { + public VcsLogBranchFilterImpl(@NotNull Collection<VcsRef> allRefs, @NotNull final String selectedBranchName) { myBranchName = selectedBranchName; myMatchingHeads = ContainerUtil.mapNotNull(allRefs, new Function<VcsRef, Integer>() { @Override @@ -35,6 +36,7 @@ public class VcsLogBranchFilter implements VcsLogGraphFilter { return "on: " + myBranchName; } + @Override @NotNull public String getBranchName() { return myBranchName; diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogDataHolder.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogDataHolder.java index 547bb248dd48..f18697ae60b2 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogDataHolder.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogDataHolder.java @@ -24,6 +24,7 @@ import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vcs.VcsException; @@ -485,11 +486,25 @@ public class VcsLogDataHolder implements Disposable { runInBackground(new ThrowableConsumer<ProgressIndicator, VcsException>() { @Override public void consume(ProgressIndicator indicator) throws VcsException { + List<VcsLogBranchFilter> branchFilters = ContainerUtil.findAll(filters, VcsLogBranchFilter.class); + List<VcsLogUserFilter> userFilters = ContainerUtil.findAll(filters, VcsLogUserFilter.class); + List<VcsLogDateFilter> dateFilters = ContainerUtil.findAll(filters, VcsLogDateFilter.class); + List<VcsLogTextFilter> textFilters = ContainerUtil.findAll(filters, VcsLogTextFilter.class); + List<VcsLogStructureFilter> structureFilters = ContainerUtil.findAll(filters, VcsLogStructureFilter.class); Collection<List<? extends TimedVcsCommit>> logs = ContainerUtil.newArrayList(); final Map<Hash, VcsFullCommitDetails> allDetails = ContainerUtil.newHashMap(); for (Map.Entry<VirtualFile, VcsLogProvider> entry : myLogProviders.entrySet()) { - List<? extends VcsFullCommitDetails> details = entry.getValue().getFilteredDetails(entry.getKey(), filters, maxCount); + VirtualFile root = entry.getKey(); + + List<VcsLogStructureFilter> rootMatchingStructureFilters = filterStructureFiltersByRoot(root, structureFilters); + if (rootMatchingStructureFilters.isEmpty() && !structureFilters.isEmpty()) { + // there were structure filters but none matches the root + continue; + } + + List<? extends VcsFullCommitDetails> details = entry.getValue().getFilteredDetails( + root, branchFilters, userFilters, dateFilters, textFilters, rootMatchingStructureFilters, maxCount); logs.add(getCommitsFromDetails(details)); for (VcsFullCommitDetails detail : details) { allDetails.put(detail.getHash(), detail); @@ -530,6 +545,17 @@ public class VcsLogDataHolder implements Disposable { } @NotNull + private static List<VcsLogStructureFilter> filterStructureFiltersByRoot(@NotNull final VirtualFile root, + @NotNull List<VcsLogStructureFilter> structureFilters) { + return ContainerUtil.filter(structureFilters, new Condition<VcsLogStructureFilter>() { + @Override + public boolean value(VcsLogStructureFilter filter) { + return !filter.getFiles(root).isEmpty(); + } + }); + } + + @NotNull public Map<VirtualFile, VcsUser> getCurrentUser() { return myCurrentUser; } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogDateFilter.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogDateFilterImpl.java index 52103a5cd2b7..c879ddd88838 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogDateFilter.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogDateFilterImpl.java @@ -16,17 +16,18 @@ package com.intellij.vcs.log.data; import com.intellij.vcs.log.VcsFullCommitDetails; +import com.intellij.vcs.log.VcsLogDateFilter; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Date; -public class VcsLogDateFilter implements VcsLogDetailsFilter { +public class VcsLogDateFilterImpl implements VcsLogDateFilter, VcsLogDetailsFilter { @Nullable private final Date myAfter; @Nullable private final Date myBefore; - public VcsLogDateFilter(@Nullable Date after, @Nullable Date before) { + public VcsLogDateFilterImpl(@Nullable Date after, @Nullable Date before) { myAfter = after; myBefore = before; } @@ -44,11 +45,13 @@ public class VcsLogDateFilter implements VcsLogDetailsFilter { return matches; } + @Override @Nullable public Date getAfter() { return myAfter; } + @Override @Nullable public Date getBefore() { return myBefore; diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogFilterer.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogFilterer.java index 2c3011135dd5..da4d79d1ba3e 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogFilterer.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogFilterer.java @@ -3,6 +3,7 @@ package com.intellij.vcs.log.data; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.EmptyRunnable; import com.intellij.openapi.util.Ref; import com.intellij.util.Consumer; import com.intellij.util.Function; @@ -87,13 +88,13 @@ public class VcsLogFilterer { myUI.updateUI(); if (model.getRowCount() == 0) { - model.requestToLoadMore(); + model.requestToLoadMore(EmptyRunnable.INSTANCE); } } }); } - public void requestVcs(@NotNull Collection<VcsLogFilter> filters, final LoadMoreStage loadMoreStage) { + public void requestVcs(@NotNull Collection<VcsLogFilter> filters, final LoadMoreStage loadMoreStage, @NotNull final Runnable onSuccess) { ApplicationManager.getApplication().assertIsDispatchThread(); int maxCount; if (loadMoreStage == LoadMoreStage.INITIAL) { @@ -108,6 +109,7 @@ public class VcsLogFilterer { LoadMoreStage newLoadMoreStage = advanceLoadMoreStage(loadMoreStage); myUI.setModel(new NoGraphTableModel(myUI, details, myLogDataHolder.getDataPack().getRefsModel(), newLoadMoreStage)); myUI.updateUI(); + onSuccess.run(); } }, maxCount); } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogStructureFilter.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogStructureFilterImpl.java index 6a0f4e3b7ce4..678cfd6fea17 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogStructureFilter.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogStructureFilterImpl.java @@ -24,17 +24,18 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.MultiMap; import com.intellij.vcs.log.VcsFullCommitDetails; +import com.intellij.vcs.log.VcsLogStructureFilter; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; -public class VcsLogStructureFilter implements VcsLogDetailsFilter { +public class VcsLogStructureFilterImpl implements VcsLogDetailsFilter, VcsLogStructureFilter { @NotNull private final Collection<VirtualFile> myFiles; @NotNull private final MultiMap<VirtualFile, VirtualFile> myFilesByRoots; - public VcsLogStructureFilter(@NotNull Collection<VirtualFile> files, Collection<VirtualFile> roots) { + public VcsLogStructureFilterImpl(@NotNull Collection<VirtualFile> files, Collection<VirtualFile> roots) { myFiles = files; myFilesByRoots = groupFilesByVcsRoots(files, roots); } @@ -87,6 +88,7 @@ public class VcsLogStructureFilter implements VcsLogDetailsFilter { }) != null; } + @Override @NotNull public Collection<VirtualFile> getFiles(@NotNull VirtualFile root) { return myFilesByRoots.get(root); diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogUserFilter.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogUserFilter.java deleted file mode 100644 index 492c3108a91d..000000000000 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogUserFilter.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.intellij.vcs.log.data; - -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.vcs.log.VcsFullCommitDetails; -import com.intellij.vcs.log.VcsUser; -import org.jetbrains.annotations.NotNull; - -import java.util.Map; - -/** - * Filters log by user. - */ -public abstract class VcsLogUserFilter implements VcsLogDetailsFilter { - - /** - * Filters by the given name or part of it. - */ - public static class ByName extends VcsLogUserFilter { - - @NotNull private final String myUser; - - public ByName(@NotNull String user) { - myUser = user; - } - - @Override - public boolean matches(@NotNull VcsFullCommitDetails detail) { - return detail.getAuthor().getName().toLowerCase().contains(myUser.toLowerCase()) || - detail.getAuthor().getEmail().toLowerCase().contains(myUser.toLowerCase()); - } - - @NotNull - @Override - public String getUserName(@NotNull VirtualFile root) { - return myUser; - } - } - - /** - * Looks for commits matching the current user, - * i.e. looks for the value stored in the VCS config and compares the configured name with the one returned in commit details. - */ - public static class Me extends VcsLogUserFilter { - - @NotNull private final Map<VirtualFile, VcsUser> myMeData; - - public Me(@NotNull Map<VirtualFile, VcsUser> meData) { - myMeData = meData; - } - - @Override - public boolean matches(@NotNull VcsFullCommitDetails details) { - VcsUser meInThisRoot = myMeData.get(details.getRoot()); - return meInThisRoot != null && meInThisRoot.equals(details.getAuthor()); - } - - @NotNull - @Override - public String getUserName(@NotNull VirtualFile root) { - return myMeData.get(root).getName(); - } - } - - /** - * Returns the user name selected in the filter for the given root. - * If it is a name-as-text filter, of course, values don't differ per root. The difference appears if the special "me" filter is used. - */ - @NotNull - public abstract String getUserName(@NotNull VirtualFile root); - -} diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogUI.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogUI.java index e913daf84e8a..95d1631c5c1c 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogUI.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogUI.java @@ -3,6 +3,9 @@ package com.intellij.vcs.log.ui; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.MessageType; +import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier; +import com.intellij.util.PairFunction; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.UIUtil; import com.intellij.vcs.log.Hash; @@ -234,33 +237,68 @@ public class VcsLogUI { updateUI(); } - public void jumpToCommit(final Hash commitHash) { - int row = myLogDataHolder.getDataPack().getRowByHash(commitHash); - if (row != -1) { + public void jumpToCommit(@NotNull Hash commitHash) { + jumpTo(commitHash, new PairFunction<AbstractVcsLogTableModel, Hash, Integer>() { + @Override + public Integer fun(AbstractVcsLogTableModel model, Hash hash) { + return model.getRowOfCommit(hash); + } + }); + } + + public void jumpToCommitByPartOfHash(@NotNull String commitHash) { + jumpTo(commitHash, new PairFunction<AbstractVcsLogTableModel, String, Integer>() { + @Override + public Integer fun(AbstractVcsLogTableModel model, String hash) { + return model.getRowOfCommitByPartOfHash(hash); + } + }); + } + + private <T> void jumpTo(@NotNull final T commitId, @NotNull final PairFunction<AbstractVcsLogTableModel, T, Integer> rowGetter) { + AbstractVcsLogTableModel model = getModel(); + if (model == null) { + return; + } + + int row = rowGetter.fun(model, commitId); + if (row >= 0) { jumpToRow(row); } - else { - myLogDataHolder.showFullLog(new Runnable() { + else if (model.canRequestMore()) { + model.requestToLoadMore(new Runnable() { @Override public void run() { - jumpToCommit(commitHash); + jumpTo(commitId, rowGetter); } }); } + else { + commitNotFound(commitId.toString()); + } } - public void jumpToCommitByPartOfHash(final String hash) { - Node node = myLogDataHolder.getDataPack().getNodeByPartOfHash(hash); - if (node != null) { - jumpToRow(node.getRowIndex()); + @Nullable + private AbstractVcsLogTableModel getModel() { + TableModel model = getTable().getModel(); + if (model instanceof AbstractVcsLogTableModel) { + return (AbstractVcsLogTableModel)model; } - else if (!myLogDataHolder.isFullLogShowing()) { - myLogDataHolder.showFullLog(new Runnable() { - @Override - public void run() { - jumpToCommitByPartOfHash(hash); - } - }); + showMessage(MessageType.WARNING, "The log is not ready to search yet"); + return null; + } + + private void showMessage(@NotNull MessageType messageType, @NotNull String message) { + LOG.info(message); + VcsBalloonProblemNotifier.showOverChangesView(myProject, message, messageType); + } + + private void commitNotFound(@NotNull String commitHash) { + if (collectFilters().isEmpty()) { + showMessage(MessageType.WARNING, "Commit " + commitHash + " not found"); + } + else { + showMessage(MessageType.WARNING, "Commit " + commitHash + " doesn't exist or doesn't match the active filters"); } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/BranchFilterPopupComponent.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/BranchFilterPopupComponent.java index 4318e92e2433..aa7f1f9384e3 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/BranchFilterPopupComponent.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/BranchFilterPopupComponent.java @@ -20,7 +20,7 @@ import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.containers.ContainerUtil; import com.intellij.vcs.log.*; -import com.intellij.vcs.log.data.VcsLogBranchFilter; +import com.intellij.vcs.log.data.VcsLogBranchFilterImpl; import com.intellij.vcs.log.impl.VcsLogUtil; import com.intellij.vcs.log.ui.VcsLogUI; import org.jetbrains.annotations.NotNull; @@ -127,7 +127,7 @@ class BranchFilterPopupComponent extends FilterPopupComponent { protected Collection<VcsLogFilter> getFilters() { String value = getValue(); Collection<VcsRef> allBranches = myUi.getLogDataHolder().getDataPack().getRefsModel().getBranches(); - return value == ALL ? null : Collections.<VcsLogFilter>singleton(new VcsLogBranchFilter(allBranches, value)); + return value == ALL ? null : Collections.<VcsLogFilter>singleton(new VcsLogBranchFilterImpl(allBranches, value)); } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/DateFilterPopupComponent.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/DateFilterPopupComponent.java index 4c98df60957a..abac979df5df 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/DateFilterPopupComponent.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/DateFilterPopupComponent.java @@ -24,7 +24,7 @@ import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.vcs.versionBrowser.DateFilterComponent; import com.intellij.util.text.DateFormatUtil; import com.intellij.vcs.log.VcsLogFilter; -import com.intellij.vcs.log.data.VcsLogDateFilter; +import com.intellij.vcs.log.data.VcsLogDateFilterImpl; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -69,7 +69,7 @@ class DateFilterPopupComponent extends FilterPopupComponent { @Nullable @Override protected Collection<VcsLogFilter> getFilters() { - return myAfter == null && myBefore == null ? null : Collections.<VcsLogFilter>singleton(new VcsLogDateFilter(myAfter, myBefore)); + return myAfter == null && myBefore == null ? null : Collections.<VcsLogFilter>singleton(new VcsLogDateFilterImpl(myAfter, myBefore)); } private void setOnlyAfter(Date after) { diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/StructureFilterPopupComponent.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/StructureFilterPopupComponent.java index ed0f8cfd511d..ab5d4dd0e575 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/StructureFilterPopupComponent.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/StructureFilterPopupComponent.java @@ -24,7 +24,7 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.containers.ContainerUtil; import com.intellij.vcs.log.VcsLogFilter; -import com.intellij.vcs.log.data.VcsLogStructureFilter; +import com.intellij.vcs.log.data.VcsLogStructureFilterImpl; import com.intellij.vcs.log.ui.VcsStructureChooser; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -53,7 +53,7 @@ class StructureFilterPopupComponent extends FilterPopupComponent { @Nullable @Override protected Collection<VcsLogFilter> getFilters() { - return getValue() == ALL || myFiles.isEmpty() ? null : Collections.<VcsLogFilter>singleton(new VcsLogStructureFilter(myFiles, myRoots)); + return getValue() == ALL || myFiles.isEmpty() ? null : Collections.<VcsLogFilter>singleton(new VcsLogStructureFilterImpl(myFiles, myRoots)); } private void setValue(@NotNull Collection<VirtualFile> files) { diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/UserFilterPopupComponent.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/UserFilterPopupComponent.java index 69488bb5ad01..102d32e9fff5 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/UserFilterPopupComponent.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/UserFilterPopupComponent.java @@ -25,6 +25,7 @@ import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.*; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.spellchecker.ui.SpellCheckingEditorCustomization; import com.intellij.ui.EditorCustomization; import com.intellij.ui.EditorTextField; @@ -33,11 +34,13 @@ import com.intellij.ui.SoftWrapsEditorCustomization; import com.intellij.util.Function; import com.intellij.util.TextFieldCompletionProviderDumbAware; import com.intellij.util.containers.ContainerUtil; +import com.intellij.vcs.log.VcsFullCommitDetails; import com.intellij.vcs.log.VcsLogFilter; import com.intellij.vcs.log.VcsUser; import com.intellij.vcs.log.data.VcsLogDataHolder; +import com.intellij.vcs.log.data.VcsLogDetailsFilter; import com.intellij.vcs.log.data.VcsLogUiProperties; -import com.intellij.vcs.log.data.VcsLogUserFilter; +import com.intellij.vcs.log.VcsLogUserFilter; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -101,7 +104,7 @@ class UserFilterPopupComponent extends FilterPopupComponent { return ContainerUtil.map(mySelectedUsers, new Function<String, VcsLogFilter>() { @Override public VcsLogFilter fun(String name) { - return name == ME ? new VcsLogUserFilter.Me(myDataHolder.getCurrentUser()) : new VcsLogUserFilter.ByName(name); + return name == ME ? new Me(myDataHolder.getCurrentUser()) : new ByName(name); } }); } @@ -265,4 +268,54 @@ class UserFilterPopupComponent extends FilterPopupComponent { } } } + + /** + * Filters by the given name or part of it. + */ + public static class ByName implements VcsLogUserFilter, VcsLogDetailsFilter { + + @NotNull private final String myUser; + + public ByName(@NotNull String user) { + myUser = user; + } + + @Override + public boolean matches(@NotNull VcsFullCommitDetails detail) { + return detail.getAuthor().getName().toLowerCase().contains(myUser.toLowerCase()) || + detail.getAuthor().getEmail().toLowerCase().contains(myUser.toLowerCase()); + } + + @NotNull + @Override + public String getUserName(@NotNull VirtualFile root) { + return myUser; + } + } + + /** + * Looks for commits matching the current user, + * i.e. looks for the value stored in the VCS config and compares the configured name with the one returned in commit details. + */ + public static class Me implements VcsLogUserFilter, VcsLogDetailsFilter { + + @NotNull private final Map<VirtualFile, VcsUser> myMeData; + + public Me(@NotNull Map<VirtualFile, VcsUser> meData) { + myMeData = meData; + } + + @Override + public boolean matches(@NotNull VcsFullCommitDetails details) { + VcsUser meInThisRoot = myMeData.get(details.getRoot()); + return meInThisRoot != null && meInThisRoot.equals(details.getAuthor()); + } + + @NotNull + @Override + public String getUserName(@NotNull VirtualFile root) { + return myMeData.get(root).getName(); + } + } + } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/VcsLogClassicFilterUi.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/VcsLogClassicFilterUi.java index d8bd69633b06..3cdf018f1a5c 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/VcsLogClassicFilterUi.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/VcsLogClassicFilterUi.java @@ -86,7 +86,7 @@ public class VcsLogClassicFilterUi implements VcsLogFilterUi { public Collection<VcsLogFilter> getFilters() { List<VcsLogFilter> filters = getPopupFilters(); if (!myTextFilter.getText().isEmpty()) { - filters.add(new VcsLogTextFilter(myTextFilter.getText())); + filters.add(new VcsLogTextFilterImpl(myTextFilter.getText())); } return filters; } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/VcsLogTextFilter.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/VcsLogTextFilterImpl.java index 7c4c767161ba..64bb6eaf20b7 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/VcsLogTextFilter.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/VcsLogTextFilterImpl.java @@ -16,14 +16,15 @@ package com.intellij.vcs.log.ui.filter; import com.intellij.vcs.log.VcsFullCommitDetails; +import com.intellij.vcs.log.VcsLogTextFilter; import com.intellij.vcs.log.data.VcsLogDetailsFilter; import org.jetbrains.annotations.NotNull; -public class VcsLogTextFilter implements VcsLogDetailsFilter { +public class VcsLogTextFilterImpl implements VcsLogDetailsFilter, VcsLogTextFilter { @NotNull private final String myText; - public VcsLogTextFilter(@NotNull String text) { + public VcsLogTextFilterImpl(@NotNull String text) { myText = text; } @@ -32,6 +33,7 @@ public class VcsLogTextFilter implements VcsLogDetailsFilter { return details.getFullMessage().toLowerCase().contains(myText.toLowerCase()); } + @Override @NotNull public String getText() { return myText; diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/AbstractVcsLogTableModel.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/AbstractVcsLogTableModel.java index 7ff8c110baf3..aba0808a0117 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/AbstractVcsLogTableModel.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/AbstractVcsLogTableModel.java @@ -1,5 +1,6 @@ package com.intellij.vcs.log.ui.tables; +import com.intellij.openapi.util.EmptyRunnable; import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.newvfs.impl.NullVirtualFile; @@ -46,7 +47,7 @@ public abstract class AbstractVcsLogTableModel<CommitColumnClass, CommitId> exte @Override public final Object getValueAt(int rowIndex, int columnIndex) { if (rowIndex >= getRowCount() - 1) { - requestToLoadMore(); + requestToLoadMore(EmptyRunnable.INSTANCE); } VcsShortCommitDetails data = getShortDetails(rowIndex); @@ -74,7 +75,16 @@ public abstract class AbstractVcsLogTableModel<CommitColumnClass, CommitId> exte } } - public abstract void requestToLoadMore(); + /** + * Requests the proper data provider to load more data from the log & recreate the model. + * @param onLoaded will be called upon task completion on the EDT. + */ + public abstract void requestToLoadMore(@NotNull Runnable onLoaded); + + /** + * Returns true if not all data has been loaded, i.e. there is sense to {@link #requestToLoadMore(Runnable) request more data}. + */ + public abstract boolean canRequestMore(); /** * Returns Changes for commits at selected rows.<br/> @@ -102,6 +112,18 @@ public abstract class AbstractVcsLogTableModel<CommitColumnClass, CommitId> exte @Nullable public abstract Hash getHashAtRow(int row); + /** + * Returns the row number containing the given commit, + * or -1 if the requested commit is not contained in this table model (possibly because not all data has been loaded). + */ + public abstract int getRowOfCommit(@NotNull Hash hash); + + /** + * Returns the number of the first row which contains a commit which hash starts with the given value, + * or -1 if no such commit was found (possibly because not all data has been loaded). + */ + public abstract int getRowOfCommitByPartOfHash(@NotNull String hash); + @Override public Class<?> getColumnClass(int column) { switch (column) { diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/GraphTableModel.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/GraphTableModel.java index 21e6cd4ecac7..2b48965e2686 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/GraphTableModel.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/GraphTableModel.java @@ -1,7 +1,6 @@ package com.intellij.vcs.log.ui.tables; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.util.EmptyRunnable; import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.vcs.log.Hash; @@ -57,8 +56,13 @@ public class GraphTableModel extends AbstractVcsLogTableModel<GraphCommitCell, N } @Override - public void requestToLoadMore() { - myDataHolder.showFullLog(EmptyRunnable.INSTANCE); + public void requestToLoadMore(@NotNull Runnable onLoaded) { + myDataHolder.showFullLog(onLoaded); + } + + @Override + public boolean canRequestMore() { + return !myDataHolder.isFullLogShowing(); } @Nullable @@ -120,4 +124,15 @@ public class GraphTableModel extends AbstractVcsLogTableModel<GraphCommitCell, N return node == null ? null : myDataHolder.getHash(node.getCommitIndex()); } + @Override + public int getRowOfCommit(@NotNull final Hash hash) { + return myDataPack.getRowByHash(hash); + } + + @Override + public int getRowOfCommitByPartOfHash(@NotNull String hash) { + Node node = myDataPack.getNodeByPartOfHash(hash); + return node != null ? node.getRowIndex() : -1; + } + } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/NoGraphTableModel.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/NoGraphTableModel.java index 1cdd138a6fc7..1cf9474b4a58 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/NoGraphTableModel.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/tables/NoGraphTableModel.java @@ -61,14 +61,19 @@ public class NoGraphTableModel extends AbstractVcsLogTableModel<CommitCell, Hash } @Override - public void requestToLoadMore() { + public void requestToLoadMore(@NotNull Runnable onLoaded) { if (myLoadMoreWasRequested.compareAndSet(false, true) // Don't send the request to VCS twice && myLoadMoreStage != LoadMoreStage.ALL_REQUESTED) { // or when everything possible is loaded myUi.getTable().setPaintBusy(true); - myUi.getFilterer().requestVcs(myUi.collectFilters(), myLoadMoreStage); + myUi.getFilterer().requestVcs(myUi.collectFilters(), myLoadMoreStage, onLoaded); } } + @Override + public boolean canRequestMore() { + return myLoadMoreStage != LoadMoreStage.ALL_REQUESTED; + } + @Nullable @Override public List<Change> getSelectedChanges(@NotNull List<Integer> selectedRows) { @@ -115,4 +120,28 @@ public class NoGraphTableModel extends AbstractVcsLogTableModel<CommitCell, Hash public Hash getHashAtRow(int row) { return myCommits.get(row).getHash(); } + + @Override + public int getRowOfCommit(@NotNull Hash hash) { + for (int i = 0; i < myCommits.size(); i++) { + VcsFullCommitDetails commit = myCommits.get(i); + if (commit.getHash().equals(hash)) { + return i; + } + } + return -1; + } + + @Override + public int getRowOfCommitByPartOfHash(@NotNull String hash) { + String lowercaseHash = hash.toLowerCase(); + for (int i = 0; i < myCommits.size(); i++) { + VcsFullCommitDetails commit = myCommits.get(i); + if (commit.getHash().toString().toLowerCase().startsWith(lowercaseHash)) { + return i; + } + } + return -1; + } + } diff --git a/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/inject/config/ui/MethodParameterPanel.form b/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/inject/config/ui/MethodParameterPanel.form index b77dd37c29d0..62fdc0efa396 100644 --- a/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/inject/config/ui/MethodParameterPanel.form +++ b/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/inject/config/ui/MethodParameterPanel.form @@ -1,9 +1,9 @@ <?xml version="1.0" encoding="UTF-8"?> <form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.intellij.plugins.intelliLang.inject.config.ui.MethodParameterPanel"> - <grid id="27dc6" binding="myRoot" layout-manager="GridLayoutManager" row-count="4" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> + <grid id="27dc6" binding="myRoot" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> <margin top="0" left="0" bottom="0" right="0"/> <constraints> - <xy x="20" y="20" width="500" height="400"/> + <xy x="20" y="20" width="500" height="697"/> </constraints> <properties/> <border type="none"/> @@ -13,16 +13,16 @@ <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="7" anchor="0" fill="1" indent="0" use-parent-layout="false"/> </constraints> </nested-form> - <grid id="84778" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> + <grid id="84778" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="0" vgap="0"> <margin top="0" left="0" bottom="0" right="0"/> <constraints> <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/> </constraints> <properties/> <clientProperties> - <BorderFactoryClass class="java.lang.String" value="com.intellij.ui.IdeBorderFactory$PlainSmallWithIndent"/> + <BorderFactoryClass class="java.lang.String" value="com.intellij.ui.IdeBorderFactory$PlainSmallWithoutIndent"/> </clientProperties> - <border type="none" title="Class-Name"/> + <border type="none" title="Class Methods"/> <children> <grid id="5081f" binding="myClassPanel" layout-manager="BorderLayout" hgap="0" vgap="0"> <constraints> @@ -32,22 +32,9 @@ <border type="none"/> <children/> </grid> - </children> - </grid> - <grid id="b75a2" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> - <margin top="0" left="0" bottom="0" right="0"/> - <constraints> - <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/> - </constraints> - <properties/> - <clientProperties> - <BorderFactoryClass class="java.lang.String" value="com.intellij.ui.IdeBorderFactory$PlainSmallWithoutIndent"/> - </clientProperties> - <border type="none" title="Parameters"/> - <children> <scrollpane id="5dfaa" class="com.intellij.ui.components.JBScrollPane"> <constraints> - <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="7" hsize-policy="7" anchor="0" fill="3" indent="0" use-parent-layout="false"/> + <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="7" hsize-policy="7" anchor="0" fill="3" indent="0" use-parent-layout="false"/> </constraints> <properties/> <border type="none"/> @@ -62,7 +49,7 @@ </grid> <nested-form id="b6e0b" form-file="org/intellij/plugins/intelliLang/inject/config/ui/AdvancedPanel.form" binding="myAdvancedPanel" custom-create="true"> <constraints> - <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="7" anchor="0" fill="1" indent="0" use-parent-layout="false"/> + <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="7" anchor="0" fill="1" indent="0" use-parent-layout="false"/> </constraints> </nested-form> </children> diff --git a/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/inject/config/ui/MethodParameterPanel.java b/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/inject/config/ui/MethodParameterPanel.java index 36da2a930603..0c1cf6408c26 100644 --- a/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/inject/config/ui/MethodParameterPanel.java +++ b/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/inject/config/ui/MethodParameterPanel.java @@ -28,6 +28,7 @@ import com.intellij.openapi.util.Condition; import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.PsiFormatUtil; +import com.intellij.psi.util.PsiFormatUtilBase; import com.intellij.ui.*; import com.intellij.ui.dualView.TreeTableView; import com.intellij.ui.treeStructure.treetable.ListTreeTableModelOnColumns; @@ -42,6 +43,7 @@ import gnu.trove.THashMap; import gnu.trove.THashSet; import org.intellij.plugins.intelliLang.inject.config.MethodParameterInjection; import org.intellij.plugins.intelliLang.util.PsiUtilEx; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; @@ -91,23 +93,23 @@ public class MethodParameterPanel extends AbstractInjectionPanel<MethodParameter myParamsTable.getTree().setShowsRootHandles(true); myParamsTable.getTree().setCellRenderer(new ColoredTreeCellRenderer() { - public void customizeCellRenderer(final JTree tree, - final Object value, - final boolean selected, - final boolean expanded, - final boolean leaf, - final int row, - final boolean hasFocus) { + public void customizeCellRenderer(@NotNull JTree tree, + Object value, + boolean selected, + boolean expanded, + boolean leaf, + int row, + boolean hasFocus) { final Object o = ((DefaultMutableTreeNode)value).getUserObject(); setIcon(o instanceof PsiMethod ? PlatformIcons.METHOD_ICON : o instanceof PsiParameter ? PlatformIcons.PARAMETER_ICON : null); final String name; if (o instanceof PsiMethod) { - name = PsiFormatUtil.formatMethod((PsiMethod)o, PsiSubstitutor.EMPTY, PsiFormatUtil.SHOW_NAME | PsiFormatUtil.SHOW_PARAMETERS, - PsiFormatUtil.SHOW_NAME | PsiFormatUtil.SHOW_TYPE); + name = PsiFormatUtil.formatMethod((PsiMethod)o, PsiSubstitutor.EMPTY, PsiFormatUtilBase.SHOW_NAME | PsiFormatUtilBase.SHOW_PARAMETERS, + PsiFormatUtilBase.SHOW_NAME | PsiFormatUtilBase.SHOW_TYPE); } else if (o instanceof PsiParameter) { - name = PsiFormatUtil.formatVariable((PsiParameter)o, PsiFormatUtil.SHOW_NAME | PsiFormatUtil.SHOW_TYPE, PsiSubstitutor.EMPTY); + name = PsiFormatUtil.formatVariable((PsiParameter)o, PsiFormatUtilBase.SHOW_NAME | PsiFormatUtilBase.SHOW_TYPE, PsiSubstitutor.EMPTY); } else name = null; final boolean missing = o instanceof PsiElement && !((PsiElement)o).isPhysical(); @@ -362,7 +364,7 @@ public class MethodParameterPanel extends AbstractInjectionPanel<MethodParameter return valueOf(o) != null; } - }, new TreeColumnInfo("Method/Parameters") + }, new TreeColumnInfo(" ") }; } @@ -386,7 +388,7 @@ public class MethodParameterPanel extends AbstractInjectionPanel<MethodParameter } } - private class MyView extends TreeTableView implements TypeSafeDataProvider { + private static class MyView extends TreeTableView implements TypeSafeDataProvider { public MyView(ListTreeTableModelOnColumns treeTableModel) { super(treeTableModel); } diff --git a/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/inject/config/ui/configurables/MethodParameterInjectionConfigurable.java b/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/inject/config/ui/configurables/MethodParameterInjectionConfigurable.java deleted file mode 100644 index 1b14fc204eca..000000000000 --- a/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/inject/config/ui/configurables/MethodParameterInjectionConfigurable.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2006 Sascha Weinreuter - * - * 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 org.intellij.plugins.intelliLang.inject.config.ui.configurables; - -import com.intellij.openapi.project.Project; -import org.intellij.plugins.intelliLang.inject.config.MethodParameterInjection; -import org.intellij.plugins.intelliLang.inject.config.ui.MethodParameterPanel; - -public class MethodParameterInjectionConfigurable extends InjectionConfigurable<MethodParameterInjection, MethodParameterPanel> { - public MethodParameterInjectionConfigurable(MethodParameterInjection injection, Runnable treeUpdater, Project project) { - super(injection, treeUpdater, project); - } - - protected MethodParameterPanel createOptionsPanelImpl() { - return new MethodParameterPanel(myInjection, myProject); - } - - public String getBannerSlogan() { - return "Edit Method Parameter Injection"; - } -} diff --git a/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/inject/java/JavaLanguageInjectionSupport.java b/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/inject/java/JavaLanguageInjectionSupport.java index 99a3ccd21eab..14c7535f8044 100644 --- a/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/inject/java/JavaLanguageInjectionSupport.java +++ b/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/inject/java/JavaLanguageInjectionSupport.java @@ -55,7 +55,6 @@ import org.intellij.plugins.intelliLang.inject.config.InjectionPlace; import org.intellij.plugins.intelliLang.inject.config.MethodParameterInjection; import org.intellij.plugins.intelliLang.inject.config.ui.AbstractInjectionPanel; import org.intellij.plugins.intelliLang.inject.config.ui.MethodParameterPanel; -import org.intellij.plugins.intelliLang.inject.config.ui.configurables.MethodParameterInjectionConfigurable; import org.intellij.plugins.intelliLang.util.ContextComputationProcessor; import org.intellij.plugins.intelliLang.util.PsiUtilEx; import org.jdom.Element; @@ -175,10 +174,7 @@ public class JavaLanguageInjectionSupport extends AbstractLanguageInjectionSuppo } public BaseInjection createInjection(final Element element) { - if (element.getName().equals(MethodParameterInjection.class.getSimpleName())) { - return new MethodParameterInjection(); - } - else return new BaseInjection(JAVA_SUPPORT_ID); + return new BaseInjection(JAVA_SUPPORT_ID); } private static boolean doInjectInJava(final Project project, @@ -371,16 +367,21 @@ public class JavaLanguageInjectionSupport extends AbstractLanguageInjectionSuppo originalCopy.setPlaceEnabled(currentPlace.getText(), true); methodParameterInjection = createFrom(project, originalCopy, contextMethod, false); } - if (InjectLanguageAction.doEditConfigurable(project, new MethodParameterInjectionConfigurable(methodParameterInjection, null, project))) { - final BaseInjection newInjection = new BaseInjection(methodParameterInjection.getSupportId()).copyFrom(methodParameterInjection); - if (originalInjection != null) { - newInjection.mergeOriginalPlacesFrom(originalInjection, true); - } - configuration.replaceInjectionsWithUndo( - project, Collections.singletonList(newInjection), - ContainerUtil.createMaybeSingletonList(originalInjection), - Collections.<PsiElement>emptyList()); + mergePlacesAndAddToConfiguration(project, configuration, methodParameterInjection, originalInjection); + } + + private static void mergePlacesAndAddToConfiguration(@NotNull Project project, + @NotNull Configuration configuration, + @NotNull MethodParameterInjection injection, + @Nullable BaseInjection originalInjection) { + BaseInjection newInjection = new BaseInjection(injection.getSupportId()).copyFrom(injection); + if (originalInjection != null) { + newInjection.mergeOriginalPlacesFrom(originalInjection, true); } + configuration.replaceInjectionsWithUndo( + project, Collections.singletonList(newInjection), + ContainerUtil.createMaybeSingletonList(originalInjection), + Collections.<PsiElement>emptyList()); } private static void collectInjections(PsiLiteralExpression host, diff --git a/plugins/IntelliLang/src/META-INF/plugin.xml b/plugins/IntelliLang/src/META-INF/plugin.xml index 8dad5cb4e41a..975422e0fdfc 100644 --- a/plugins/IntelliLang/src/META-INF/plugin.xml +++ b/plugins/IntelliLang/src/META-INF/plugin.xml @@ -36,7 +36,7 @@ <highlightErrorFilter implementation="org.intellij.plugins.intelliLang.inject.FrankensteinErrorFilter"/> <daemon.highlightInfoFilter implementation="org.intellij.plugins.intelliLang.inject.FrankensteinErrorFilter"/> - <projectConfigurable instance="org.intellij.plugins.intelliLang.InjectionsSettingsUI" nonDefaultProject="true"/> + <projectConfigurable displayName="Language Injections" instance="org.intellij.plugins.intelliLang.InjectionsSettingsUI" nonDefaultProject="true"/> <multiHostInjector implementation="org.intellij.plugins.intelliLang.inject.CommentLanguageInjector"/> <multiHostInjector implementation="org.intellij.plugins.intelliLang.inject.TemporaryPlacesInjector"/> diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/InjectionsSettingsUI.java b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/InjectionsSettingsUI.java index 414500ba2096..a0420939dfdc 100644 --- a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/InjectionsSettingsUI.java +++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/InjectionsSettingsUI.java @@ -30,6 +30,7 @@ import com.intellij.openapi.fileTypes.FileTypes; import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.options.Configurable; import com.intellij.openapi.options.SearchableConfigurable; +import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.ui.SplitterProportionsData; @@ -47,7 +48,6 @@ import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.Convertor; import com.intellij.util.ui.ColumnInfo; import com.intellij.util.ui.ListTableModel; -import gnu.trove.THashMap; import gnu.trove.THashSet; import gnu.trove.TObjectHashingStrategy; import org.intellij.plugins.intelliLang.inject.AbstractLanguageInjectionSupport; @@ -81,19 +81,14 @@ public class InjectionsSettingsUI implements SearchableConfigurable.Parent, Conf private final JPanel myRoot; private final InjectionsTable myInjectionsTable; - private final Map<String, LanguageInjectionSupport> mySupports = new THashMap<String, LanguageInjectionSupport>(); - private final Map<String, AnAction> myEditActions = new THashMap<String, AnAction>(); - private final List<AnAction> myAddActions = new ArrayList<AnAction>(); - private final ActionToolbar myToolbar; + private final Map<String, LanguageInjectionSupport> mySupports = ContainerUtil.newLinkedHashMap(); + private final Map<String, AnAction> myEditActions = ContainerUtil.newLinkedHashMap(); + private final List<AnAction> myAddActions = ContainerUtil.newArrayList(); private final JLabel myCountLabel; private Configurable[] myConfigurables; private Configuration myConfiguration; - public InjectionsSettingsUI(final Project project) { - this(project, Configuration.getProjectInstance(project)); - } - public InjectionsSettingsUI(final Project project, final Configuration configuration) { myProject = project; myConfiguration = configuration; @@ -107,16 +102,12 @@ public class InjectionsSettingsUI implements SearchableConfigurable.Parent, Conf myInjectionsTable = new InjectionsTable(getInjInfoList(myInfos)); myInjectionsTable.getEmptyText().setText("No injections configured"); - final JPanel tablePanel = new JPanel(new BorderLayout()); - - tablePanel.add(ScrollPaneFactory.createScrollPane(myInjectionsTable), BorderLayout.CENTER); - final DefaultActionGroup group = createActions(); + ToolbarDecorator decorator = ToolbarDecorator.createDecorator(myInjectionsTable); + createActions(decorator); - myToolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, group, true); - myToolbar.setTargetComponent(myInjectionsTable); - myRoot.add(myToolbar.getComponent(), BorderLayout.NORTH); - myRoot.add(tablePanel, BorderLayout.CENTER); + //myRoot.add(new TitledSeparator("Languages injection places"), BorderLayout.NORTH); + myRoot.add(decorator.createPanel(), BorderLayout.CENTER); myCountLabel = new JLabel(); myCountLabel.setHorizontalAlignment(SwingConstants.RIGHT); myCountLabel.setForeground(SimpleTextAttributes.GRAY_ITALIC_ATTRIBUTES.getFgColor()); @@ -124,7 +115,7 @@ public class InjectionsSettingsUI implements SearchableConfigurable.Parent, Conf updateCountLabel(); } - private DefaultActionGroup createActions() { + private void createActions(ToolbarDecorator decorator) { final Consumer<BaseInjection> consumer = new Consumer<BaseInjection>() { public void consume(final BaseInjection injection) { addInjection(injection); @@ -148,22 +139,22 @@ public class InjectionsSettingsUI implements SearchableConfigurable.Parent, Conf return Comparing.compare(o1.getTemplatePresentation().getText(), o2.getTemplatePresentation().getText()); } }); - - final DefaultActionGroup group = new DefaultActionGroup(); - final AnAction addAction = new AnAction("Add", "Add", IconUtil.getAddIcon()) { + decorator.disableUpDownActions(); + decorator.setAddActionUpdater(new AnActionButtonUpdater() { @Override - public void update(final AnActionEvent e) { - e.getPresentation().setEnabled(!myAddActions.isEmpty()); + public boolean isEnabled(AnActionEvent e) { + return !myAddActions.isEmpty(); } - + }); + decorator.setAddAction(new AnActionButtonRunnable() { @Override - public void actionPerformed(final AnActionEvent e) { - performAdd(e); + public void run(AnActionButton button) { + performAdd(button); } - }; - final AnAction removeAction = new AnAction("Remove", "Remove", PlatformIcons.DELETE_ICON) { + }); + decorator.setRemoveActionUpdater(new AnActionButtonUpdater() { @Override - public void update(final AnActionEvent e) { + public boolean isEnabled(AnActionEvent e) { boolean enabled = false; for (InjInfo info : getSelectedInjections()) { if (!info.bundled) { @@ -171,69 +162,63 @@ public class InjectionsSettingsUI implements SearchableConfigurable.Parent, Conf break; } } - e.getPresentation().setEnabled(enabled); + return enabled; } - + }); + decorator.setRemoveAction(new AnActionButtonRunnable() { @Override - public void actionPerformed(final AnActionEvent e) { + public void run(AnActionButton button) { performRemove(); } - }; + }); - final AnAction editAction = new AnAction("Edit", "Edit", PlatformIcons.PROPERTIES_ICON) { + decorator.setEditActionUpdater(new AnActionButtonUpdater() { @Override - public void update(final AnActionEvent e) { - final AnAction action = getEditAction(); - e.getPresentation().setEnabled(action != null); - if (action != null) action.update(e); + public boolean isEnabled(AnActionEvent e) { + AnAction edit = getEditAction(); + if (edit != null) edit.update(e); + return edit != null && edit.getTemplatePresentation().isEnabled(); } - + }); + decorator.setEditAction(new AnActionButtonRunnable() { @Override - public void actionPerformed(final AnActionEvent e) { - performEditAction(e); + public void run(AnActionButton button) { + performEditAction(); } - }; - final AnAction copyAction = new AnAction("Duplicate", "Duplicate", PlatformIcons.COPY_ICON) { + }); + decorator.addExtraAction(new DumbAwareActionButton("Duplicate", "Duplicate", PlatformIcons.COPY_ICON) { + @Override - public void update(final AnActionEvent e) { - final AnAction action = getEditAction(); - e.getPresentation().setEnabled(action != null); - if (action != null) action.update(e); + public boolean isEnabled() { + return getEditAction() != null; } @Override - public void actionPerformed(final AnActionEvent e) { + public void actionPerformed(AnActionEvent e) { final InjInfo injection = getSelectedInjection(); if (injection != null) { addInjection(injection.injection.copy()); //performEditAction(e); } } - }; - group.add(addAction); - group.add(removeAction); - group.add(copyAction); - group.add(editAction); + }); - addAction.registerCustomShortcutSet(CommonShortcuts.INSERT, myInjectionsTable); - removeAction.registerCustomShortcutSet(CommonShortcuts.DELETE, myInjectionsTable); - editAction.registerCustomShortcutSet(CommonShortcuts.ENTER, myInjectionsTable); + decorator.addExtraAction(new DumbAwareActionButton("Enable Selected Injections", "Enable Selected Injections", PlatformIcons.SELECT_ALL_ICON) { - group.addSeparator(); - group.add(new AnAction("Enable Selected Injections", "Enable Selected Injections", PlatformIcons.SELECT_ALL_ICON) { @Override public void actionPerformed(final AnActionEvent e) { performSelectedInjectionsEnabled(true); } }); - group.add(new AnAction("Disable Selected Injections", "Disable Selected Injections", PlatformIcons.UNSELECT_ALL_ICON) { - @Override - public void actionPerformed(final AnActionEvent e) { - performSelectedInjectionsEnabled(false); - } - }); + decorator.addExtraAction(new DumbAwareActionButton("Disable Selected Injections", "Disable Selected Injections", PlatformIcons.UNSELECT_ALL_ICON) { - new AnAction("Toggle") { + @Override + public void actionPerformed(final AnActionEvent e) { + performSelectedInjectionsEnabled(false); + } + }); + + new DumbAwareAction("Toggle") { @Override public void update(AnActionEvent e) { SpeedSearchSupply supply = SpeedSearchSupply.getSupply(myInjectionsTable); @@ -247,8 +232,18 @@ public class InjectionsSettingsUI implements SearchableConfigurable.Parent, Conf }.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0)), myInjectionsTable); if (myInfos.length > 1) { - group.addSeparator(); - final AnAction shareAction = new AnAction("Make Global", null, PlatformIcons.IMPORT_ICON) { + AnActionButton shareAction = new DumbAwareActionButton("Make Global", null, PlatformIcons.IMPORT_ICON) { + { + addCustomUpdater(new AnActionButtonUpdater() { + @Override + public boolean isEnabled(AnActionEvent e) { + CfgInfo cfg = getTargetCfgInfo(getSelectedInjections()); + e.getPresentation().setText(cfg == getDefaultCfgInfo() ? "Make Global" : "Move to Project"); + return cfg != null; + } + }); + } + @Override public void actionPerformed(final AnActionEvent e) { final List<InjInfo> injections = getSelectedInjections(); @@ -265,14 +260,6 @@ public class InjectionsSettingsUI implements SearchableConfigurable.Parent, Conf TableUtil.selectRows(myInjectionsTable, selectedRows); } - @Override - public void update(final AnActionEvent e) { - final CfgInfo cfg = getTargetCfgInfo(getSelectedInjections()); - e.getPresentation().setEnabled(cfg != null); - e.getPresentation().setText(cfg == getDefaultCfgInfo() ? "Make Global" : "Move to Project"); - super.update(e); - } - @Nullable private CfgInfo getTargetCfgInfo(final List<InjInfo> injections) { CfgInfo cfg = null; @@ -283,25 +270,26 @@ public class InjectionsSettingsUI implements SearchableConfigurable.Parent, Conf if (cfg == null) cfg = info.cfgInfo; else if (cfg != info.cfgInfo) return info.cfgInfo; } - if (cfg == null) return cfg; + if (cfg == null) return null; for (CfgInfo info : myInfos) { if (info != cfg) return info; } throw new AssertionError(); } }; - shareAction.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, InputEvent.SHIFT_DOWN_MASK)), myInjectionsTable); - group.add(shareAction); + shareAction.setShortcut(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, InputEvent.SHIFT_DOWN_MASK))); + decorator.addExtraAction(shareAction); } - group.addSeparator(); - group.add(new AnAction("Import", "Import", AllIcons.Actions.Install) { + decorator.addExtraAction(new DumbAwareActionButton("Import", "Import", AllIcons.Actions.Install) { + @Override public void actionPerformed(final AnActionEvent e) { doImportAction(e.getDataContext()); updateCountLabel(); } }); - group.add(new AnAction("Export", "Export", AllIcons.Actions.Export) { + decorator.addExtraAction(new DumbAwareActionButton("Export", "Export", AllIcons.Actions.Export) { + @Override public void actionPerformed(final AnActionEvent e) { final List<BaseInjection> injections = getInjectionList(getSelectedInjections()); @@ -321,20 +309,19 @@ public class InjectionsSettingsUI implements SearchableConfigurable.Parent, Conf } @Override - public void update(final AnActionEvent e) { - e.getPresentation().setEnabled(!getSelectedInjections().isEmpty()); + public boolean isEnabled() { + return !getSelectedInjections().isEmpty(); } }); - - return group; } - private void performEditAction(AnActionEvent e) { + private void performEditAction() { final AnAction action = getEditAction(); if (action != null) { final int row = myInjectionsTable.getSelectedRow(); - action.actionPerformed(e); + action.actionPerformed(new AnActionEvent(null, DataManager.getInstance().getDataContext(myInjectionsTable), + ActionPlaces.UNKNOWN, new Presentation(""), ActionManager.getInstance(), 0)); myInjectionsTable.getListTableModel().fireTableDataChanged(); myInjectionsTable.getSelectionModel().setSelectionInterval(row, row); updateCountLabel(); @@ -352,10 +339,8 @@ public class InjectionsSettingsUI implements SearchableConfigurable.Parent, Conf if (place.isEnabled()) enablePlacesCount++; } } - final StringBuilder sb = new StringBuilder(); - sb.append(items.size()).append(" injection").append(items.size() > 1 ? "s" : "").append(" (").append(enablePlacesCount) - .append(" of ").append(placesCount).append(" place").append(placesCount > 1 ? "s" : "").append(" enabled) "); - myCountLabel.setText(sb.toString()); + myCountLabel.setText(items.size() + " injection" + (items.size() > 1 ? "s" : "") + " (" + enablePlacesCount + " of " + + placesCount + " place" + (placesCount > 1 ? "s" : "") + " enabled) "); } else { myCountLabel.setText("no injections configured "); @@ -511,17 +496,14 @@ public class InjectionsSettingsUI implements SearchableConfigurable.Parent, Conf return row < 0? null : myInjectionsTable.getItems().get(myInjectionsTable.convertRowIndexToModel(row)); } - private void performAdd(final AnActionEvent e) { - final DefaultActionGroup group = new DefaultActionGroup(); - for (AnAction action : myAddActions) { - group.add(action); - } + private void performAdd(AnActionButton e) { + DefaultActionGroup group = new DefaultActionGroup(myAddActions); JBPopupFactory.getInstance().createActionGroupPopup(null, group, e.getDataContext(), JBPopupFactory.ActionSelectionAid.NUMBERING, true, new Runnable() { public void run() { updateCountLabel(); } - }, -1).showUnderneathOf(myToolbar.getComponent()); + }, -1).show(e.getPreferredPopupPoint()); } @Nls @@ -552,8 +534,7 @@ public class InjectionsSettingsUI implements SearchableConfigurable.Parent, Conf if (row < 0) return false; if (columnAtPoint(e.getPoint()) <= 0) return false; myInjectionsTable.getSelectionModel().setSelectionInterval(row, row); - performEditAction(new AnActionEvent(e, DataManager.getInstance().getDataContext(InjectionsTable.this), - ActionPlaces.UNKNOWN, new Presentation(""), ActionManager.getInstance(), 0)); + performEditAction(); return true; } }.installOn(this); @@ -632,7 +613,7 @@ public class InjectionsSettingsUI implements SearchableConfigurable.Parent, Conf public TableCellRenderer getRenderer(final InjInfo injection) { return booleanCellRenderer; } - }, new ColumnInfo<InjInfo, InjInfo>("Display Name") { + }, new ColumnInfo<InjInfo, InjInfo>("Name") { @Override public InjInfo valueOf(final InjInfo info) { return info; diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/AbstractLanguageInjectionSupport.java b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/AbstractLanguageInjectionSupport.java index a18a26b529af..132393737525 100644 --- a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/AbstractLanguageInjectionSupport.java +++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/AbstractLanguageInjectionSupport.java @@ -114,12 +114,13 @@ public abstract class AbstractLanguageInjectionSupport extends LanguageInjection public static AnAction createDefaultAddAction(final Project project, final Consumer<BaseInjection> consumer, final AbstractLanguageInjectionSupport support) { + final String supportTitle = StringUtil.capitalize(support.getId()); Icon icon = FileTypeManager.getInstance().getFileTypeByExtension(support.getId()).getIcon(); - return new AnAction("Generic "+ StringUtil.capitalize(support.getId()), null, icon) { + return new AnAction("Generic "+ supportTitle, null, icon) { @Override public void actionPerformed(AnActionEvent e) { final BaseInjection injection = new BaseInjection(support.getId()); - injection.setDisplayName("New "+ StringUtil.capitalize(support.getId())+" Injection"); + injection.setDisplayName("New "+ supportTitle +" Injection"); final BaseInjection newInjection = showDefaultInjectionUI(project, injection); if (newInjection != null) { consumer.consume(injection); @@ -134,7 +135,7 @@ public abstract class AbstractLanguageInjectionSupport extends LanguageInjection panel.reset(); final DialogBuilder builder = new DialogBuilder(project); LanguageInjectionSupport support = InjectorUtils.findInjectionSupport(injection.getSupportId()); - if (support != null && support instanceof AbstractLanguageInjectionSupport) { + if (support instanceof AbstractLanguageInjectionSupport) { builder.setHelpId(((AbstractLanguageInjectionSupport)support).getHelpId()); } builder.addOkAction(); diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/InjectLanguageAction.java b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/InjectLanguageAction.java index 18f4c0147b12..b03a07de7233 100644 --- a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/InjectLanguageAction.java +++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/InjectLanguageAction.java @@ -28,7 +28,6 @@ import com.intellij.openapi.actionSystem.IdeActions; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.keymap.KeymapUtil; -import com.intellij.openapi.options.Configurable; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.ui.popup.PopupChooserBuilder; @@ -221,7 +220,4 @@ public class InjectLanguageAction implements IntentionAction { return false; } - public static boolean doEditConfigurable(final Project project, final Configurable configurable) { - return true; //ShowSettingsUtil.getInstance().editConfigurable(project, configurable); - } } diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/config/BaseInjection.java b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/config/BaseInjection.java index ffd54be5bd67..f5fddc277c04 100644 --- a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/config/BaseInjection.java +++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/config/BaseInjection.java @@ -56,7 +56,8 @@ public class BaseInjection implements Injection, PersistentStateComponent<Elemen public static final Key<BaseInjection> INJECTION_KEY = Key.create("INJECTION_KEY"); @NotNull private final String mySupportId; - private String myDisplayName; + + private String myDisplayName = ""; private String myInjectedLanguageId = ""; private String myPrefix = ""; @@ -98,7 +99,7 @@ public class BaseInjection implements Injection, PersistentStateComponent<Elemen return myDisplayName; } - public void setDisplayName(String displayName) { + public void setDisplayName(@NotNull String displayName) { myDisplayName = displayName; } diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/config/ui/AbstractInjectionPanel.java b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/config/ui/AbstractInjectionPanel.java index 2626d4f433ed..9b6fc83533fa 100644 --- a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/config/ui/AbstractInjectionPanel.java +++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/config/ui/AbstractInjectionPanel.java @@ -15,8 +15,6 @@ */ package org.intellij.plugins.intelliLang.inject.config.ui; -import com.intellij.openapi.editor.event.DocumentAdapter; -import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.project.Project; import com.intellij.util.ui.UIUtil; import org.intellij.plugins.intelliLang.inject.config.BaseInjection; @@ -73,7 +71,6 @@ public abstract class AbstractInjectionPanel<T extends BaseInjection> implements final InjectionPanel p = getField(panel); p.init(copy); } - reset(); } public final boolean isModified() { @@ -89,23 +86,28 @@ public abstract class AbstractInjectionPanel<T extends BaseInjection> implements @SuppressWarnings({"unchecked"}) public final void apply() { - apply(myOrigInjection); - for (Field panel : myOtherPanels) { getField(panel).apply(); } - myOrigInjection.generatePlaces(); - myEditCopy.copyFrom(myOrigInjection); + + // auto-generated name should go last + apply(myOrigInjection); + if (!myOtherPanels.isEmpty()) { + myOrigInjection.generatePlaces(); + myEditCopy.copyFrom(myOrigInjection); + } } protected abstract void apply(T other); @SuppressWarnings({"unchecked"}) public final void reset() { + if (!myOtherPanels.isEmpty()) { + myEditCopy.copyFrom(myOrigInjection); + } for (Field panel : myOtherPanels) { getField(panel).reset(); } - myEditCopy.copyFrom(myOrigInjection); UIUtil.invokeAndWaitIfNeeded(new Runnable() { public void run() { resetImpl(); @@ -138,10 +140,4 @@ public abstract class AbstractInjectionPanel<T extends BaseInjection> implements updater.run(); } } - - protected class TreeUpdateListener extends DocumentAdapter { - public void documentChanged(DocumentEvent e) { - updateTree(); - } - } } diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/config/ui/AdvancedPanel.form b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/config/ui/AdvancedPanel.form index a45bfaff44cc..0ac5d480dd01 100644 --- a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/config/ui/AdvancedPanel.form +++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/config/ui/AdvancedPanel.form @@ -26,7 +26,7 @@ </constraints> <properties> <labelFor value="7bf4e"/> - <text value="&Value Pattern:"/> + <text value="&Value pattern:"/> <toolTipText value="<html>Enter a regular expression that selects the parts of the tag's or attribute's value the language should be injected into.<br>The pattern should contain exactly one capturing group.</html>"/> </properties> </component> @@ -43,7 +43,7 @@ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/> </constraints> <properties> - <text value="&Single File"/> + <text value="&Single file"/> </properties> </component> </children> diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/config/ui/BaseInjectionPanel.form b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/config/ui/BaseInjectionPanel.form index 0c9523fbb1ea..79dafacd8fa8 100644 --- a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/config/ui/BaseInjectionPanel.form +++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/config/ui/BaseInjectionPanel.form @@ -21,7 +21,7 @@ <clientProperties> <BorderFactoryClass class="java.lang.String" value="com.intellij.ui.IdeBorderFactory$PlainSmallWithoutIndent"/> </clientProperties> - <border type="none" title="Places Pattern"/> + <border type="none" title="Places Patterns"/> <children/> </grid> <nested-form id="b6e0b" form-file="org/intellij/plugins/intelliLang/inject/config/ui/AdvancedPanel.form" binding="myAdvancedPanel" custom-create="true"> @@ -43,7 +43,7 @@ </constraints> <properties> <labelFor value="5916a"/> - <text value="Display &Name:"/> + <text value="&Name:"/> </properties> </component> <component id="5916a" class="javax.swing.JTextField" binding="myNameTextField"> diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/config/ui/LanguagePanel.form b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/config/ui/LanguagePanel.form index 1f582bb85f03..e8e087e03189 100644 --- a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/config/ui/LanguagePanel.form +++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/config/ui/LanguagePanel.form @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.intellij.plugins.intelliLang.inject.config.ui.LanguagePanel"> - <grid id="27dc6" binding="myRoot" layout-manager="GridLayoutManager" row-count="3" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> + <grid id="27dc6" binding="myRoot" layout-manager="GridLayoutManager" row-count="3" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> <margin top="0" left="0" bottom="0" right="0"/> <constraints> <xy x="20" y="20" width="493" height="143"/> @@ -44,20 +44,15 @@ </constraints> <properties/> </component> - <hspacer id="7c4e8"> - <constraints> - <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="1" hsize-policy="7" anchor="0" fill="1" indent="0" use-parent-layout="false"/> - </constraints> - </hspacer> <component id="96508" class="com.intellij.ui.EditorTextField" binding="myPrefix"> <constraints> - <grid row="1" column="1" row-span="1" col-span="2" vsize-policy="0" hsize-policy="7" anchor="0" fill="1" indent="0" use-parent-layout="false"/> + <grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="7" anchor="0" fill="1" indent="0" use-parent-layout="false"/> </constraints> <properties/> </component> <component id="dcdf7" class="com.intellij.ui.EditorTextField" binding="mySuffix"> <constraints> - <grid row="2" column="1" row-span="1" col-span="2" vsize-policy="0" hsize-policy="7" anchor="0" fill="1" indent="0" use-parent-layout="false"/> + <grid row="2" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="7" anchor="0" fill="1" indent="0" use-parent-layout="false"/> </constraints> <properties/> </component> diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/config/ui/configurables/InjectionConfigurable.java b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/config/ui/configurables/InjectionConfigurable.java deleted file mode 100644 index b09de2e4c915..000000000000 --- a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/config/ui/configurables/InjectionConfigurable.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2006 Sascha Weinreuter - * - * 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 org.intellij.plugins.intelliLang.inject.config.ui.configurables; - -import com.intellij.openapi.options.ConfigurationException; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.NamedConfigurable; -import org.intellij.plugins.intelliLang.inject.config.Injection; -import org.intellij.plugins.intelliLang.inject.config.ui.InjectionPanel; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; - -public abstract class InjectionConfigurable<T extends Injection, P extends InjectionPanel<T>> extends NamedConfigurable<T> { - private final Runnable myTreeUpdater; - protected final T myInjection; - protected final Project myProject; - private P myPanel; - - public InjectionConfigurable(T injection, Runnable treeUpdater, Project project) { - myProject = project; - myInjection = injection; - myTreeUpdater = treeUpdater; - } - - public void setDisplayName(String name) { - } - - public T getEditableObject() { - return myInjection; - } - - @Nullable - @NonNls - public String getHelpTopic() { - return null; - } - - public JComponent createOptionsPanel() { - myPanel = createOptionsPanelImpl(); - myPanel.addUpdater(myTreeUpdater); - return myPanel.getComponent(); - } - - protected abstract P createOptionsPanelImpl(); - - public P getPanel() { - return myPanel; - } - - public boolean isModified() { - return myPanel.isModified(); - } - - public void apply() throws ConfigurationException { - myPanel.apply(); - } - - public void reset() { - myPanel.reset(); - } - - public void disposeUIResources() { - myPanel = null; - } - - public String getDisplayName() { - final P p = getPanel(); - return p != null ? p.getInjection().getDisplayName() : myInjection.getDisplayName(); - } -} diff --git a/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/AbstractTagInjection.java b/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/AbstractTagInjection.java index 0662837d0574..491b277b9393 100644 --- a/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/AbstractTagInjection.java +++ b/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/AbstractTagInjection.java @@ -16,7 +16,6 @@ package org.intellij.plugins.intelliLang.inject.config; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.util.JDOMExternalizer; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import com.intellij.psi.xml.XmlAttribute; @@ -44,7 +43,7 @@ import java.util.TreeSet; * * @see org.intellij.plugins.intelliLang.inject.config.XPathSupportProxy */ -public class AbstractTagInjection extends BaseInjection { +public abstract class AbstractTagInjection extends BaseInjection { private static final Logger LOG = Logger.getInstance("org.intellij.plugins.intelliLang.inject.config.AbstractTagInjection"); @@ -57,7 +56,7 @@ public class AbstractTagInjection extends BaseInjection { private String myXPathCondition = ""; private XPath myCompiledXPathCondition; - private boolean myApplyToSubTagTexts; + private boolean myApplyToSubTags; public AbstractTagInjection() { super(XmlLanguageInjectionSupport.XML_SUPPORT_ID); @@ -134,9 +133,7 @@ public class AbstractTagInjection extends BaseInjection { } @Override - public AbstractTagInjection copy() { - return new AbstractTagInjection().copyFrom(this); - } + public abstract AbstractTagInjection copy(); public AbstractTagInjection copyFrom(@NotNull BaseInjection o) { super.copyFrom(o); @@ -146,30 +143,21 @@ public class AbstractTagInjection extends BaseInjection { myTagNamespace = other.myTagNamespace; setXPathCondition(other.getXPathCondition()); - setApplyToSubTagTexts(other.isApplyToSubTagTexts()); + setApplyToSubTags(other.isApplyToSubTags()); } return this; } protected void readExternalImpl(Element e) { - if (e.getAttribute("injector-id") == null) { - setTagName(JDOMExternalizer.readString(e, "TAGNAME")); - setTagNamespace(JDOMExternalizer.readString(e, "TAGNAMESPACE")); - setXPathCondition(JDOMExternalizer.readString(e, "XPATH_CONDITION")); - - myApplyToSubTagTexts = JDOMExternalizer.readBoolean(e, "APPLY_TO_SUBTAGS"); - } - else { - setXPathCondition(e.getChildText("xpath-condition")); - myApplyToSubTagTexts = e.getChild("apply-to-subtags") != null; - } + setXPathCondition(e.getChildText("xpath-condition")); + myApplyToSubTags = e.getChild("apply-to-subtags") != null; } protected void writeExternalImpl(Element e) { if (StringUtil.isNotEmpty(myXPathCondition)) { e.addContent(new Element("xpath-condition").setText(myXPathCondition)); } - if (myApplyToSubTagTexts) { + if (myApplyToSubTags) { e.addContent(new Element("apply-to-subtags")); } } @@ -186,7 +174,7 @@ public class AbstractTagInjection extends BaseInjection { if (!myTagNamespace.equals(that.myTagNamespace)) return false; if (!myXPathCondition.equals(that.myXPathCondition)) return false; - if (myApplyToSubTagTexts != that.myApplyToSubTagTexts) return false; + if (myApplyToSubTags != that.myApplyToSubTags) return false; return true; } @@ -196,7 +184,7 @@ public class AbstractTagInjection extends BaseInjection { result = 31 * result + myTagNamespace.hashCode(); result = 31 * result + myXPathCondition.hashCode(); - result = 31 * result + (myApplyToSubTagTexts ? 1 : 0); + result = 31 * result + (myApplyToSubTags ? 1 : 0); return result; } @@ -215,12 +203,12 @@ public class AbstractTagInjection extends BaseInjection { return myXPathCondition.length() == 0; } - public boolean isApplyToSubTagTexts() { - return myApplyToSubTagTexts; + public boolean isApplyToSubTags() { + return myApplyToSubTags; } - public void setApplyToSubTagTexts(final boolean applyToSubTagTexts) { - myApplyToSubTagTexts = applyToSubTagTexts; + public void setApplyToSubTags(final boolean applyToSubTagTexts) { + myApplyToSubTags = applyToSubTagTexts; } @Override diff --git a/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/XmlAttributeInjection.java b/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/XmlAttributeInjection.java index 5ffe3271bb07..32a2239dd3a1 100644 --- a/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/XmlAttributeInjection.java +++ b/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/XmlAttributeInjection.java @@ -15,7 +15,6 @@ */ package org.intellij.plugins.intelliLang.inject.config; -import com.intellij.openapi.util.JDOMExternalizer; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import com.intellij.psi.xml.XmlAttribute; @@ -57,8 +56,7 @@ public class XmlAttributeInjection extends AbstractTagInjection { return element instanceof XmlAttribute && matches((XmlAttribute)element); } - @NotNull - public String getDisplayName() { + public String getGeneratedName() { final String tag = getTagName(); final String attributeName = getAttributeName(); if (!attributeName.equals(StringMatcher.NONE.getPattern())) { @@ -74,6 +72,7 @@ public class XmlAttributeInjection extends AbstractTagInjection { @Override public void generatePlaces() { + if (StringUtil.isEmpty(getDisplayName())) setDisplayName(getGeneratedName()); setInjectionPlaces(new InjectionPlace(getCompiler().createElementPattern(getPatternString(this), getDisplayName()), true)); } @@ -95,25 +94,14 @@ public class XmlAttributeInjection extends AbstractTagInjection { public XmlAttributeInjection copyFrom(@NotNull BaseInjection o) { super.copyFrom(o); if (o instanceof XmlAttributeInjection) { - final XmlAttributeInjection other = (XmlAttributeInjection)o; + XmlAttributeInjection other = (XmlAttributeInjection)o; + setApplyToSubTags(other.isApplyToSubTags()); setAttributeName(other.getAttributeName()); setAttributeNamespace(other.getAttributeNamespace()); } return this; } - protected void readExternalImpl(Element e) { - super.readExternalImpl(e); - if (e.getAttribute("injector-id") == null) { - setAttributeName(JDOMExternalizer.readString(e, "ATT_NAME")); - setAttributeNamespace(JDOMExternalizer.readString(e, "ATT_NAMESPACE")); - } - } - - protected void writeExternalImpl(Element e) { - super.writeExternalImpl(e); - } - @SuppressWarnings({"RedundantIfStatement"}) public boolean equals(Object o) { if (this == o) return true; @@ -142,7 +130,8 @@ public class XmlAttributeInjection extends AbstractTagInjection { if (StringUtil.isNotEmpty(name)) appendStringPattern(result, ".withLocalName(", name, ")"); if (StringUtil.isNotEmpty(namespace)) appendStringPattern(result, ".withNamespace(", namespace, ")"); if (StringUtil.isNotEmpty(injection.getTagName()) || StringUtil.isNotEmpty(injection.getTagNamespace())) { - result.append(".withParent(").append(XmlTagInjection.getPatternString(injection)).append(")"); + result.append(".").append(injection.isApplyToSubTags() ? "inside" : "withParent").append("(") + .append(XmlTagInjection.getPatternString(injection)).append(")"); } return result.toString(); } diff --git a/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/XmlTagInjection.java b/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/XmlTagInjection.java index ade0c399c84d..19d3a8c64f3b 100644 --- a/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/XmlTagInjection.java +++ b/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/XmlTagInjection.java @@ -31,8 +31,7 @@ public class XmlTagInjection extends AbstractTagInjection { return matches(context) && matchXPath(context); } - @NotNull - public String getDisplayName() { + public String getGeneratedName() { final String name = getTagName(); return name.length() > 0 ? name : "*"; } @@ -49,6 +48,7 @@ public class XmlTagInjection extends AbstractTagInjection { @Override public void generatePlaces() { + if (StringUtil.isEmpty(getDisplayName())) setDisplayName(getGeneratedName()); setInjectionPlaces(new InjectionPlace(getCompiler().createElementPattern(getPatternString(this), getDisplayName()), true)); } diff --git a/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/ui/AdvancedXmlPanel.form b/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/ui/AdvancedXmlPanel.form index c4cd838349cd..48df40018f5a 100644 --- a/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/ui/AdvancedXmlPanel.form +++ b/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/ui/AdvancedXmlPanel.form @@ -1,9 +1,9 @@ <?xml version="1.0" encoding="UTF-8"?> <form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.intellij.plugins.intelliLang.inject.config.ui.AdvancedXmlPanel"> - <grid id="27dc6" binding="myRoot" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> + <grid id="27dc6" binding="myRoot" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> <margin top="0" left="0" bottom="0" right="0"/> <constraints> - <xy x="20" y="20" width="531" height="97"/> + <xy x="20" y="20" width="531" height="123"/> </constraints> <properties/> <border type="none"/> @@ -25,7 +25,7 @@ </constraints> <properties> <labelFor value="7bf4e"/> - <text value="&Value Pattern:"/> + <text value="&Value pattern:"/> <toolTipText value="<html>Enter a regular expression that selects the parts of the tag's or attribute's value the language should be injected into.<br>The pattern should contain exactly one capturing group.</html>"/> </properties> </component> @@ -43,7 +43,7 @@ </constraints> <properties> <labelFor value="a6f2"/> - <text value="&XPath Condition:"/> + <text value="&XPath condition:"/> <toolTipText value="<html>Enter an XPath expression that the tag/attribute must match additionally to the name-condition specified above.<br>This requires the XPathView plugin to be installed</html>"/> </properties> </component> @@ -60,11 +60,16 @@ <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/> </constraints> <properties> - <text value="&Single File"/> + <text value="Single &file"/> </properties> </component> </children> </grid> + <vspacer id="5c79c"> + <constraints> + <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/> + </constraints> + </vspacer> </children> </grid> </form> diff --git a/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/ui/TagPanel.form b/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/ui/TagPanel.form index bcbe680d8e0d..70051cbf3730 100644 --- a/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/ui/TagPanel.form +++ b/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/ui/TagPanel.form @@ -8,7 +8,7 @@ <properties/> <border type="none"/> <children> - <grid id="7e115" layout-manager="GridLayoutManager" row-count="2" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> + <grid id="49b27" layout-manager="GridLayoutManager" row-count="2" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> <margin top="0" left="0" bottom="0" right="0"/> <constraints> <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/> @@ -19,25 +19,25 @@ </clientProperties> <border type="none" title="XML Tag"/> <children> - <component id="edc9a" class="javax.swing.JLabel"> + <component id="9c78e" class="javax.swing.JLabel"> <constraints> <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/> </constraints> <properties> - <labelFor value="16487"/> - <text value="&Local Name:"/> + <labelFor value="8f17"/> + <text value="&Local name:"/> </properties> </component> - <component id="1068d" class="javax.swing.JLabel"> + <component id="7d32d" class="javax.swing.JLabel"> <constraints> <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/> </constraints> <properties> - <labelFor value="eeee3"/> + <labelFor value="3d65f"/> <text value="&Namespace:"/> </properties> </component> - <component id="16487" class="com.intellij.ui.EditorTextField" binding="myLocalName" custom-create="true"> + <component id="8f17" class="com.intellij.ui.EditorTextField" binding="myLocalName" custom-create="true"> <constraints> <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="7" anchor="8" fill="1" indent="0" use-parent-layout="false"> <preferred-size width="150" height="-1"/> @@ -45,24 +45,29 @@ </constraints> <properties/> </component> - <component id="eeee3" class="com.intellij.openapi.ui.ComboBox" binding="myNamespace" custom-create="true"> + <component id="3d65f" class="com.intellij.openapi.ui.ComboBox" binding="myNamespace" custom-create="true"> <constraints> - <grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="7" anchor="8" fill="1" indent="0" use-parent-layout="false"/> + <grid row="1" column="1" row-span="1" col-span="2" vsize-policy="0" hsize-policy="7" anchor="8" fill="1" indent="0" use-parent-layout="false"/> </constraints> <properties> <editable value="true"/> </properties> </component> + <component id="3ed2c" class="javax.swing.JCheckBox" binding="myWithSubtags"> + <constraints> + <grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/> + </constraints> + <properties> + <text value="Sub-&tags"/> + </properties> + </component> </children> </grid> - <component id="bc807" class="javax.swing.JCheckBox" binding="myApplyRecursivelyCheckBox"> + <vspacer id="bdf19"> <constraints> - <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/> + <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/> </constraints> - <properties> - <text value="Apply to all text fragments &recursively"/> - </properties> - </component> + </vspacer> </children> </grid> </form> diff --git a/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/ui/TagPanel.java b/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/ui/TagPanel.java index 42db562728ff..2b224a1be58d 100644 --- a/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/ui/TagPanel.java +++ b/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/ui/TagPanel.java @@ -28,7 +28,6 @@ import com.intellij.ui.LanguageTextField; import org.intellij.lang.regexp.RegExpLanguage; import org.intellij.plugins.intelliLang.inject.config.AbstractTagInjection; import org.intellij.plugins.intelliLang.inject.config.JspSupportProxy; -import org.intellij.plugins.intelliLang.inject.config.XmlTagInjection; import javax.swing.*; import java.util.ArrayList; @@ -43,14 +42,13 @@ public class TagPanel extends AbstractInjectionPanel<AbstractTagInjection> { private EditorTextField myLocalName; private ComboBox myNamespace; - private JCheckBox myApplyRecursivelyCheckBox; + private JCheckBox myWithSubtags; public TagPanel(Project project, AbstractTagInjection injection) { super(injection, project); $$$setupUI$$$(); myNamespace.setModel(createNamespaceUriModel(myProject)); - myLocalName.getDocument().addDocumentListener(new TreeUpdateListener()); } public static ComboBoxModel createNamespaceUriModel(Project project) { @@ -95,19 +93,13 @@ public class TagPanel extends AbstractInjectionPanel<AbstractTagInjection> { protected void resetImpl() { myLocalName.setText(myOrigInjection.getTagName()); myNamespace.getEditor().setItem(myOrigInjection.getTagNamespace()); - final boolean isXmlTag = myOrigInjection instanceof XmlTagInjection; - myApplyRecursivelyCheckBox.setVisible(isXmlTag); - if (isXmlTag) { - myApplyRecursivelyCheckBox.setSelected(((XmlTagInjection)myOrigInjection).isApplyToSubTagTexts()); - } + myWithSubtags.setSelected(myOrigInjection.isApplyToSubTags()); } - protected void apply(AbstractTagInjection i) { - i.setTagName(myLocalName.getText()); - i.setTagNamespace(getNamespace()); - if (i instanceof XmlTagInjection) { - ((XmlTagInjection)i).setApplyToSubTagTexts(myApplyRecursivelyCheckBox.isSelected()); - } + protected void apply(AbstractTagInjection other) { + other.setTagName(myLocalName.getText()); + other.setTagNamespace(getNamespace()); + other.setApplyToSubTags(myWithSubtags.isSelected()); } private String getNamespace() { diff --git a/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/ui/XmlAttributePanel.form b/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/ui/XmlAttributePanel.form index 1dce7a917205..a9d67f3f938d 100644 --- a/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/ui/XmlAttributePanel.form +++ b/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/ui/XmlAttributePanel.form @@ -1,22 +1,22 @@ <?xml version="1.0" encoding="UTF-8"?> <form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.intellij.plugins.intelliLang.inject.config.ui.XmlAttributePanel"> - <grid id="27dc6" binding="myRoot" layout-manager="GridLayoutManager" row-count="5" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> + <grid id="27dc6" binding="myRoot" layout-manager="GridLayoutManager" row-count="6" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> <margin top="0" left="0" bottom="0" right="0"/> <constraints> - <xy x="20" y="20" width="500" height="479"/> + <xy x="20" y="20" width="500" height="551"/> </constraints> <properties/> <border type="none"/> <children> <nested-form id="260fa" form-file="org/intellij/plugins/intelliLang/inject/config/ui/TagPanel.form" binding="myTagPanel" custom-create="true"> <constraints> - <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="7" anchor="0" fill="1" indent="0" use-parent-layout="false"/> + <grid row="2" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="7" anchor="0" fill="1" indent="0" use-parent-layout="false"/> </constraints> </nested-form> <grid id="ea19" layout-manager="GridLayoutManager" row-count="2" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> <margin top="0" left="0" bottom="0" right="0"/> <constraints> - <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/> + <grid row="3" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/> </constraints> <properties/> <clientProperties> @@ -30,7 +30,7 @@ </constraints> <properties> <labelFor value="cca5"/> - <text value="L&ocal Name:"/> + <text value="L&ocal name:"/> <toolTipText value="Regular expression to match the attribute name, e.g. "on.*""/> </properties> </component> @@ -65,19 +65,46 @@ </grid> <vspacer id="f7910"> <constraints> - <grid row="4" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/> + <grid row="5" column="0" row-span="1" col-span="2" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/> </constraints> </vspacer> <nested-form id="8cfe8" form-file="org/intellij/plugins/intelliLang/inject/config/ui/LanguagePanel.form" binding="myLanguagePanel" custom-create="true"> <constraints> - <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="7" anchor="0" fill="1" indent="0" use-parent-layout="false"/> + <grid row="1" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="7" anchor="0" fill="1" indent="0" use-parent-layout="false"/> </constraints> </nested-form> <nested-form id="b6e0b" form-file="org/intellij/plugins/intelliLang/inject/config/ui/AdvancedXmlPanel.form" binding="myAdvancedPanel" custom-create="true"> <constraints> - <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="7" anchor="0" fill="1" indent="0" use-parent-layout="false"/> + <grid row="4" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="7" anchor="0" fill="1" indent="0" use-parent-layout="false"/> </constraints> </nested-form> + <grid id="2032c" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> + <margin top="0" left="0" bottom="0" right="0"/> + <constraints> + <grid row="0" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="0" anchor="0" fill="3" indent="0" use-parent-layout="false"/> + </constraints> + <properties/> + <border type="none"/> + <children> + <component id="e174b" class="javax.swing.JLabel"> + <constraints> + <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/> + </constraints> + <properties> + <labelFor value="80ca6"/> + <text value="&Name:"/> + </properties> + </component> + <component id="80ca6" class="javax.swing.JTextField" binding="myNameTextField"> + <constraints> + <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false"> + <preferred-size width="150" height="-1"/> + </grid> + </constraints> + <properties/> + </component> + </children> + </grid> </children> </grid> </form> diff --git a/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/ui/XmlAttributePanel.java b/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/ui/XmlAttributePanel.java index 9e8e935c39e8..79f451dd31f9 100644 --- a/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/ui/XmlAttributePanel.java +++ b/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/ui/XmlAttributePanel.java @@ -17,6 +17,8 @@ package org.intellij.plugins.intelliLang.inject.config.ui; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.ComboBox; +import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.EditorTextField; import com.intellij.ui.LanguageTextField; import org.intellij.lang.regexp.RegExpLanguage; @@ -35,6 +37,9 @@ public class XmlAttributePanel extends AbstractInjectionPanel<XmlAttributeInject private EditorTextField myLocalName; private ComboBox myNamespace; + private JTextField myNameTextField; + + private boolean myUseGeneratedName; public XmlAttributePanel(XmlAttributeInjection injection, Project project) { super(injection, project); @@ -43,9 +48,6 @@ public class XmlAttributePanel extends AbstractInjectionPanel<XmlAttributeInject myNamespace.setModel(TagPanel.createNamespaceUriModel(project)); init(injection.copy()); - - // be sure to add the listener after initializing the textfield's value - myLocalName.getDocument().addDocumentListener(new TreeUpdateListener()); } public JPanel getComponent() { @@ -53,13 +55,21 @@ public class XmlAttributePanel extends AbstractInjectionPanel<XmlAttributeInject } protected void resetImpl() { + myNameTextField.setText(myOrigInjection.getDisplayName()); myLocalName.setText(myOrigInjection.getAttributeName()); myNamespace.getEditor().setItem(myOrigInjection.getAttributeNamespace()); + + myUseGeneratedName = Comparing.equal(myOrigInjection.getDisplayName(), myOrigInjection.getGeneratedName()); } - protected void apply(XmlAttributeInjection i) { - i.setAttributeName(myLocalName.getText()); - i.setAttributeNamespace(getNamespace()); + protected void apply(XmlAttributeInjection other) { + other.setAttributeName(myLocalName.getText()); + other.setAttributeNamespace(getNamespace()); + + String name = myNameTextField.getText(); + boolean useGenerated = myUseGeneratedName && Comparing.equal(myOrigInjection.getDisplayName(), name); + String newName = useGenerated || StringUtil.isEmptyOrSpaces(name) ? other.getGeneratedName() : name; + other.setDisplayName(newName); } private String getNamespace() { diff --git a/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/ui/XmlTagPanel.form b/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/ui/XmlTagPanel.form index d19d8571c038..36585ab8a020 100644 --- a/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/ui/XmlTagPanel.form +++ b/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/ui/XmlTagPanel.form @@ -1,33 +1,60 @@ <?xml version="1.0" encoding="UTF-8"?> <form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.intellij.plugins.intelliLang.inject.config.ui.XmlTagPanel"> - <grid id="27dc6" binding="myRoot" layout-manager="GridLayoutManager" row-count="4" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> + <grid id="27dc6" binding="myRoot" layout-manager="GridLayoutManager" row-count="5" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> <margin top="0" left="0" bottom="0" right="0"/> <constraints> - <xy x="20" y="20" width="500" height="400"/> + <xy x="20" y="20" width="500" height="437"/> </constraints> <properties/> <border type="none"/> <children> <nested-form id="4c744" form-file="org/intellij/plugins/intelliLang/inject/config/ui/TagPanel.form" binding="myPanel" custom-create="true"> <constraints> - <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="7" anchor="0" fill="1" indent="0" use-parent-layout="false"/> + <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="7" anchor="0" fill="1" indent="0" use-parent-layout="false"/> </constraints> </nested-form> <vspacer id="12f92"> <constraints> - <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/> + <grid row="4" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/> </constraints> </vspacer> <nested-form id="4e64b" form-file="org/intellij/plugins/intelliLang/inject/config/ui/LanguagePanel.form" binding="myLanguagePanel" custom-create="true"> <constraints> - <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="7" anchor="0" fill="1" indent="0" use-parent-layout="false"/> + <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="7" anchor="0" fill="1" indent="0" use-parent-layout="false"/> </constraints> </nested-form> <nested-form id="fa14a" form-file="org/intellij/plugins/intelliLang/inject/config/ui/AdvancedXmlPanel.form" binding="myAdvancedPanel" custom-create="true"> <constraints> - <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="7" anchor="0" fill="1" indent="0" use-parent-layout="false"/> + <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="7" anchor="0" fill="1" indent="0" use-parent-layout="false"/> </constraints> </nested-form> + <grid id="d9c54" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> + <margin top="0" left="0" bottom="0" right="0"/> + <constraints> + <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="3" indent="0" use-parent-layout="false"/> + </constraints> + <properties/> + <border type="none"/> + <children> + <component id="d2267" class="javax.swing.JLabel"> + <constraints> + <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/> + </constraints> + <properties> + <labelFor value="6c531"/> + <text value="&Name:"/> + </properties> + </component> + <component id="6c531" class="javax.swing.JTextField" binding="myNameTextField"> + <constraints> + <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false"> + <preferred-size width="150" height="-1"/> + </grid> + </constraints> + <properties/> + </component> + </children> + </grid> </children> </grid> </form> diff --git a/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/ui/XmlTagPanel.java b/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/ui/XmlTagPanel.java index ffc92143ba0c..342cfd61cbe7 100644 --- a/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/ui/XmlTagPanel.java +++ b/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/ui/XmlTagPanel.java @@ -16,6 +16,8 @@ package org.intellij.plugins.intelliLang.inject.config.ui; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.text.StringUtil; import org.intellij.plugins.intelliLang.inject.config.XmlTagInjection; import javax.swing.*; @@ -28,6 +30,9 @@ public class XmlTagPanel extends AbstractInjectionPanel<XmlTagInjection> { AdvancedXmlPanel myAdvancedPanel; private JPanel myRoot; + private JTextField myNameTextField; + + private boolean myUseGeneratedName; public XmlTagPanel(XmlTagInjection injection, Project project) { super(injection, project); @@ -37,11 +42,16 @@ public class XmlTagPanel extends AbstractInjectionPanel<XmlTagInjection> { } protected void apply(XmlTagInjection other) { - // nothing to do, TagPanel.apply() already does this + String name = myNameTextField.getText(); + boolean useGenerated = myUseGeneratedName && Comparing.equal(myOrigInjection.getDisplayName(), name); + String newName = useGenerated || StringUtil.isEmptyOrSpaces(name) ? other.getGeneratedName() : name; + other.setDisplayName(newName); } protected void resetImpl() { - // same here^ + myNameTextField.setText(myOrigInjection.getDisplayName()); + + myUseGeneratedName = Comparing.equal(myOrigInjection.getDisplayName(), myOrigInjection.getGeneratedName()); } public JPanel getComponent() { diff --git a/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/ui/configurables/XmlAttributeInjectionConfigurable.java b/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/ui/configurables/XmlAttributeInjectionConfigurable.java deleted file mode 100644 index a0898e0e8664..000000000000 --- a/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/ui/configurables/XmlAttributeInjectionConfigurable.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2006 Sascha Weinreuter - * - * 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 org.intellij.plugins.intelliLang.inject.config.ui.configurables; - -import com.intellij.openapi.project.Project; -import org.intellij.plugins.intelliLang.inject.config.XmlAttributeInjection; -import org.intellij.plugins.intelliLang.inject.config.ui.XmlAttributePanel; - -public class XmlAttributeInjectionConfigurable extends InjectionConfigurable<XmlAttributeInjection, XmlAttributePanel> { - public XmlAttributeInjectionConfigurable(XmlAttributeInjection injection, Runnable treeUpdater, Project project) { - super(injection, treeUpdater, project); - } - - protected XmlAttributePanel createOptionsPanelImpl() { - return new XmlAttributePanel(myInjection, myProject); - } - - public String getBannerSlogan() { - return "Edit XML Attribute Injection"; - } -} diff --git a/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/ui/configurables/XmlTagInjectionConfigurable.java b/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/ui/configurables/XmlTagInjectionConfigurable.java deleted file mode 100644 index 655590b2f4cf..000000000000 --- a/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/config/ui/configurables/XmlTagInjectionConfigurable.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2006 Sascha Weinreuter - * - * 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 org.intellij.plugins.intelliLang.inject.config.ui.configurables; - -import com.intellij.openapi.project.Project; -import org.intellij.plugins.intelliLang.inject.config.XmlTagInjection; -import org.intellij.plugins.intelliLang.inject.config.ui.XmlTagPanel; - -public class XmlTagInjectionConfigurable extends InjectionConfigurable<XmlTagInjection, XmlTagPanel> { - public XmlTagInjectionConfigurable(XmlTagInjection injection, Runnable treeUpdater, Project project) { - super(injection, treeUpdater, project); - } - - public String getBannerSlogan() { - return "Edit XML Text Injection"; - } - - protected XmlTagPanel createOptionsPanelImpl() { - return new XmlTagPanel(myInjection, myProject); - } -} diff --git a/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/xml/XmlLanguageInjectionSupport.java b/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/xml/XmlLanguageInjectionSupport.java index 6c84a879b1a6..abe3349a9c15 100644 --- a/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/xml/XmlLanguageInjectionSupport.java +++ b/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/xml/XmlLanguageInjectionSupport.java @@ -43,8 +43,6 @@ import org.intellij.plugins.intelliLang.inject.config.*; import org.intellij.plugins.intelliLang.inject.config.ui.AbstractInjectionPanel; import org.intellij.plugins.intelliLang.inject.config.ui.XmlAttributePanel; import org.intellij.plugins.intelliLang.inject.config.ui.XmlTagPanel; -import org.intellij.plugins.intelliLang.inject.config.ui.configurables.XmlAttributeInjectionConfigurable; -import org.intellij.plugins.intelliLang.inject.config.ui.configurables.XmlTagInjectionConfigurable; import org.jdom.Element; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -66,7 +64,7 @@ public class XmlLanguageInjectionSupport extends AbstractLanguageInjectionSuppor final PsiElement p = host.getParent(); if (p instanceof XmlAttribute) { final String s = ((XmlAttribute)p).getName(); - return !(s.equals("xmlns") || s.startsWith("xmlns:")); + return !("xmlns".equals(s) || s.startsWith("xmlns:")); } } else if (host instanceof XmlText) { @@ -177,7 +175,7 @@ public class XmlLanguageInjectionSupport extends AbstractLanguageInjectionSuppor } }); if (builder.show() == DialogWrapper.OK_EXIT_CODE) { - return new AbstractTagInjection().copyFrom(xmlInjection); + return xmlInjection.copy(); } return null; } @@ -219,12 +217,15 @@ public class XmlLanguageInjectionSupport extends AbstractLanguageInjectionSuppor result.setTagNamespace(value); } } - else if (result instanceof XmlAttributeInjection && - "inside".equals(condition.getDebugMethodName()) && condition instanceof PatternConditionPlus) { - final ElementPattern<?> insidePattern = ((PatternConditionPlus)condition).getValuePattern(); + else if (result instanceof XmlAttributeInjection && condition instanceof PatternConditionPlus) { + boolean strict = "withParent".equals(condition.getDebugMethodName()); + if (!strict && !"inside".equals(condition.getDebugMethodName())) return null; + + result.setApplyToSubTags(!strict); + ElementPattern<?> insidePattern = ((PatternConditionPlus)condition).getValuePattern(); if (!XmlTag.class.equals(insidePattern.getCondition().getInitialCondition().getAcceptedClass())) return null; for (PatternCondition<?> insideCondition : insidePattern.getCondition().getConditions()) { - final String tagValue = extractValue(insideCondition); + String tagValue = extractValue(insideCondition); if (tagValue == null) return null; if ("withLocalName".equals(insideCondition.getDebugMethodName())) { result.setTagName(tagValue); @@ -232,10 +233,11 @@ public class XmlLanguageInjectionSupport extends AbstractLanguageInjectionSuppor else if ("withNamespace".equals(insideCondition.getDebugMethodName())) { result.setTagNamespace(tagValue); } - } } - else return null; + else { + return null; + } } result.generatePlaces(); return result; @@ -265,14 +267,17 @@ public class XmlLanguageInjectionSupport extends AbstractLanguageInjectionSuppor return null; } - public BaseInjection createInjection(final Element element) { - if (element.getName().equals(XmlAttributeInjection.class.getSimpleName())) { + public BaseInjection createInjection(Element element) { + String place = StringUtil.notNullize(element.getChildText("place"), ""); + if (place.startsWith("xmlAttribute")) { return new XmlAttributeInjection(); } - else if (element.getName().equals(XmlTagInjection.class.getSimpleName())) { + else if (place.startsWith("xmlTag")) { return new XmlTagInjection(); } - return new AbstractTagInjection(); + else { + return new BaseInjection(XML_SUPPORT_ID); + } } public Configurable[] createSettings(final Project project, final Configuration configuration) { @@ -298,12 +303,10 @@ public class XmlLanguageInjectionSupport extends AbstractLanguageInjectionSuppor final AbstractTagInjection originalInjection = (AbstractTagInjection)configuration.findExistingInjection(template); final XmlTagInjection newInjection = originalInjection == null? template : new XmlTagInjection().copyFrom(originalInjection); - if (InjectLanguageAction.doEditConfigurable(project, new XmlTagInjectionConfigurable(newInjection, null, project))) { - configuration.replaceInjectionsWithUndo( - project, Collections.singletonList(newInjection), - ContainerUtil.createMaybeSingletonList(originalInjection), - Collections.<PsiElement>emptyList()); - } + configuration.replaceInjectionsWithUndo( + project, Collections.singletonList(newInjection), + ContainerUtil.createMaybeSingletonList(originalInjection), + Collections.<PsiElement>emptyList()); } private static boolean doInjectInAttributeValue(final XmlAttributeValue host, final String languageId) { @@ -327,12 +330,10 @@ public class XmlLanguageInjectionSupport extends AbstractLanguageInjectionSuppor final Configuration configuration = InjectorUtils.getEditableInstance(project); final BaseInjection originalInjection = configuration.findExistingInjection(template); final BaseInjection newInjection = originalInjection == null ? template : originalInjection.copy(); - if (InjectLanguageAction.doEditConfigurable(project, new XmlAttributeInjectionConfigurable((XmlAttributeInjection)newInjection, null, project))) { - configuration.replaceInjectionsWithUndo( - project, Collections.singletonList(newInjection), - ContainerUtil.createMaybeSingletonList(originalInjection), - Collections.<PsiElement>emptyList()); - } + configuration.replaceInjectionsWithUndo( + project, Collections.singletonList(newInjection), + ContainerUtil.createMaybeSingletonList(originalInjection), + Collections.<PsiElement>emptyList()); } private static ArrayList<BaseInjection> collectInjections(final PsiElement host, diff --git a/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/xml/XmlLanguageInjector.java b/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/xml/XmlLanguageInjector.java index f8026edb4f11..c7808b0c1c40 100644 --- a/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/xml/XmlLanguageInjector.java +++ b/plugins/IntelliLang/xml-support/org/intellij/plugins/intelliLang/inject/xml/XmlLanguageInjector.java @@ -32,6 +32,7 @@ import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil; import com.intellij.psi.xml.*; import com.intellij.util.PairProcessor; import com.intellij.util.PatternValuesIndex; +import com.intellij.util.containers.ContainerUtil; import gnu.trove.THashMap; import org.intellij.plugins.intelliLang.Configuration; import org.intellij.plugins.intelliLang.inject.InjectedLanguage; @@ -113,8 +114,7 @@ public final class XmlLanguageInjector implements MultiHostInjector { if (language == null) continue; final boolean separateFiles = !injection.isSingleFile() && StringUtil.isNotEmpty(injection.getValuePattern()); - final List<Trinity<PsiLanguageInjectionHost, InjectedLanguage, TextRange>> result = - new ArrayList<Trinity<PsiLanguageInjectionHost, InjectedLanguage, TextRange>>(); + final List<Trinity<PsiLanguageInjectionHost, InjectedLanguage, TextRange>> result = ContainerUtil.newArrayList(); xmlTag.acceptChildren(new PsiElementVisitor() { @Override @@ -130,7 +130,7 @@ public final class XmlLanguageInjector implements MultiHostInjector { } else if (element instanceof XmlTag) { if (!separateFiles) unparsableRef.set(Boolean.TRUE); - if (injection instanceof AbstractTagInjection && ((AbstractTagInjection)injection).isApplyToSubTagTexts()) { + if (injection instanceof AbstractTagInjection && ((AbstractTagInjection)injection).isApplyToSubTags()) { element.acceptChildren(this); } } diff --git a/plugins/git4idea/src/git4idea/log/GitLogProvider.java b/plugins/git4idea/src/git4idea/log/GitLogProvider.java index 0a8bc3723d3f..672e4eb8dead 100644 --- a/plugins/git4idea/src/git4idea/log/GitLogProvider.java +++ b/plugins/git4idea/src/git4idea/log/GitLogProvider.java @@ -29,12 +29,7 @@ import com.intellij.util.ExceptionUtil; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import com.intellij.vcs.log.*; -import com.intellij.vcs.log.data.VcsLogBranchFilter; -import com.intellij.vcs.log.data.VcsLogDateFilter; -import com.intellij.vcs.log.data.VcsLogStructureFilter; -import com.intellij.vcs.log.data.VcsLogUserFilter; import com.intellij.vcs.log.impl.HashImpl; -import com.intellij.vcs.log.ui.filter.VcsLogTextFilter; import git4idea.GitLocalBranch; import git4idea.GitRemoteBranch; import git4idea.GitVcs; @@ -189,7 +184,11 @@ public class GitLogProvider implements VcsLogProvider { @NotNull @Override public List<? extends VcsFullCommitDetails> getFilteredDetails(@NotNull final VirtualFile root, - @NotNull Collection<VcsLogFilter> filters, + @NotNull Collection<VcsLogBranchFilter> branchFilters, + @NotNull Collection<VcsLogUserFilter> userFilters, + @NotNull Collection<VcsLogDateFilter> dateFilters, + @NotNull Collection<VcsLogTextFilter> textFilters, + @NotNull Collection<VcsLogStructureFilter> structureFilters, int maxCount) throws VcsException { if (!isRepositoryReady(root)) { return Collections.emptyList(); @@ -197,21 +196,19 @@ public class GitLogProvider implements VcsLogProvider { List<String> filterParameters = ContainerUtil.newArrayList(); - List<VcsLogBranchFilter> branchFilters = ContainerUtil.findAll(filters, VcsLogBranchFilter.class); if (!branchFilters.isEmpty()) { // git doesn't support filtering by several branches very well (--branches parameter give a weak pattern capabilities) // => by now assuming there is only one branch filter. if (branchFilters.size() > 1) { LOG.warn("More than one branch filter was passed. Using only the first one."); } - VcsLogBranchFilter branchFilter = branchFilters.get(0); + VcsLogBranchFilter branchFilter = branchFilters.iterator().next(); filterParameters.add(branchFilter.getBranchName()); } else { filterParameters.addAll(GitHistoryUtils.LOG_ALL); } - List<VcsLogUserFilter> userFilters = ContainerUtil.findAll(filters, VcsLogUserFilter.class); if (!userFilters.isEmpty()) { String authorFilter = joinFilters(userFilters, new Function<VcsLogUserFilter, String>() { @Override @@ -222,7 +219,6 @@ public class GitLogProvider implements VcsLogProvider { filterParameters.add(prepareParameter("author", authorFilter)); } - List<VcsLogDateFilter> dateFilters = ContainerUtil.findAll(filters, VcsLogDateFilter.class); if (!dateFilters.isEmpty()) { // assuming there is only one date filter, until filter expressions are defined VcsLogDateFilter filter = dateFilters.iterator().next(); @@ -234,7 +230,6 @@ public class GitLogProvider implements VcsLogProvider { } } - List<VcsLogTextFilter> textFilters = ContainerUtil.findAll(filters, VcsLogTextFilter.class); if (textFilters.size() > 1) { LOG.warn("Expected only one text filter: " + textFilters); } @@ -249,8 +244,7 @@ public class GitLogProvider implements VcsLogProvider { } filterParameters.add("--date-order"); - // note: this filter must be the last parameter, because it uses "--" which separates parameters from paths - List<VcsLogStructureFilter> structureFilters = ContainerUtil.findAll(filters, VcsLogStructureFilter.class); + // note: structure filter must be the last parameter, because it uses "--" which separates parameters from paths if (!structureFilters.isEmpty()) { filterParameters.add("--"); for (VcsLogStructureFilter filter : structureFilters) { @@ -281,7 +275,7 @@ public class GitLogProvider implements VcsLogProvider { return "--" + paramName + "=" + value; // no value escaping needed, because the parameter itself will be quoted by GeneralCommandLine } - private static <T> String joinFilters(List<T> filters, Function<T, String> toString) { + private static <T> String joinFilters(Collection<T> filters, Function<T, String> toString) { return StringUtil.join(filters, toString, "\\|"); } diff --git a/plugins/gradle/src/META-INF/plugin.xml b/plugins/gradle/src/META-INF/plugin.xml index fc7d25465569..034ef355421d 100644 --- a/plugins/gradle/src/META-INF/plugin.xml +++ b/plugins/gradle/src/META-INF/plugin.xml @@ -67,12 +67,14 @@ <externalSystemConfigLocator implementation="org.jetbrains.plugins.gradle.service.settings.GradleConfigLocator"/> <externalSystemManager implementation="org.jetbrains.plugins.gradle.GradleManager"/> + <externalProjectDataService implementation="org.jetbrains.plugins.gradle.service.project.data.BuildClasspathModuleGradleDataService"/> <applicationService serviceImplementation="org.jetbrains.plugins.gradle.service.GradleInstallationManager"/> <projectService serviceImplementation="org.jetbrains.plugins.gradle.settings.GradleSettings"/> <projectService serviceImplementation="org.jetbrains.plugins.gradle.settings.GradleLocalSettings"/> <projectService serviceImplementation="org.jetbrains.plugins.gradle.service.project.GradleNotification"/> + <projectService serviceImplementation="org.jetbrains.plugins.gradle.service.GradleBuildClasspathManager"/> <configurationProducer implementation="org.jetbrains.plugins.gradle.service.execution.GradleRuntimeConfigurationProducer"/> diff --git a/plugins/gradle/src/META-INF/services/org.jetbrains.plugins.gradle.model.ModelBuilderService b/plugins/gradle/src/META-INF/services/org.jetbrains.plugins.gradle.model.ModelBuilderService index 3989c7cb0610..430ac6f37ba0 100644 --- a/plugins/gradle/src/META-INF/services/org.jetbrains.plugins.gradle.model.ModelBuilderService +++ b/plugins/gradle/src/META-INF/services/org.jetbrains.plugins.gradle.model.ModelBuilderService @@ -16,3 +16,4 @@ org.jetbrains.plugins.gradle.model.builder.WarModelBuilderImpl org.jetbrains.plugins.gradle.model.builder.ModelDependenciesBuilderImpl org.jetbrains.plugins.gradle.model.builder.ModuleExtendedModelBuilderImpl +org.jetbrains.plugins.gradle.model.builder.ModelBuildScriptClasspathBuilderImpl diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/config/GradleClassFinder.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/config/GradleClassFinder.java index f85c75acce16..d1fa23ad150e 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/config/GradleClassFinder.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/config/GradleClassFinder.java @@ -16,13 +16,15 @@ package org.jetbrains.plugins.gradle.config; +import com.intellij.openapi.externalSystem.psi.search.ExternalModuleBuildGlobalSearchScope; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.NonClasspathClassFinder; +import com.intellij.psi.search.GlobalSearchScope; import org.jetbrains.annotations.NotNull; -import org.jetbrains.plugins.gradle.service.GradleInstallationManager; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.gradle.service.GradleBuildClasspathManager; -import java.util.Collections; import java.util.List; /** @@ -30,19 +32,31 @@ import java.util.List; */ public class GradleClassFinder extends NonClasspathClassFinder { - @NotNull private final GradleInstallationManager myLibraryManager; + @NotNull private final GradleBuildClasspathManager myBuildClasspathManager; - public GradleClassFinder(Project project, @NotNull GradleInstallationManager manager) { - super(project, true, true); - myLibraryManager = manager; + public GradleClassFinder(Project project, @NotNull GradleBuildClasspathManager buildClasspathManager) { + super(project, true); + myBuildClasspathManager = buildClasspathManager; } @Override protected List<VirtualFile> calcClassRoots() { - final List<VirtualFile> roots = myLibraryManager.getClassRoots(myProject); - if (roots != null) { - return roots; + // do not use default NonClasspathClassFinder caching strategy based on PSI change + // the caching performed in GradleBuildClasspathManager + throw new AssertionError(); + } + + @Override + protected List<VirtualFile> getClassRoots() { + return myBuildClasspathManager.getAllClasspathEntries(); + } + + @Override + protected List<VirtualFile> getClassRoots(@Nullable GlobalSearchScope scope) { + if (scope instanceof ExternalModuleBuildGlobalSearchScope) { + ExternalModuleBuildGlobalSearchScope externalModuleBuildGlobalSearchScope = (ExternalModuleBuildGlobalSearchScope)scope; + return myBuildClasspathManager.getModuleClasspathEntries(externalModuleBuildGlobalSearchScope.getExternalModulePath()); } - return Collections.emptyList(); + return myBuildClasspathManager.getAllClasspathEntries(); } }
\ No newline at end of file diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/config/GradleScriptType.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/config/GradleScriptType.java index b7ecd351d58d..d5984903fe90 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/config/GradleScriptType.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/config/GradleScriptType.java @@ -21,12 +21,18 @@ import com.intellij.execution.*; import com.intellij.execution.configurations.JavaParameters; import com.intellij.execution.configurations.RunProfile; import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.externalSystem.psi.search.ExternalModuleBuildGlobalSearchScope; import com.intellij.openapi.externalSystem.util.ExternalSystemConstants; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.JdkOrderEntry; +import com.intellij.openapi.roots.ModuleRootManager; +import com.intellij.openapi.roots.OrderEntry; import com.intellij.openapi.roots.OrderEnumerator; +import com.intellij.openapi.roots.impl.LibraryScopeCache; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.io.FileUtilRt; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; @@ -40,8 +46,10 @@ import icons.GradleIcons; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.gradle.execution.GradleTaskLocation; -import org.jetbrains.plugins.gradle.util.GradleConstants; +import org.jetbrains.plugins.gradle.service.GradleBuildClasspathManager; import org.jetbrains.plugins.gradle.service.GradleInstallationManager; +import org.jetbrains.plugins.gradle.service.resolve.GradleResolverUtil; +import org.jetbrains.plugins.gradle.util.GradleConstants; import org.jetbrains.plugins.groovy.config.GroovyConfigUtils; import org.jetbrains.plugins.groovy.extensions.GroovyScriptType; import org.jetbrains.plugins.groovy.lang.psi.GroovyFile; @@ -72,9 +80,9 @@ public class GradleScriptType extends GroovyScriptType { private static final Pattern MAIN_CLASS_NAME_PATTERN = Pattern.compile("\nSTARTER_MAIN_CLASS=(.*)\n"); public static final GroovyScriptType INSTANCE = new GradleScriptType(); - + private GradleScriptType() { - super(GradleConstants.EXTENSION); + super(GradleConstants.EXTENSION); } @NotNull @@ -89,7 +97,7 @@ public class GradleScriptType extends GroovyScriptType { if (params == null) { return false; } - + final List<String> tasks = getTasksTarget(location); if (tasks == null) { return false; @@ -218,7 +226,9 @@ public class GradleScriptType extends GroovyScriptType { throw new CantRunException(String.format("Module '%s' is not backed by gradle", module.getName())); } final VirtualFile gradleHome = libraryManager.getGradleHome(module, project, rootProjectPath); - assert gradleHome != null; + if(gradleHome == null) { + throw new CantRunException("Gradle home can not be found"); + } params.setMainClass(findMainClass(gradleHome, script, project)); @@ -249,11 +259,11 @@ public class GradleScriptType extends GroovyScriptType { params.getVMParametersList().add("-Dgradle.home=" + FileUtil.toSystemDependentName(gradleHome.getPath())); setToolsJar(params); - + final String scriptPath = configuration.getScriptPath(); if (scriptPath == null) { throw new CantRunException("Target script is undefined"); - } + } params.getProgramParametersList().add("--build-file"); params.getProgramParametersList().add(FileUtil.toSystemDependentName(scriptPath)); params.getProgramParametersList().addParametersString(configuration.getProgramParameters()); @@ -298,22 +308,28 @@ public class GradleScriptType extends GroovyScriptType { @Override public GlobalSearchScope patchResolveScope(@NotNull GroovyFile file, @NotNull GlobalSearchScope baseScope) { + if (!FileUtilRt.extensionEquals(file.getName(), GradleConstants.EXTENSION)) return baseScope; + + final Collection<VirtualFile> files; + GlobalSearchScope result = GlobalSearchScope.EMPTY_SCOPE; final Module module = ModuleUtilCore.findModuleForPsiElement(file); - final GradleInstallationManager libraryManager = ServiceManager.getService(GradleInstallationManager.class); if (module != null) { - if (libraryManager.getGradleHome(module) != null) { - return baseScope; + for (OrderEntry entry : ModuleRootManager.getInstance(module).getOrderEntries()) { + if (entry instanceof JdkOrderEntry) { + GlobalSearchScope scopeForSdk = LibraryScopeCache.getInstance(module.getProject()).getScopeForSdk((JdkOrderEntry)entry); + result = result.uniteWith(scopeForSdk); + } } - } - final Collection<VirtualFile> files = libraryManager.getClassRoots(file.getProject()); - if (files == null || files.isEmpty()) { - return baseScope; - } + String modulePath = module.getOptionValue(ExternalSystemConstants.LINKED_PROJECT_PATH_KEY); + if(modulePath == null) return result; + + files = GradleBuildClasspathManager.getInstance(file.getProject()).getModuleClasspathEntries(modulePath); - GlobalSearchScope result = baseScope; - for (final VirtualFile root : files) { - result = result.uniteWith(new NonClasspathDirectoryScope(root)); + for (final VirtualFile root : files) { + result = result.uniteWith(new NonClasspathDirectoryScope(root)); + } + result = new ExternalModuleBuildGlobalSearchScope(result, modulePath); } return result; } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/integrations/maven/ImportMavenRepositoriesTask.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/integrations/maven/ImportMavenRepositoriesTask.java index f34748e44487..d14125e85122 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/integrations/maven/ImportMavenRepositoriesTask.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/integrations/maven/ImportMavenRepositoriesTask.java @@ -15,6 +15,7 @@ */ package org.jetbrains.plugins.gradle.integrations.maven; +import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.application.Result; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.externalSystem.util.ExternalSystemConstants; @@ -97,9 +98,9 @@ public class ImportMavenRepositoriesTask implements Runnable { final PsiFile[] psiFiles = ArrayUtil.toObjectArray(psiFileList, PsiFile.class); - final Set<MavenRemoteRepository> mavenRemoteRepositories = new WriteCommandAction<Set<MavenRemoteRepository>>(myProject, psiFiles) { + final Set<MavenRemoteRepository> mavenRemoteRepositories = new ReadAction<Set<MavenRemoteRepository>>() { @Override - protected void run(Result<Set<MavenRemoteRepository>> result) throws Throwable { + protected void run(@NotNull Result<Set<MavenRemoteRepository>> result) throws Throwable { Set<MavenRemoteRepository> myRemoteRepositories = ContainerUtil.newHashSet(); for (PsiFile psiFile : psiFiles) { List<GrClosableBlock> repositoriesBlocks = ContainerUtil.newArrayList(); @@ -118,7 +119,7 @@ public class ImportMavenRepositoriesTask implements Runnable { } }.execute().getResultObject(); - if (mavenRemoteRepositories.isEmpty()) return; + if (mavenRemoteRepositories == null || mavenRemoteRepositories.isEmpty()) return; MavenRepositoriesHolder.getInstance(myProject).update(mavenRemoteRepositories); diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/model/BuildScriptClasspathModel.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/BuildScriptClasspathModel.java new file mode 100644 index 000000000000..6854655609ca --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/BuildScriptClasspathModel.java @@ -0,0 +1,28 @@ +/* + * 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 org.jetbrains.plugins.gradle.model; + +import org.gradle.tooling.model.DomainObjectSet; + +import java.io.Serializable; + +/** + * @author Vladislav.Soroka + * @since 12/20/13 + */ +public interface BuildScriptClasspathModel extends Serializable { + DomainObjectSet<? extends ClasspathEntryModel> getClasspath(); +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/model/ClasspathEntryModel.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/ClasspathEntryModel.java new file mode 100644 index 000000000000..09120caccacf --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/ClasspathEntryModel.java @@ -0,0 +1,36 @@ +/* + * 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 org.jetbrains.plugins.gradle.model; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; + +/** + * @author Vladislav.Soroka + * @since 12/20/13 + */ +public interface ClasspathEntryModel { + @NotNull + File getClassesFile(); + + @Nullable + File getSourcesFile(); + + @Nullable + File getJavadocFile(); +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/model/GradleDependencyScope.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/GradleDependencyScope.java index 7186312c24b4..78caa4d9f4d5 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/model/GradleDependencyScope.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/GradleDependencyScope.java @@ -22,11 +22,6 @@ import org.jetbrains.annotations.Nullable; * @since 11/25/13 */ public enum GradleDependencyScope { - // Implicit scopes - PROVIDED("provided", "provided", true, true, true, true), - OPTIONAL("optional", "compile", true, true, true, true), - - // Java Plugin Scopes /** * Compile time dependencies @@ -74,7 +69,11 @@ public enum GradleDependencyScope { /** * Compiles test Scala source files. */ - COMPILE_TEST_SCALA("compileTestScala", "test", false, false, true, true); + COMPILE_TEST_SCALA("compileTestScala", "test", false, false, true, true), + + // Implicit scopes + PROVIDED("provided", "provided", true, true, true, true), + OPTIONAL("optional", "compile", true, true, true, true),; private final String myGradleName; private final String myIdeaMappingName; @@ -124,7 +123,7 @@ public enum GradleDependencyScope { @Nullable public static GradleDependencyScope fromIdeaMappingName(final String ideaMappingName) { for (GradleDependencyScope scope : values()) { - if (scope.myIdeaMappingName.equals(ideaMappingName.toLowerCase())) return scope; + if (scope.myIdeaMappingName.equals(ideaMappingName)) return scope; } return null; } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/model/builder/ModelBuildScriptClasspathBuilderImpl.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/builder/ModelBuildScriptClasspathBuilderImpl.java new file mode 100644 index 000000000000..875d9cc26bc6 --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/builder/ModelBuildScriptClasspathBuilderImpl.java @@ -0,0 +1,93 @@ +/* + * 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 org.jetbrains.plugins.gradle.model.builder; + +import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.gradle.plugins.ide.idea.IdeaPlugin; +import org.gradle.plugins.ide.idea.model.IdeaModel; +import org.gradle.plugins.ide.internal.IdeDependenciesExtractor; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.gradle.model.BuildScriptClasspathModel; +import org.jetbrains.plugins.gradle.model.ModelBuilderService; +import org.jetbrains.plugins.gradle.model.internal.BuildScriptClasspathModelImpl; +import org.jetbrains.plugins.gradle.model.internal.ClasspathEntryModelImpl; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * @author Vladislav.Soroka + * @since 12/20/13 + */ +public class ModelBuildScriptClasspathBuilderImpl implements ModelBuilderService { + + @Override + public boolean canBuild(String modelName) { + return BuildScriptClasspathModel.class.getName().equals(modelName); + } + + @Nullable + @Override + public Object buildAll(final String modelName, final Project project) { + boolean offline = false; + boolean downloadJavadoc = false; + boolean downloadSources = true; + + final IdeaPlugin ideaPlugin = project.getPlugins().getPlugin(IdeaPlugin.class); + + if (ideaPlugin != null) { + IdeaModel ideaModel = ideaPlugin.getModel(); + if (ideaModel != null && ideaModel.getModule() != null) { + offline = ideaModel.getModule().isOffline(); + downloadJavadoc = ideaModel.getModule().isDownloadJavadoc(); + downloadSources = ideaModel.getModule().isDownloadSources(); + } + } + + final IdeDependenciesExtractor dependenciesExtractor = new IdeDependenciesExtractor(); + + final Configuration configuration = project.getBuildscript().getConfigurations().findByName("classpath"); + Collection<Configuration> plusConfigurations = Collections.singletonList(configuration); + + BuildScriptClasspathModelImpl buildScriptClasspath = new BuildScriptClasspathModelImpl(); + + if (!offline) { + // download sources and/or javadoc + List<IdeDependenciesExtractor.IdeRepoFileDependency> repoFileDependencies = dependenciesExtractor.extractRepoFileDependencies( + project.getConfigurations(), plusConfigurations, new ArrayList<Configuration>(), downloadSources, downloadJavadoc); + + for (IdeDependenciesExtractor.IdeRepoFileDependency dependency : repoFileDependencies) { + if (dependency.getFile() == null) continue; + + buildScriptClasspath.add( + new ClasspathEntryModelImpl(dependency.getFile(), dependency.getSourceFile(), dependency.getJavadocFile())); + } + } + + final List<IdeDependenciesExtractor.IdeLocalFileDependency> localFileDependencies = + dependenciesExtractor.extractLocalFileDependencies(plusConfigurations, new ArrayList<Configuration>()); + + for (IdeDependenciesExtractor.IdeLocalFileDependency dependency : localFileDependencies) { + if (dependency.getFile() == null) continue; + buildScriptClasspath.add(new ClasspathEntryModelImpl(dependency.getFile(), null, null)); + } + + return buildScriptClasspath; + } +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/model/builder/ModelDependenciesBuilderImpl.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/builder/ModelDependenciesBuilderImpl.java index 05864829e6ed..4c0e3914185e 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/model/builder/ModelDependenciesBuilderImpl.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/builder/ModelDependenciesBuilderImpl.java @@ -24,6 +24,7 @@ import org.gradle.plugins.ide.idea.IdeaPlugin; import org.gradle.plugins.ide.idea.model.IdeaModel; import org.gradle.plugins.ide.internal.IdeDependenciesExtractor; import org.gradle.tooling.model.idea.IdeaDependency; +import org.gradle.util.GradleVersion; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.gradle.model.GradleDependencyScope; import org.jetbrains.plugins.gradle.model.ModelBuilderService; @@ -54,7 +55,7 @@ public class ModelDependenciesBuilderImpl implements ModelBuilderService { public Object buildAll(final String modelName, final Project project) { final List<IdeaDependency> dependencies = new ArrayList<IdeaDependency>(); - final Map<DependencyVersionId, Scopes> scopesMap = new HashMap<DependencyVersionId, Scopes>(); + final Map<DependencyVersionId, Scopes> scopesMap = new LinkedHashMap<DependencyVersionId, Scopes>(); final IdeDependenciesExtractor dependenciesExtractor = new IdeDependenciesExtractor(); boolean offline = false; @@ -148,6 +149,7 @@ public class ModelDependenciesBuilderImpl implements ModelBuilderService { versionId.getClassifier() ); libraryDependency.setFile(fileDependency.getFile()); + attachGradleSdkSources(libraryDependency, fileDependency); dependencies.add(libraryDependency); } } @@ -156,6 +158,32 @@ public class ModelDependenciesBuilderImpl implements ModelBuilderService { return new ProjectDependenciesModelImpl(project.getPath(), dependencies); } + private static void attachGradleSdkSources(IdeaSingleEntryLibraryDependencyImpl libraryDependency, + IdeDependenciesExtractor.IdeLocalFileDependency localFileDependency) { + final String libName = localFileDependency.getFile().getName(); + if (localFileDependency.getFile() == null || !libName.startsWith("gradle-")) return; + + File libOrPluginsFile = localFileDependency.getFile().getParentFile(); + if (libOrPluginsFile != null && ("plugins".equals(libOrPluginsFile.getName()))) { + libOrPluginsFile = libOrPluginsFile.getParentFile(); + } + + if (libOrPluginsFile != null && "lib".equals(libOrPluginsFile.getName()) && libOrPluginsFile.getParentFile() != null) { + File srcDir = new File(libOrPluginsFile.getParentFile(), "src"); + if (GradleVersion.current().compareTo(GradleVersion.version("1.9")) >= 0) { + int endIndex = libName.indexOf(GradleVersion.current().getVersion() + ".jar"); + if (endIndex != -1) { + String srcDirChild = libName.substring("gradle-".length(), endIndex - 1); + srcDir = new File(srcDir, srcDirChild); + } + } + + if (srcDir.isDirectory()) { + libraryDependency.setSource(srcDir); + } + } + } + @Nullable private static String findDeDuplicatedModuleName(Project project) { if (project.hasProperty(MODULE_PROPERTY)) { @@ -261,15 +289,13 @@ public class ModelDependenciesBuilderImpl implements ModelBuilderService { private static GradleDependencyScope deduceScope(String configurationName, Map<String, Map<String, Collection<Configuration>>> userScopes) { GradleDependencyScope scope = GradleDependencyScope.fromName(configurationName); - if (scope == null) { - for (Map.Entry<String, Map<String, Collection<Configuration>>> entry : userScopes.entrySet()) { - Collection<Configuration> plusConfigurations = entry.getValue().get("plus"); - if (plusConfigurations == null) continue; - - for (Configuration plus : plusConfigurations) { - if (plus.getName().equals(configurationName)) { - return GradleDependencyScope.fromIdeaMappingName(entry.getKey()); - } + for (Map.Entry<String, Map<String, Collection<Configuration>>> entry : userScopes.entrySet()) { + Collection<Configuration> plusConfigurations = entry.getValue().get("plus"); + if (plusConfigurations == null) continue; + + for (Configuration plus : plusConfigurations) { + if (plus.getName().equals(configurationName)) { + return GradleDependencyScope.fromIdeaMappingName(entry.getKey().toLowerCase()); } } } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/model/builder/ModuleExtendedModelBuilderImpl.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/builder/ModuleExtendedModelBuilderImpl.java index 3dc854020c6c..44eb6cfe328b 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/model/builder/ModuleExtendedModelBuilderImpl.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/builder/ModuleExtendedModelBuilderImpl.java @@ -15,6 +15,7 @@ */ package org.jetbrains.plugins.gradle.model.builder; +import groovy.lang.GroovyObject; import org.gradle.api.Project; import org.gradle.api.Task; import org.gradle.api.tasks.SourceSet; @@ -69,10 +70,17 @@ public class ModuleExtendedModelBuilderImpl implements ModelBuilderService { moduleVersionModel.setArtifacts(artifacts); + final Set<String> sourceDirectories = new HashSet<String>(); final Set<String> testDirectories = new HashSet<String>(); + final Set<String> resourceDirectories = new HashSet<String>(); + final Set<String> testResourceDirectories = new HashSet<String>(); + + final List<File> testClassesDirs = new ArrayList<File>(); for (Task task : project.getTasks()) { if (task instanceof Test) { Test test = (Test)task; + testClassesDirs.add(test.getTestClassesDir()); + if (test.hasProperty(TEST_SRC_DIRS_PROPERTY)) { Object testSrcDirs = test.property(TEST_SRC_DIRS_PROPERTY); if (testSrcDirs instanceof Iterable) { @@ -84,40 +92,58 @@ public class ModuleExtendedModelBuilderImpl implements ModelBuilderService { } } - final Set<String> javaDirectories = new HashSet<String>(); - final Set<String> resourceDirectories = new HashSet<String>(); - if (project.hasProperty(SOURCE_SETS_PROPERTY)) { Object sourceSets = project.property(SOURCE_SETS_PROPERTY); if (sourceSets instanceof SourceSetContainer) { SourceSetContainer sourceSetContainer = (SourceSetContainer)sourceSets; for (SourceSet sourceSet : sourceSetContainer) { - for (File javaSrcDir : sourceSet.getAllJava().getSrcDirs()) { - addFilePath(javaDirectories, javaSrcDir); + for (File javaSrcDir : sourceSet.getJava().getSrcDirs()) { + boolean isTestDir = isTestDir(sourceSet, testClassesDirs); + addFilePath(isTestDir ? testDirectories : sourceDirectories, javaSrcDir); } for (File resourcesSrcDir : sourceSet.getResources().getSrcDirs()) { - addFilePath(resourceDirectories, resourcesSrcDir); + boolean isTestDir = isTestDir(sourceSet, testClassesDirs); + addFilePath(isTestDir ? testResourceDirectories : resourceDirectories, resourcesSrcDir); } } } } - File projectDir = project.getProjectDir(); IdeaContentRootImpl contentRoot = new IdeaContentRootImpl(projectDir); - enrichDataFromIdeaPlugin(project, contentRoot, javaDirectories, testDirectories); + final Set<String> ideaSourceDirectories = new HashSet<String>(); + final Set<String> ideaTestDirectories = new HashSet<String>(); + final Set<String> ideaExtResourceDirectories = new HashSet<String>(); + final Set<String> ideaExtTestResourceDirectories = new HashSet<String>(); + final Set<File> excludeDirectories = new HashSet<File>(); + + enrichDataFromIdeaPlugin(project, excludeDirectories, ideaSourceDirectories, ideaTestDirectories, + ideaExtResourceDirectories, ideaExtTestResourceDirectories); - javaDirectories.removeAll(testDirectories); - javaDirectories.removeAll(resourceDirectories); + if (ideaSourceDirectories.isEmpty()) { + sourceDirectories.clear(); + resourceDirectories.clear(); + } + if (ideaTestDirectories.isEmpty()) { + testDirectories.clear(); + testResourceDirectories.clear(); + } - final Set<String> testResourceDirectories = new HashSet<String>(resourceDirectories); - testResourceDirectories.retainAll(testDirectories); + ideaSourceDirectories.removeAll(resourceDirectories); + sourceDirectories.addAll(ideaSourceDirectories); + ideaTestDirectories.removeAll(testResourceDirectories); + testDirectories.addAll(ideaTestDirectories); - testDirectories.removeAll(resourceDirectories); - resourceDirectories.removeAll(testResourceDirectories); + resourceDirectories.addAll(ideaExtResourceDirectories); + testResourceDirectories.addAll(ideaExtTestResourceDirectories); - for (String javaDir : javaDirectories) { + // ensure disjoint directories with different type + resourceDirectories.removeAll(sourceDirectories); + testDirectories.removeAll(sourceDirectories); + testResourceDirectories.removeAll(testDirectories); + + for (String javaDir : sourceDirectories) { contentRoot.addSourceDirectory(new IdeaSourceDirectoryImpl(new File(javaDir))); } for (String testDir : testDirectories) { @@ -129,11 +155,29 @@ public class ModuleExtendedModelBuilderImpl implements ModelBuilderService { for (String testResourceDir : testResourceDirectories) { contentRoot.addTestResourceDirectory(new IdeaSourceDirectoryImpl(new File(testResourceDir))); } + for (File excludeDir : excludeDirectories) { + contentRoot.addExcludeDirectory(excludeDir); + } moduleVersionModel.setContentRoots(Collections.<ExtIdeaContentRoot>singleton(contentRoot)); return moduleVersionModel; } + private static boolean isTestDir(SourceSet sourceSet, List<File> testClassesDirs) { + if (SourceSet.TEST_SOURCE_SET_NAME.equals(sourceSet.getName())) return true; + if (SourceSet.MAIN_SOURCE_SET_NAME.equals(sourceSet.getName())) return false; + + File sourceSetClassesDir = sourceSet.getOutput().getClassesDir(); + for (File testClassesDir : testClassesDirs) { + do { + if (sourceSetClassesDir.getPath().equals(testClassesDir.getPath())) return true; + } + while ((testClassesDir = testClassesDir.getParentFile()) != null); + } + + return false; + } + private static void addFilePath(Set<String> filePathSet, Object file) { if (file instanceof File) { try { @@ -145,9 +189,11 @@ public class ModuleExtendedModelBuilderImpl implements ModelBuilderService { } private static void enrichDataFromIdeaPlugin(Project project, - IdeaContentRootImpl contentRoot, + Set<File> excludeDirectories, Set<String> javaDirectories, - Set<String> testDirectories) { + Set<String> testDirectories, + Set<String> ideaExtResourceDirectories, + Set<String> ideaExtTestResourceDirectories) { IdeaPlugin ideaPlugin = project.getPlugins().getPlugin(IdeaPlugin.class); if (ideaPlugin == null) return; @@ -156,7 +202,7 @@ public class ModuleExtendedModelBuilderImpl implements ModelBuilderService { if (ideaModel == null || ideaModel.getModule() == null) return; for (File excludeDir : ideaModel.getModule().getExcludeDirs()) { - contentRoot.addExcludeDirectory(excludeDir); + excludeDirectories.add(excludeDir); } for (File file : ideaModel.getModule().getSourceDirs()) { javaDirectories.add(file.getPath()); @@ -164,5 +210,25 @@ public class ModuleExtendedModelBuilderImpl implements ModelBuilderService { for (File file : ideaModel.getModule().getTestSourceDirs()) { testDirectories.add(file.getPath()); } + + ideaExtResourceDirectories.addAll(getExtDirs("resourceDirs", ideaModel.getModule())); + ideaExtTestResourceDirectories.addAll(getExtDirs("testResourceDirs", ideaModel.getModule())); + } + + private static List<String> getExtDirs(String propertyName, GroovyObject ideaModule) { + List<String> directories = new ArrayList<String>(); + Object resourceDirs = ideaModule.getProperty(propertyName); + if (resourceDirs instanceof Iterable) { + for (Object o : Iterable.class.cast(resourceDirs)) { + if (o instanceof File) { + directories.add(File.class.cast(o).getPath()); + } + else if (o instanceof String) { + directories.add((String)o); + } + } + } + + return directories; } } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/BuildScriptClasspathData.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/BuildScriptClasspathData.java new file mode 100644 index 000000000000..cc252a9750a0 --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/BuildScriptClasspathData.java @@ -0,0 +1,49 @@ +/* + * 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 org.jetbrains.plugins.gradle.model.data; + +import com.intellij.openapi.externalSystem.model.Key; +import com.intellij.openapi.externalSystem.model.ProjectKeys; +import com.intellij.openapi.externalSystem.model.ProjectSystemId; +import com.intellij.openapi.externalSystem.model.project.AbstractExternalEntityData; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * @author Vladislav.Soroka + * @since 12/20/13 + */ +public class BuildScriptClasspathData extends AbstractExternalEntityData { + private static final long serialVersionUID = 1L; + @NotNull + public static final Key<BuildScriptClasspathData> KEY = + Key.create(BuildScriptClasspathData.class, ProjectKeys.LIBRARY_DEPENDENCY.getProcessingWeight() + 1); + + @NotNull + private final List<ClasspathEntry> myClasspathEntries; + + + public BuildScriptClasspathData(@NotNull ProjectSystemId owner, @NotNull List<ClasspathEntry> classpathEntries) { + super(owner); + myClasspathEntries = classpathEntries; + } + + @NotNull + public List<ClasspathEntry> getClasspathEntries() { + return myClasspathEntries; + } +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/ClasspathEntry.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/ClasspathEntry.java new file mode 100644 index 000000000000..fa0959ff74ad --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/data/ClasspathEntry.java @@ -0,0 +1,84 @@ +/* + * 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 org.jetbrains.plugins.gradle.model.data; + +import com.intellij.openapi.util.io.FileUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.Serializable; + +/** + * @author Vladislav.Soroka + * @since 12/23/13 + */ +public class ClasspathEntry implements Serializable { + + private static final long serialVersionUID = 1L; + + @NotNull + private final File myClassesFile; + + @Nullable + private final File mySourcesFile; + + @Nullable + private final File myJavadocFile; + + public ClasspathEntry(@NotNull File classesFile, @Nullable File sourcesFile, @Nullable File javadocFile) { + myClassesFile = classesFile; + mySourcesFile = sourcesFile; + myJavadocFile = javadocFile; + } + + @NotNull + public File getClassesFile() { + return myClassesFile; + } + + @Nullable + public File getSourcesFile() { + return mySourcesFile; + } + + @Nullable + public File getJavadocFile() { + return myJavadocFile; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ClasspathEntry)) return false; + + ClasspathEntry entry = (ClasspathEntry)o; + + if (!FileUtil.filesEqual(myClassesFile, entry.myClassesFile)) return false; + if (!FileUtil.filesEqual(myJavadocFile, entry.myJavadocFile)) return false; + if (!FileUtil.filesEqual(mySourcesFile, entry.mySourcesFile)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = FileUtil.fileHashCode(myClassesFile); + result = 31 * result + FileUtil.fileHashCode(mySourcesFile); + result = 31 * result + FileUtil.fileHashCode(myJavadocFile); + return result; + } +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/model/internal/BuildScriptClasspathModelImpl.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/internal/BuildScriptClasspathModelImpl.java new file mode 100644 index 000000000000..b59241181110 --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/internal/BuildScriptClasspathModelImpl.java @@ -0,0 +1,47 @@ +/* + * 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 org.jetbrains.plugins.gradle.model.internal; + +import org.gradle.tooling.model.DomainObjectSet; +import org.gradle.tooling.model.internal.ImmutableDomainObjectSet; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.gradle.model.BuildScriptClasspathModel; +import org.jetbrains.plugins.gradle.model.ClasspathEntryModel; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Vladislav.Soroka + * @since 12/20/13 + */ +public class BuildScriptClasspathModelImpl implements BuildScriptClasspathModel { + + private final List<ClasspathEntryModel> myClasspathEntries; + + public BuildScriptClasspathModelImpl() { + myClasspathEntries = new ArrayList<ClasspathEntryModel>(); + } + + @Override + public DomainObjectSet<? extends ClasspathEntryModel> getClasspath() { + return ImmutableDomainObjectSet.of(myClasspathEntries); + } + + public void add(@NotNull ClasspathEntryModel classpathEntryModel) { + myClasspathEntries.add(classpathEntryModel); + } +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/model/internal/ClasspathEntryModelImpl.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/internal/ClasspathEntryModelImpl.java new file mode 100644 index 000000000000..06188100d4ab --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/internal/ClasspathEntryModelImpl.java @@ -0,0 +1,60 @@ +/* + * 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 org.jetbrains.plugins.gradle.model.internal; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.gradle.model.ClasspathEntryModel; + +import java.io.File; +import java.io.Serializable; + +/** + * @author Vladislav.Soroka + * @since 12/20/13 + */ +public class ClasspathEntryModelImpl implements ClasspathEntryModel, Serializable { + @NotNull + private final File classesFile; + @Nullable + private final File sourcesFile; + @Nullable + private final File javadocFile; + + public ClasspathEntryModelImpl(@NotNull File classesFile, @Nullable File sourcesFile, @Nullable File javadocFile) { + this.classesFile = classesFile; + this.sourcesFile = sourcesFile; + this.javadocFile = javadocFile; + } + + @NotNull + @Override + public File getClassesFile() { + return classesFile; + } + + @Nullable + @Override + public File getSourcesFile() { + return sourcesFile; + } + + @Nullable + @Override + public File getJavadocFile() { + return javadocFile; + } +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/model/internal/buildSrcInit.gradle b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/internal/buildSrcInit.gradle new file mode 100644 index 000000000000..91140f2fcec6 --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/internal/buildSrcInit.gradle @@ -0,0 +1,25 @@ +/* + * 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. + */ + +allprojects { project -> + if(project.name == 'buildSrc') { + apply plugin: 'groovy' + dependencies { + compile gradleApi() + compile localGroovy() + } + } +}
\ No newline at end of file diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/model/internal/init.gradle b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/internal/init.gradle index a788cb85d83d..4f382fd42590 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/model/internal/init.gradle +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/model/internal/init.gradle @@ -34,6 +34,10 @@ try { URLClassLoader classLoader = new URLClassLoader(urls, getClass().classLoader) Class modelClass = classLoader.loadClass('org.jetbrains.plugins.gradle.model.internal.ExtraModelBuilder') allprojects { + apply plugin: 'idea' + idea.module.ext.set('resourceDirs', []) + idea.module.ext.set('testResourceDirs', []) + ext.jetExtraModelBuilderClass = modelClass apply plugin: JetGradlePlugin } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/GradleBuildClasspathManager.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/GradleBuildClasspathManager.java new file mode 100644 index 000000000000..512bd70ecfe0 --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/GradleBuildClasspathManager.java @@ -0,0 +1,111 @@ +/* + * 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 org.jetbrains.plugins.gradle.service; + +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.externalSystem.ExternalSystemManager; +import com.intellij.openapi.externalSystem.model.project.ExternalModuleBuildClasspathPojo; +import com.intellij.openapi.externalSystem.model.project.ExternalProjectBuildClasspathPojo; +import com.intellij.openapi.externalSystem.settings.AbstractExternalSystemLocalSettings; +import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.JarFileSystem; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.gradle.util.GradleConstants; + +import java.util.*; +import java.util.concurrent.atomic.AtomicReference; + +/** + * @author Vladislav.Soroka + * @since 12/27/13 + */ +public class GradleBuildClasspathManager { + @NotNull + private final Project myProject; + + @NotNull + private volatile List<VirtualFile> allFilesCache; + + @NotNull + private final AtomicReference<Map<String/*module path*/, List<VirtualFile> /*module build classpath*/>> myClasspathMap + = new AtomicReference<Map<String, List<VirtualFile>>>(new HashMap<String, List<VirtualFile>>()); + + public GradleBuildClasspathManager(@NotNull Project project) { + myProject = project; + reload(); + } + + @NotNull + public static GradleBuildClasspathManager getInstance(@NotNull Project project) { + return ServiceManager.getService(project, GradleBuildClasspathManager.class); + } + + public void reload() { + ExternalSystemManager<?, ?, ?, ?, ?> manager = ExternalSystemApiUtil.getManager(GradleConstants.SYSTEM_ID); + assert manager != null; + AbstractExternalSystemLocalSettings localSettings = manager.getLocalSettingsProvider().fun(myProject); + + Map<String/*module path*/, List<VirtualFile> /*module build classpath*/> map = ContainerUtil.newHashMap(); + + final LocalFileSystem localFileSystem = LocalFileSystem.getInstance(); + final JarFileSystem jarFileSystem = JarFileSystem.getInstance(); + for (ExternalProjectBuildClasspathPojo projectBuildClasspathPojo : localSettings.getProjectBuildClasspath().values()) { + List<VirtualFile> projectBuildClasspath = ContainerUtil.newArrayList(); + for (String path : projectBuildClasspathPojo.getProjectBuildClasspath()) { + final VirtualFile virtualFile = localFileSystem.refreshAndFindFileByPath(path); + if (virtualFile != null) { + ContainerUtil.addIfNotNull( + projectBuildClasspath, virtualFile.isDirectory() ? virtualFile : jarFileSystem.getJarRootForLocalFile(virtualFile)); + } + } + + for (ExternalModuleBuildClasspathPojo moduleBuildClasspathPojo : projectBuildClasspathPojo.getModulesBuildClasspath().values()) { + List<VirtualFile> moduleBuildClasspath = ContainerUtil.newArrayList(projectBuildClasspath); + for (String path : moduleBuildClasspathPojo.getEntries()) { + final VirtualFile virtualFile = localFileSystem.refreshAndFindFileByPath(path); + if (virtualFile != null) { + ContainerUtil.addIfNotNull(moduleBuildClasspath, jarFileSystem.getJarRootForLocalFile(virtualFile)); + } + } + + map.put(moduleBuildClasspathPojo.getPath(), moduleBuildClasspath); + } + } + + myClasspathMap.set(map); + + Set<VirtualFile> set = new LinkedHashSet<VirtualFile>(); + for (List<VirtualFile> virtualFiles : myClasspathMap.get().values()) { + set.addAll(virtualFiles); + } + allFilesCache = ContainerUtil.newArrayList(set); + } + + @NotNull + public List<VirtualFile> getAllClasspathEntries() { + return allFilesCache; + } + + @NotNull + public List<VirtualFile> getModuleClasspathEntries(@NotNull String externalModulePath) { + List<VirtualFile> virtualFiles = myClasspathMap.get().get(externalModulePath); + return virtualFiles == null ? Collections.<VirtualFile>emptyList() : virtualFiles; + } +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/GradleInstallationManager.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/GradleInstallationManager.java index 715db5a94015..95fdb0a7f244 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/GradleInstallationManager.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/GradleInstallationManager.java @@ -10,6 +10,7 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.JarFileSystem; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.ContainerUtilRt; import org.gradle.StartParameter; @@ -49,6 +50,9 @@ public class GradleInstallationManager { public static final Pattern GRADLE_JAR_FILE_PATTERN; public static final Pattern ANY_GRADLE_JAR_FILE_PATTERN; + public static final Pattern ANT_JAR_PATTERN = Pattern.compile("ant(-(.*))?\\.jar"); + public static final Pattern IVY_JAR_PATTERN = Pattern.compile("ivy(-(.*))?\\.jar"); + private static final String[] GRADLE_START_FILE_NAMES; @NonNls private static final String GRADLE_ENV_PROPERTY_NAME; @@ -375,57 +379,83 @@ public class GradleInstallationManager { */ @Nullable public List<VirtualFile> getClassRoots(@Nullable Project project) { + List<File> files = getClassRoots(project, null); + if(files == null) return null; + final LocalFileSystem localFileSystem = LocalFileSystem.getInstance(); + final JarFileSystem jarFileSystem = JarFileSystem.getInstance(); + return ContainerUtil.mapNotNull(files, new Function<File, VirtualFile>() { + @Override + public VirtualFile fun(File file) { + final VirtualFile virtualFile = localFileSystem.refreshAndFindFileByIoFile(file); + return virtualFile != null ? jarFileSystem.getJarRootForLocalFile(virtualFile) : null; + } + }); + } + + @Nullable + public List<File> getClassRoots(@Nullable Project project, @Nullable String rootProjectPath) { if (project == null) return null; - for (Module module : myPlatformFacade.getModules(project)) { - String rootProjectPath = module.getOptionValue(ExternalSystemConstants.ROOT_PROJECT_PATH_KEY); - if (StringUtil.isEmpty(rootProjectPath)) { - continue; + if(rootProjectPath == null) { + for (Module module : myPlatformFacade.getModules(project)) { + rootProjectPath = module.getOptionValue(ExternalSystemConstants.ROOT_PROJECT_PATH_KEY); + List<File> result = findGradleSdkClasspath(project, rootProjectPath); + if(!result.isEmpty()) return result; } - File gradleHome = getGradleHome(module.getProject(), rootProjectPath); + } else { + return findGradleSdkClasspath(project, rootProjectPath); + } - if (gradleHome == null || !gradleHome.isDirectory()) { - continue; - } + return null; + } - final Collection<File> libraries = getAllLibraries(gradleHome); - if (libraries == null) { - continue; - } - final LocalFileSystem localFileSystem = LocalFileSystem.getInstance(); - final JarFileSystem jarFileSystem = JarFileSystem.getInstance(); - List<VirtualFile> result = new ArrayList<VirtualFile>(); - for (File file : libraries) { - if (ANY_GRADLE_JAR_FILE_PATTERN.matcher(file.getName()).matches() || GroovyConfigUtils.matchesGroovyAll(file.getName())) { - final VirtualFile virtualFile = localFileSystem.refreshAndFindFileByIoFile(file); - if (virtualFile != null) { - ContainerUtil.addIfNotNull(result, jarFileSystem.getJarRootForLocalFile(virtualFile)); - } - } - } + private List<File> findGradleSdkClasspath(Project project, String rootProjectPath) { + List<File> result = new ArrayList<File>(); - File src = new File(gradleHome, "src"); - if (src.isDirectory()) { - if(new File(src, "org").isDirectory()) { - addRoots(localFileSystem, result, src); - } else { - addRoots(localFileSystem, result, src.listFiles()); - } - } + if (StringUtil.isEmpty(rootProjectPath)) return result; + + File gradleHome = getGradleHome(project, rootProjectPath); + if (gradleHome == null || !gradleHome.isDirectory()) { return result; } - return null; + + final Collection<File> libraries = getAllLibraries(gradleHome); + if (libraries == null) { + return result; + } + + for (File file : libraries) { + if (isGradleBuildClasspathLibrary(file)) { + ContainerUtil.addIfNotNull(result, file); + } + } + + File src = new File(gradleHome, "src"); + if (src.isDirectory()) { + if(new File(src, "org").isDirectory()) { + addRoots(result, src); + } else { + addRoots(result, src.listFiles()); + } + } + + return result; + } + + private boolean isGradleBuildClasspathLibrary(File file) { + String fileName = file.getName(); + return ANY_GRADLE_JAR_FILE_PATTERN.matcher(fileName).matches() + || ANT_JAR_PATTERN.matcher(fileName).matches() + || IVY_JAR_PATTERN.matcher(fileName).matches() + || GroovyConfigUtils.matchesGroovyAll(fileName); } - private void addRoots(@NotNull LocalFileSystem localFileSystem, @NotNull List<VirtualFile> result, @Nullable File... files) { - if(files == null) return; + private void addRoots(@NotNull List<File> result, @Nullable File... files) { + if (files == null) return; for (File file : files) { - if(file == null || !file.isDirectory()) continue; - final VirtualFile virtualFile = localFileSystem.refreshAndFindFileByIoFile(file); - if (virtualFile != null) { - result.add(0, virtualFile); - } + if (file == null || !file.isDirectory()) continue; + result.add(0, file); } } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java index 0a28fccd3347..b2eb6a0e089e 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java @@ -35,8 +35,10 @@ import com.intellij.openapi.module.StdModuleTypes; import com.intellij.openapi.roots.DependencyScope; import com.intellij.openapi.util.KeyValue; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.pom.java.LanguageLevel; import com.intellij.util.BooleanFunction; +import com.intellij.util.Function; import com.intellij.util.PathUtil; import com.intellij.util.PathsList; import com.intellij.util.containers.ContainerUtil; @@ -51,9 +53,9 @@ import org.gradle.tooling.model.idea.*; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.plugins.gradle.model.ExtIdeaContentRoot; -import org.jetbrains.plugins.gradle.model.ModuleExtendedModel; -import org.jetbrains.plugins.gradle.model.ProjectDependenciesModel; +import org.jetbrains.plugins.gradle.model.*; +import org.jetbrains.plugins.gradle.model.data.BuildScriptClasspathData; +import org.jetbrains.plugins.gradle.model.data.ClasspathEntry; import org.jetbrains.plugins.gradle.util.GradleBundle; import org.jetbrains.plugins.gradle.util.GradleConstants; import org.jetbrains.plugins.gradle.util.GradleUtil; @@ -132,14 +134,16 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver "Creating module data ('%s') with the external config path: '%s'", gradleModule.getGradleProject().getPath(), moduleConfigPath )); } - ModuleData moduleData = new ModuleData(gradleModule.getGradleProject().getPath(), + + final String path = gradleModule.getGradleProject().getPath(); + final ModuleData moduleData = new ModuleData(StringUtil.isEmpty(path) || ":".equals(path) ? moduleName : path, GradleConstants.SYSTEM_ID, StdModuleTypes.JAVA.getId(), moduleName, moduleConfigPath, moduleConfigPath); - ModuleExtendedModel moduleExtendedModel = resolverCtx.getExtraProject(gradleModule, ModuleExtendedModel.class); + final ModuleExtendedModel moduleExtendedModel = resolverCtx.getExtraProject(gradleModule, ModuleExtendedModel.class); if (moduleExtendedModel != null) { moduleData.setGroup(moduleExtendedModel.getGroup()); moduleData.setVersion(moduleExtendedModel.getVersion()); @@ -150,6 +154,19 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver @Override public void populateModuleExtraModels(@NotNull IdeaModule gradleModule, @NotNull DataNode<ModuleData> ideModule) { + BuildScriptClasspathModel buildScriptClasspathModel = resolverCtx.getExtraProject(gradleModule, BuildScriptClasspathModel.class); + if (buildScriptClasspathModel != null) { + List<ClasspathEntry> classpathEntries = + ContainerUtil.map(buildScriptClasspathModel.getClasspath(), new Function<ClasspathEntryModel, ClasspathEntry>() { + @Override + public ClasspathEntry fun(ClasspathEntryModel model) { + return new ClasspathEntry(model.getClassesFile(), model.getSourcesFile(), model.getJavadocFile()); + } + }); + BuildScriptClasspathData buildScriptClasspathData = + new BuildScriptClasspathData(GradleConstants.SYSTEM_ID, classpathEntries); + ideModule.createChild(BuildScriptClasspathData.KEY, buildScriptClasspathData); + } } @Override @@ -289,7 +306,7 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver @NotNull @Override public Set<Class> getExtraProjectModelClasses() { - return ContainerUtil.<Class>set(ModuleExtendedModel.class, ProjectDependenciesModel.class); + return ContainerUtil.<Class>set(ModuleExtendedModel.class, ProjectDependenciesModel.class, BuildScriptClasspathModel.class); } @NotNull diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleExecutionHelper.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleExecutionHelper.java index e4ae7b389306..39dfe9afdbe5 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleExecutionHelper.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleExecutionHelper.java @@ -355,22 +355,46 @@ public class GradleExecutionHelper { } @SuppressWarnings("IOResourceOpenedButNotSafelyClosed") - public static void setInitScript(LongRunningOperation longRunningOperation) { + public static boolean setInitScript(@NotNull LongRunningOperation longRunningOperation, boolean isBuildSrcProject) { try { InputStream stream = GradleProjectResolver.class.getResourceAsStream("/org/jetbrains/plugins/gradle/model/internal/init.gradle"); - if (stream == null) return; + if (stream == null) return isBuildSrcProject; String jarPath = PathUtil.getCanonicalPath(PathUtil.getJarPathForClass(GradleProjectResolver.class)); String s = FileUtil.loadTextAndClose(stream).replace("${JAR_PATH}", jarPath); + if(isBuildSrcProject) { + String buildSrcDefaultInitScript = getBuildSrcDefaultInitScript(); + if(buildSrcDefaultInitScript == null) return false; + s += buildSrcDefaultInitScript; + } + final File tempFile = FileUtil.createTempFile("ijinit", '.' + GradleConstants.EXTENSION, true); FileUtil.writeToFile(tempFile, s); String[] buildExecutorArgs = new String[]{"--init-script", tempFile.getAbsolutePath()}; longRunningOperation.withArguments(buildExecutorArgs); + + return true; + } + catch (Exception e) { + LOG.warn("Can't use IJ gradle init script", e); + return false; + } + } + + @Nullable + @SuppressWarnings("IOResourceOpenedButNotSafelyClosed") + public static String getBuildSrcDefaultInitScript() { + try { + InputStream stream = GradleProjectResolver.class.getResourceAsStream("/org/jetbrains/plugins/gradle/model/internal/buildSrcInit.gradle"); + if (stream == null) return null; + + return FileUtil.loadTextAndClose(stream); } catch (Exception e) { LOG.warn("Can't use IJ gradle init script", e); + return null; } } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolver.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolver.java index 1b1997b048a2..29a27ba94c0f 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolver.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolver.java @@ -32,6 +32,7 @@ import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil; import com.intellij.openapi.externalSystem.util.ExternalSystemDebugEnvironment; import com.intellij.openapi.util.KeyValue; import com.intellij.openapi.util.Pair; +import com.intellij.util.BooleanFunction; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.ContainerUtilRt; @@ -52,6 +53,7 @@ import org.jetbrains.plugins.gradle.settings.GradleExecutionSettings; import org.jetbrains.plugins.gradle.util.GradleConstants; import org.jetbrains.plugins.gradle.util.GradleEnvironment; +import java.io.File; import java.util.*; /** @@ -110,19 +112,19 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver<Grad projectResolverChain = new BaseGradleProjectResolverExtension(); } - return myHelper.execute(projectPath, settings, new Function<ProjectConnection, DataNode<ProjectData>>() { - @Override - public DataNode<ProjectData> fun(ProjectConnection connection) { - try { - return doResolveProjectInfo( - new ProjectResolverContext(id, projectPath, settings, connection, listener, isPreviewMode), projectResolverChain); - } - catch (RuntimeException e) { - LOG.info("Gradle project resolve error", e); - throw projectResolverChain.getUserFriendlyError(e, projectPath, null); - } - } - }); + final DataNode<ProjectData> resultProjectDataNode = myHelper.execute( + projectPath, settings, + new ProjectConnectionDataNodeFunction( + id, projectPath, settings, listener, isPreviewMode, projectResolverChain, false) + ); + + // auto-discover buildSrc project if needed + final String buildSrcProjectPath = projectPath + "/buildSrc"; + handleBuildSrcProject( + resultProjectDataNode, + new ProjectConnectionDataNodeFunction(id, buildSrcProjectPath, settings, listener, isPreviewMode, projectResolverChain, true) + ); + return resultProjectDataNode; } @Override @@ -133,7 +135,8 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver<Grad @NotNull private DataNode<ProjectData> doResolveProjectInfo(@NotNull final ProjectResolverContext resolverCtx, - @NotNull final GradleProjectResolverExtension projectResolverChain) + @NotNull final GradleProjectResolverExtension projectResolverChain, + boolean isBuildSrcProject) throws IllegalArgumentException, IllegalStateException { final ProjectImportAction projectImportAction = new ProjectImportAction(resolverCtx.isPreviewMode()); @@ -162,7 +165,7 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver<Grad // TODO [vlad] remove the check if (!GradleEnvironment.DISABLE_ENHANCED_TOOLING_API) { - GradleExecutionHelper.setInitScript(buildActionExecutor); + GradleExecutionHelper.setInitScript(buildActionExecutor, isBuildSrcProject); } ProjectImportAction.AllModels allModels; @@ -242,8 +245,10 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver<Grad projectResolverChain.populateModuleContentRoots(ideaModule, moduleDataNode); projectResolverChain.populateModuleCompileOutputSettings(ideaModule, moduleDataNode); projectResolverChain.populateModuleDependencies(ideaModule, moduleDataNode, projectDataNode); - final Collection<TaskData> moduleTasks = projectResolverChain.populateModuleTasks(ideaModule, moduleDataNode, projectDataNode); - allTasks.addAll(moduleTasks); + if(!isBuildSrcProject) { + final Collection<TaskData> moduleTasks = projectResolverChain.populateModuleTasks(ideaModule, moduleDataNode, projectDataNode); + allTasks.addAll(moduleTasks); + } } // populate root project tasks @@ -275,4 +280,78 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver<Grad return null; } } + + private void handleBuildSrcProject(@NotNull final DataNode<ProjectData> resultProjectDataNode, + @NotNull final ProjectConnectionDataNodeFunction projectConnectionDataNodeFunction) { + + if (projectConnectionDataNodeFunction.myIsPreviewMode || GradleEnvironment.DISABLE_ENHANCED_TOOLING_API || + !new File(projectConnectionDataNodeFunction.myProjectPath).isDirectory()) return; + + final DataNode<ModuleData> buildSrcModuleDataNode = + ExternalSystemApiUtil.find(resultProjectDataNode, ProjectKeys.MODULE, new BooleanFunction<DataNode<ModuleData>>() { + @Override + public boolean fun(DataNode<ModuleData> node) { + return projectConnectionDataNodeFunction.myProjectPath.equals(node.getData().getLinkedExternalProjectPath()); + } + }); + + // check if buildSrc project was already exposed in settings.gradle file + if (buildSrcModuleDataNode != null) return; + + final DataNode<ProjectData> buildSrcProjectDataDataNode = myHelper.execute( + projectConnectionDataNodeFunction.myProjectPath, projectConnectionDataNodeFunction.mySettings, projectConnectionDataNodeFunction); + + if (buildSrcProjectDataDataNode != null) { + final DataNode<ModuleData> moduleDataNode = ExternalSystemApiUtil.find(buildSrcProjectDataDataNode, ProjectKeys.MODULE); + if (moduleDataNode != null) { + for (DataNode<LibraryData> libraryDataNode : ExternalSystemApiUtil.findAll(buildSrcProjectDataDataNode, ProjectKeys.LIBRARY)) { + resultProjectDataNode.createChild(libraryDataNode.getKey(), libraryDataNode.getData()); + } + + final DataNode<ModuleData> newModuleDataNode = resultProjectDataNode.createChild(ProjectKeys.MODULE, moduleDataNode.getData()); + for (DataNode node : moduleDataNode.getChildren()) { + newModuleDataNode.createChild(node.getKey(), node.getData()); + } + } + } + } + + private class ProjectConnectionDataNodeFunction implements Function<ProjectConnection, DataNode<ProjectData>> { + @NotNull private final ExternalSystemTaskId myId; + @NotNull private final String myProjectPath; + @Nullable private final GradleExecutionSettings mySettings; + @NotNull private final ExternalSystemTaskNotificationListener myListener; + private final boolean myIsPreviewMode; + @NotNull private final GradleProjectResolverExtension myProjectResolverChain; + private final boolean myIsBuildSrcProject; + + public ProjectConnectionDataNodeFunction(@NotNull ExternalSystemTaskId id, + @NotNull String projectPath, + @Nullable GradleExecutionSettings settings, + @NotNull ExternalSystemTaskNotificationListener listener, + boolean isPreviewMode, + @NotNull GradleProjectResolverExtension projectResolverChain, + boolean isBuildSrcProject) { + myId = id; + myProjectPath = projectPath; + mySettings = settings; + myListener = listener; + myIsPreviewMode = isPreviewMode; + myProjectResolverChain = projectResolverChain; + myIsBuildSrcProject = isBuildSrcProject; + } + + @Override + public DataNode<ProjectData> fun(ProjectConnection connection) { + try { + return doResolveProjectInfo( + new ProjectResolverContext(myId, myProjectPath, mySettings, connection, myListener, myIsPreviewMode), + myProjectResolverChain, myIsBuildSrcProject); + } + catch (RuntimeException e) { + LOG.info("Gradle project resolve error", e); + throw myProjectResolverChain.getUserFriendlyError(e, myProjectPath, null); + } + } + } } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleStartupActivity.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleStartupActivity.java index 623a79736028..fc4805e81058 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleStartupActivity.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleStartupActivity.java @@ -30,6 +30,7 @@ import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VfsUtilCore; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.gradle.service.GradleBuildClasspathManager; import org.jetbrains.plugins.gradle.service.project.wizard.GradleProjectImportBuilder; import org.jetbrains.plugins.gradle.service.project.wizard.GradleProjectImportProvider; import org.jetbrains.plugins.gradle.settings.GradleSettings; @@ -52,9 +53,14 @@ public class GradleStartupActivity implements StartupActivity { @Override public void runActivity(@NotNull Project project) { + configureBuildClasspath(project); showNotificationForUnlinkedGradleProject(project); } + private static void configureBuildClasspath(@NotNull final Project project) { + GradleBuildClasspathManager.getInstance(project).reload(); + } + private static void showNotificationForUnlinkedGradleProject(@NotNull final Project project) { if (!PropertiesComponent.getInstance(project).getBoolean(SHOW_UNLINKED_GRADLE_POPUP, true) || !GradleSettings.getInstance(project).getLinkedProjectsSettings().isEmpty() diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/data/BuildClasspathModuleGradleDataService.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/data/BuildClasspathModuleGradleDataService.java new file mode 100644 index 000000000000..907f2903ef09 --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/data/BuildClasspathModuleGradleDataService.java @@ -0,0 +1,161 @@ +/* + * 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 org.jetbrains.plugins.gradle.service.project.data; + +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.externalSystem.ExternalSystemManager; +import com.intellij.openapi.externalSystem.model.DataNode; +import com.intellij.openapi.externalSystem.model.Key; +import com.intellij.openapi.externalSystem.model.ProjectKeys; +import com.intellij.openapi.externalSystem.model.project.ExternalModuleBuildClasspathPojo; +import com.intellij.openapi.externalSystem.model.project.ExternalProjectBuildClasspathPojo; +import com.intellij.openapi.externalSystem.model.project.ModuleData; +import com.intellij.openapi.externalSystem.model.project.ProjectData; +import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataService; +import com.intellij.openapi.externalSystem.settings.AbstractExternalSystemLocalSettings; +import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil; +import com.intellij.openapi.externalSystem.util.ExternalSystemConstants; +import com.intellij.openapi.externalSystem.util.Order; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.FactoryMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.gradle.model.data.BuildScriptClasspathData; +import org.jetbrains.plugins.gradle.model.data.ClasspathEntry; +import org.jetbrains.plugins.gradle.service.GradleBuildClasspathManager; +import org.jetbrains.plugins.gradle.service.GradleInstallationManager; +import org.jetbrains.plugins.gradle.settings.GradleProjectSettings; +import org.jetbrains.plugins.gradle.settings.GradleSettings; +import org.jetbrains.plugins.gradle.util.GradleConstants; + +import java.io.File; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * @author Vladislav.Soroka + * @since 8/27/13 + */ +@Order(ExternalSystemConstants.UNORDERED) +public class BuildClasspathModuleGradleDataService implements ProjectDataService<BuildScriptClasspathData, Module> { + + @NotNull + @Override + public Key<BuildScriptClasspathData> getTargetDataKey() { + return BuildScriptClasspathData.KEY; + } + + @Override + public void importData(@NotNull final Collection<DataNode<BuildScriptClasspathData>> toImport, + @NotNull final Project project, + boolean synchronous) { + if (toImport.isEmpty()) { + return; + } + if (!project.isInitialized()) { + return; + } + + final GradleInstallationManager gradleInstallationManager = ServiceManager.getService(GradleInstallationManager.class); + + ExternalSystemManager<?, ?, ?, ?, ?> manager = ExternalSystemApiUtil.getManager(GradleConstants.SYSTEM_ID); + assert manager != null; + AbstractExternalSystemLocalSettings localSettings = manager.getLocalSettingsProvider().fun(project); + + //noinspection MismatchedQueryAndUpdateOfCollection + Map<String/* externalProjectPath */, Set<String>> externalProjectGradleSdkLibs = new FactoryMap<String, Set<String>>() { + @Nullable + @Override + protected Set<String> create(String externalProjectPath) { + GradleProjectSettings settings = GradleSettings.getInstance(project).getLinkedProjectSettings(externalProjectPath); + if (settings == null || settings.getDistributionType() == null) return null; + + final Set<String> gradleSdkLibraries = ContainerUtil.newHashSet(); + File gradleHome = + gradleInstallationManager.getGradleHome(settings.getDistributionType(), externalProjectPath, settings.getGradleHome()); + if (gradleHome != null && gradleHome.isDirectory()) { + + final Collection<File> libraries = gradleInstallationManager.getClassRoots(project, externalProjectPath); + if (libraries != null) { + for (File library : libraries) { + gradleSdkLibraries.add(FileUtil.toCanonicalPath(library.getPath())); + } + } + } + return gradleSdkLibraries; + } + }; + + for (final DataNode<BuildScriptClasspathData> node : toImport) { + if (GradleConstants.SYSTEM_ID.equals(node.getData().getOwner())) { + + + DataNode<ProjectData> projectDataNode = ExternalSystemApiUtil.findParent(node, ProjectKeys.PROJECT); + assert projectDataNode != null; + + String linkedExternalProjectPath = projectDataNode.getData().getLinkedExternalProjectPath(); + DataNode<ModuleData> moduleDataNode = ExternalSystemApiUtil.findParent(node, ProjectKeys.MODULE); + if (moduleDataNode == null) continue; + + String externalModulePath = moduleDataNode.getData().getLinkedExternalProjectPath(); + GradleProjectSettings settings = GradleSettings.getInstance(project).getLinkedProjectSettings(linkedExternalProjectPath); + if (settings == null || settings.getDistributionType() == null) continue; + + final Set<String> buildClasspath = ContainerUtil.newHashSet(); + BuildScriptClasspathData buildScriptClasspathData = node.getData(); + for (ClasspathEntry classpathEntry : buildScriptClasspathData.getClasspathEntries()) { + if (classpathEntry.getSourcesFile() != null) { + buildClasspath.add(FileUtil.toCanonicalPath(classpathEntry.getSourcesFile().getPath())); + } + else { + buildClasspath.add(FileUtil.toCanonicalPath(classpathEntry.getClassesFile().getPath())); + } + } + + ExternalProjectBuildClasspathPojo projectBuildClasspathPojo = + localSettings.getProjectBuildClasspath().get(linkedExternalProjectPath); + if (projectBuildClasspathPojo == null) { + projectBuildClasspathPojo = new ExternalProjectBuildClasspathPojo( + moduleDataNode.getData().getExternalName(), + ContainerUtil.<String>newArrayList(), + ContainerUtil.<String, ExternalModuleBuildClasspathPojo>newHashMap()); + localSettings.getProjectBuildClasspath().put(linkedExternalProjectPath, projectBuildClasspathPojo); + } + + List<String> projectBuildClasspath = ContainerUtil.newArrayList(externalProjectGradleSdkLibs.get(linkedExternalProjectPath)); + // add main java root of buildSrc project + projectBuildClasspath.add(linkedExternalProjectPath + "/buildSrc/src/main/java"); + // add main groovy root of buildSrc project + projectBuildClasspath.add(linkedExternalProjectPath + "/buildSrc/src/main/groovy"); + + projectBuildClasspathPojo.setProjectBuildClasspath(projectBuildClasspath); + projectBuildClasspathPojo.getModulesBuildClasspath().put( + externalModulePath, new ExternalModuleBuildClasspathPojo(externalModulePath, ContainerUtil.newArrayList(buildClasspath))); + } + } + + GradleBuildClasspathManager.getInstance(project).reload(); + } + + @Override + public void removeData(@NotNull Collection<? extends Module> toRemove, @NotNull Project project, boolean synchronous) { + } +} diff --git a/plugins/gradle/testData/testAddMavenDependencyInEmptyFile.gradle b/plugins/gradle/testData/testAddMavenDependencyInEmptyFile.gradle index f90814d636ed..e69de29bb2d1 100644 --- a/plugins/gradle/testData/testAddMavenDependencyInEmptyFile.gradle +++ b/plugins/gradle/testData/testAddMavenDependencyInEmptyFile.gradle @@ -1 +0,0 @@ -//empty file
\ No newline at end of file diff --git a/plugins/gradle/testData/testAddMavenDependencyInEmptyFile_after.gradle b/plugins/gradle/testData/testAddMavenDependencyInEmptyFile_after.gradle index aa2892d73dc4..54c09742fd5c 100644 --- a/plugins/gradle/testData/testAddMavenDependencyInEmptyFile_after.gradle +++ b/plugins/gradle/testData/testAddMavenDependencyInEmptyFile_after.gradle @@ -1,4 +1,3 @@ -//empty file dependencies{ compile 'testGroupId:testArtifactId:1.0' }
\ No newline at end of file diff --git a/plugins/gradle/testData/testDefaultDependenciesModel/build.gradle b/plugins/gradle/testData/testDefaultDependenciesModel/build.gradle index 849b1b486dd2..a6c9dde56871 100644 --- a/plugins/gradle/testData/testDefaultDependenciesModel/build.gradle +++ b/plugins/gradle/testData/testDefaultDependenciesModel/build.gradle @@ -4,8 +4,8 @@ allprojects{ apply plugin: 'java' } -project(":group2:subgroup11:project") { +project("dependentProject") { dependencies { - compile project(":project") + compile project(":dependencyProject") } }
\ No newline at end of file diff --git a/plugins/gradle/testData/testDefaultDependenciesModel/settings.gradle b/plugins/gradle/testData/testDefaultDependenciesModel/settings.gradle index cf870fe6d6d1..ef853f062e1c 100644 --- a/plugins/gradle/testData/testDefaultDependenciesModel/settings.gradle +++ b/plugins/gradle/testData/testDefaultDependenciesModel/settings.gradle @@ -1,5 +1,4 @@ //noinspection GrPackage -include 'group1:subgroup11:project' -include 'group2:subgroup11:project' -include 'project'
\ No newline at end of file +include 'dependencyProject' +include 'dependentProject'
\ No newline at end of file diff --git a/plugins/gradle/testData/testGradleIdeaPluginPlusScopesDependenciesModel/build.gradle b/plugins/gradle/testData/testGradleIdeaPluginPlusScopesDependenciesModel/build.gradle index 6f9afa4b8d09..73cb6a15b5cd 100644 --- a/plugins/gradle/testData/testGradleIdeaPluginPlusScopesDependenciesModel/build.gradle +++ b/plugins/gradle/testData/testGradleIdeaPluginPlusScopesDependenciesModel/build.gradle @@ -27,7 +27,7 @@ allprojects { project(":service") { dependencies { - compile (project(':api')) + compile(project(':api')) } } @@ -35,4 +35,21 @@ project(":api") { dependencies { provided(project(':lib')) } -}
\ No newline at end of file +} + +project(":withIdeaModelCustomisations") { + dependencies { + //compile 'org.slf4j:slf4j-api:1.7.5' + //testCompile 'junit:junit:4.11' + + compile files("lib/someDep.jar") + testCompile files("lib/someTestDep.jar") + } + + idea { + module { + scopes.COMPILE.plus += scopes.TEST.plus + scopes.TEST.plus = [] + } + } +} diff --git a/plugins/gradle/testData/testGradleIdeaPluginPlusScopesDependenciesModel/settings.gradle b/plugins/gradle/testData/testGradleIdeaPluginPlusScopesDependenciesModel/settings.gradle index 271f7343d37b..26ee835a9fb1 100644 --- a/plugins/gradle/testData/testGradleIdeaPluginPlusScopesDependenciesModel/settings.gradle +++ b/plugins/gradle/testData/testGradleIdeaPluginPlusScopesDependenciesModel/settings.gradle @@ -1,4 +1,5 @@ //noinspection GrPackage include "lib" include "api" -include "service"
\ No newline at end of file +include "service" +include "withIdeaModelCustomisations"
\ No newline at end of file diff --git a/plugins/gradle/testData/testGradleSourcesSetsInterpretation/build.gradle b/plugins/gradle/testData/testGradleSourcesSetsInterpretation/build.gradle new file mode 100644 index 000000000000..8c7584942858 --- /dev/null +++ b/plugins/gradle/testData/testGradleSourcesSetsInterpretation/build.gradle @@ -0,0 +1,74 @@ +//noinspection GrPackage + +project("defaultJavaModule") { + apply plugin: "java" +} + +project("moduleWithSourceSetDirBothAsResourceAndJava") { + apply plugin: "java" + + sourceSets.main.resources.srcDir 'src/main/java' + sourceSets.test.resources.srcDir 'src/test/java' +} + +project("moduleWithCustomSourceSet") { + apply plugin: "java" + + sourceSets { + custom + } + + sourceSets.custom.java.srcDir 'src/custom/java' +} + +project("withIntegrationTests") { + apply plugin: 'java' + + sourceSets { + integrationTest { + java.srcDir file('src/integration-test/java') + resources.srcDir file('src/integration-test/resources') + } + } + + dependencies { + integrationTestCompile sourceSets.main.output + integrationTestCompile configurations.testCompile + integrationTestCompile sourceSets.test.output + integrationTestRuntime configurations.testRuntime + } + + //noinspection GrUnresolvedAccess + task integrationTest(type: Test, dependsOn: jar) { + testClassesDir = sourceSets.integrationTest.output.classesDir + classpath = sourceSets.integrationTest.runtimeClasspath + } + + check.dependsOn integrationTest +} + +project("withIdeaPluginCustomization1") { + apply plugin: 'java' + + idea { + module { + testSourceDirs += file('src/intTest/java') + testSourceDirs += file('src/intTest/resources') + excludeDirs += file('some-extra-exclude-folder') + } + } +} + +// see http://youtrack.jetbrains.com/issue/IDEA-119365 +project("withIdeaPluginCustomization2") { + apply plugin: 'java' + + idea { + module { + sourceDirs += testSourceDirs + testSourceDirs = [] + ext.resourceDirs += file('some-extra-resource-folder') + ext.testResourceDirs += file('some-extra-test-resource-folder') + } + } +} diff --git a/plugins/gradle/testData/testGradleSourcesSetsInterpretation/settings.gradle b/plugins/gradle/testData/testGradleSourcesSetsInterpretation/settings.gradle new file mode 100644 index 000000000000..4f7ab785f8fd --- /dev/null +++ b/plugins/gradle/testData/testGradleSourcesSetsInterpretation/settings.gradle @@ -0,0 +1,8 @@ +//noinspection GrPackage + +include 'defaultJavaModule' +include 'moduleWithSourceSetDirBothAsResourceAndJava' +include 'moduleWithCustomSourceSet' +include 'withIntegrationTests' +include 'withIdeaPluginCustomization1' +include 'withIdeaPluginCustomization2'
\ No newline at end of file diff --git a/plugins/gradle/testData/testModelBuildScriptClasspathBuilder/build.gradle b/plugins/gradle/testData/testModelBuildScriptClasspathBuilder/build.gradle new file mode 100644 index 000000000000..5b09fca15f4d --- /dev/null +++ b/plugins/gradle/testData/testModelBuildScriptClasspathBuilder/build.gradle @@ -0,0 +1,9 @@ +//noinspection GrPackage + +project("moduleWithAdditionalClasspath") { + buildscript { + dependencies { + classpath files("lib/someDep.jar") + } + } +} diff --git a/plugins/gradle/testData/testModelBuildScriptClasspathBuilder/settings.gradle b/plugins/gradle/testData/testModelBuildScriptClasspathBuilder/settings.gradle new file mode 100644 index 000000000000..02978875f494 --- /dev/null +++ b/plugins/gradle/testData/testModelBuildScriptClasspathBuilder/settings.gradle @@ -0,0 +1,4 @@ +//noinspection GrPackage + +include 'moduleWithoutAdditionalClasspath' +include 'moduleWithAdditionalClasspath' diff --git a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/model/builder/AbstractModelBuilderTest.java b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/model/builder/AbstractModelBuilderTest.java index 835d7305bd1b..0cdd2f032b78 100644 --- a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/model/builder/AbstractModelBuilderTest.java +++ b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/model/builder/AbstractModelBuilderTest.java @@ -73,7 +73,7 @@ public abstract class AbstractModelBuilderTest { final ProjectImportAction projectImportAction = new ProjectImportAction(true); projectImportAction.addExtraProjectModelClasses(getModels()); BuildActionExecuter<ProjectImportAction.AllModels> buildActionExecutor = connection.action(projectImportAction); - GradleExecutionHelper.setInitScript(buildActionExecutor); + GradleExecutionHelper.setInitScript(buildActionExecutor, false); allModels = buildActionExecutor.run(); assertNotNull(allModels); diff --git a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/model/builder/ModelBuildScriptClasspathBuilderImplTest.java b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/model/builder/ModelBuildScriptClasspathBuilderImplTest.java new file mode 100644 index 000000000000..d812d8df6a68 --- /dev/null +++ b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/model/builder/ModelBuildScriptClasspathBuilderImplTest.java @@ -0,0 +1,78 @@ +/* + * 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 org.jetbrains.plugins.gradle.model.builder; + +import com.intellij.util.Function; +import com.intellij.util.containers.ContainerUtil; +import org.gradle.tooling.model.DomainObjectSet; +import org.gradle.tooling.model.idea.IdeaModule; +import org.jetbrains.plugins.gradle.model.BuildScriptClasspathModel; +import org.jetbrains.plugins.gradle.model.ClasspathEntryModel; +import org.junit.Test; + +import java.util.List; +import java.util.Set; + +import static junit.framework.Assert.*; +import static org.junit.Assert.assertTrue; + +/** + * @author Vladislav.Soroka + * @since 1/16/14 + */ +public class ModelBuildScriptClasspathBuilderImplTest extends AbstractModelBuilderTest { + + @Test + public void testModelBuildScriptClasspathBuilder() throws Exception { + ModelBuildScriptClasspathBuilderImpl buildScriptClasspathBuilder = new ModelBuildScriptClasspathBuilderImpl(); + assertTrue(buildScriptClasspathBuilder.canBuild("org.jetbrains.plugins.gradle.model.BuildScriptClasspathModel")); + + DomainObjectSet<? extends IdeaModule> ideaModules = allModels.getIdeaProject().getModules(); + + List<BuildScriptClasspathModel> ideaModule = + ContainerUtil.mapNotNull(ideaModules, new Function<IdeaModule, BuildScriptClasspathModel>() { + @Override + public BuildScriptClasspathModel fun(IdeaModule module) { + BuildScriptClasspathModel classpathModel = allModels.getExtraProject(module, BuildScriptClasspathModel.class); + + if (module.getName().equals("moduleWithAdditionalClasspath")) { + assertNotNull(classpathModel); + assertEquals(1, classpathModel.getClasspath().size()); + + ClasspathEntryModel classpathEntry = classpathModel.getClasspath().getAt(0); + assertEquals("someDep.jar", classpathEntry.getClassesFile().getName()); + } + else if (module.getName().equals("moduleWithoutAdditionalClasspath") || + module.getName().equals("testModelBuildScriptClasspathBuilder")) { + assertNotNull(classpathModel); + assertTrue(classpathModel.getClasspath().isEmpty()); + } + else { + fail(); + } + + return classpathModel; + } + }); + + assertEquals(3, ideaModule.size()); + } + + @Override + protected Set<Class> getModels() { + return ContainerUtil.<Class>set(BuildScriptClasspathModel.class); + } +} diff --git a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/model/builder/ModelDependenciesBuilderImplTest.java b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/model/builder/ModelDependenciesBuilderImplTest.java index 6bb9b6f5eb9c..8cf50ef62af5 100644 --- a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/model/builder/ModelDependenciesBuilderImplTest.java +++ b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/model/builder/ModelDependenciesBuilderImplTest.java @@ -15,19 +15,26 @@ */ package org.jetbrains.plugins.gradle.model.builder; -import com.intellij.openapi.util.Condition; +import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import org.gradle.tooling.model.DomainObjectSet; import org.gradle.tooling.model.idea.IdeaDependency; import org.gradle.tooling.model.idea.IdeaModule; import org.gradle.tooling.model.idea.IdeaModuleDependency; +import org.gradle.tooling.model.idea.IdeaSingleEntryLibraryDependency; +import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.gradle.model.GradleDependencyScope; import org.jetbrains.plugins.gradle.model.ProjectDependenciesModel; +import org.junit.Assert; import org.junit.Test; +import java.util.List; import java.util.Set; -import static org.junit.Assert.*; +import static junit.framework.Assert.assertTrue; +import static junit.framework.Assert.fail; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; /** * @author Vladislav.Soroka @@ -42,17 +49,29 @@ public class ModelDependenciesBuilderImplTest extends AbstractModelBuilderTest { DomainObjectSet<? extends IdeaModule> ideaModules = allModels.getIdeaProject().getModules(); - IdeaModule ideaModule = ContainerUtil.find(ideaModules, new Condition<IdeaModule>() { - @Override - public boolean value(IdeaModule module) { - return module.getName().equals("group2-subgroup11-project"); - } - }); - - assertNotNull(ideaModule); + final int modulesSize = 3; + assertEquals(modulesSize, ideaModules.size()); - DomainObjectSet<? extends IdeaDependency> dependencies = ideaModule.getDependencies(); - assertEquals(1, dependencies.size()); + for (IdeaModule ideaModule : ideaModules) { + if (ideaModule.getName().equals("dependencyProject") || + ideaModule.getName().equals("testDefaultDependenciesModel")) { + DomainObjectSet<? extends IdeaDependency> dependencies = ideaModule.getDependencies(); + assertTrue((dependencies.isEmpty())); + } + else if (ideaModule.getName().equals("dependentProject")) { + DomainObjectSet<? extends IdeaDependency> dependencies = ideaModule.getDependencies(); + assertEquals(1, dependencies.size()); + assertTrue(dependencies.getAt(0) instanceof IdeaModuleDependency); + IdeaModuleDependency moduleDependency = (IdeaModuleDependency)dependencies.getAt(0); + + assertEquals("dependencyProject", moduleDependency.getDependencyModule().getName()); + assertEquals("COMPILE", moduleDependency.getScope().getScope()); + assertTrue(moduleDependency.getExported()); + } + else { + fail(); + } + } } @Test @@ -62,44 +81,71 @@ public class ModelDependenciesBuilderImplTest extends AbstractModelBuilderTest { DomainObjectSet<? extends IdeaModule> ideaModules = allModels.getIdeaProject().getModules(); - // test api module dependencies - IdeaModule apiModule = ContainerUtil.find(ideaModules, new Condition<IdeaModule>() { - @Override - public boolean value(IdeaModule module) { - return module.getName().equals("api"); + final int modulesSize = 5; + assertEquals(modulesSize, ideaModules.size()); + + for (IdeaModule ideaModule : ideaModules) { + DomainObjectSet<? extends IdeaDependency> dependencies = ideaModule.getDependencies(); + if (ideaModule.getName().equals("lib") || + ideaModule.getName().equals("testGradleIdeaPluginPlusScopesDependenciesModel")) { + assertTrue((dependencies.isEmpty())); } - }); - assertNotNull(apiModule); + else if (ideaModule.getName().equals("api")) { + assertEquals(1, dependencies.size()); + IdeaDependency libDependency = dependencies.getAt(0); + assertEquals(GradleDependencyScope.PROVIDED.getIdeaMappingName(), libDependency.getScope().getScope().toLowerCase()); + assertTrue(libDependency instanceof IdeaModuleDependency); + + IdeaModuleDependency libModuleDependency = (IdeaModuleDependency)libDependency; + assertNotNull(libModuleDependency.getDependencyModule()); + assertEquals("lib", libModuleDependency.getDependencyModule().getName()); + } + else if (ideaModule.getName().equals("service")) { + assertEquals(1, dependencies.size()); + IdeaDependency apiDependency = dependencies.getAt(0); + assertEquals(GradleDependencyScope.COMPILE.getIdeaMappingName(), apiDependency.getScope().getScope().toLowerCase()); + assertTrue(apiDependency instanceof IdeaModuleDependency); + + IdeaModuleDependency apiModuleDependency = (IdeaModuleDependency)apiDependency; + assertNotNull(apiModuleDependency.getDependencyModule()); + assertEquals("api", apiModuleDependency.getDependencyModule().getName()); + } + else if (ideaModule.getName().equals("withIdeaModelCustomisations")) { + + assertTrue(findLocalLibraries(dependencies, GradleDependencyScope.TEST_COMPILE.getIdeaMappingName()).isEmpty()); - DomainObjectSet<? extends IdeaDependency> dependencies = apiModule.getDependencies(); - assertEquals(1, dependencies.size()); - IdeaDependency libDependency = dependencies.getAt(0); - assertEquals(GradleDependencyScope.PROVIDED.name(), libDependency.getScope().getScope()); - assertTrue(libDependency instanceof IdeaModuleDependency); + List<IdeaSingleEntryLibraryDependency> libraryDependencies = + findLocalLibraries(dependencies, GradleDependencyScope.COMPILE.getIdeaMappingName()); + assertEquals(2, libraryDependencies.size()); - IdeaModuleDependency libModuleDependency = (IdeaModuleDependency)libDependency; - assertNotNull(libModuleDependency.getDependencyModule()); - assertEquals("lib", libModuleDependency.getDependencyModule().getName()); + IdeaSingleEntryLibraryDependency someDep = libraryDependencies.get(0); + assertEquals(GradleDependencyScope.COMPILE.getIdeaMappingName(), someDep.getScope().getScope().toLowerCase()); + assertEquals("someDep.jar", someDep.getFile().getName()); + IdeaSingleEntryLibraryDependency someTestDep = libraryDependencies.get(1); + assertEquals(GradleDependencyScope.COMPILE.getIdeaMappingName(), someTestDep.getScope().getScope().toLowerCase()); + assertEquals("someTestDep.jar", someTestDep.getFile().getName()); + } + else { + fail(); + } + } + } - // test service module dependencies - IdeaModule serviceModule = ContainerUtil.find(ideaModules, new Condition<IdeaModule>() { - @Override - public boolean value(IdeaModule module) { - return module.getName().equals("service"); + @NotNull + private static List<IdeaSingleEntryLibraryDependency> findLocalLibraries( + @NotNull final DomainObjectSet<? extends IdeaDependency> dependencies, @NotNull final String scope) { + return ContainerUtil.mapNotNull( + dependencies, + new Function<IdeaDependency, IdeaSingleEntryLibraryDependency>() { + @Override + public IdeaSingleEntryLibraryDependency fun(IdeaDependency dependency) { + return dependency instanceof IdeaSingleEntryLibraryDependency && scope.equals(dependency.getScope().getScope().toLowerCase()) + ? (IdeaSingleEntryLibraryDependency)dependency + : null; + } } - }); - assertNotNull(serviceModule); - - DomainObjectSet<? extends IdeaDependency> serviceModuleDependencies = serviceModule.getDependencies(); - assertEquals(1, serviceModuleDependencies.size()); - IdeaDependency apiDependency = serviceModuleDependencies.getAt(0); - assertEquals(GradleDependencyScope.COMPILE.name(), apiDependency.getScope().getScope()); - assertTrue(apiDependency instanceof IdeaModuleDependency); - - IdeaModuleDependency apiModuleDependency = (IdeaModuleDependency)apiDependency; - assertNotNull(apiModuleDependency.getDependencyModule()); - assertEquals("api", apiModuleDependency.getDependencyModule().getName()); + ); } @Override diff --git a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/model/builder/ModuleExtendedModelBuilderImplTest.java b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/model/builder/ModuleExtendedModelBuilderImplTest.java new file mode 100644 index 000000000000..9694c9d37345 --- /dev/null +++ b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/model/builder/ModuleExtendedModelBuilderImplTest.java @@ -0,0 +1,177 @@ +/* + * 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 org.jetbrains.plugins.gradle.model.builder; + +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.util.Function; +import com.intellij.util.containers.ContainerUtil; +import org.gradle.tooling.model.DomainObjectSet; +import org.gradle.tooling.model.idea.IdeaModule; +import org.gradle.tooling.model.idea.IdeaSourceDirectory; +import org.jetbrains.plugins.gradle.model.ExtIdeaContentRoot; +import org.jetbrains.plugins.gradle.model.ModuleExtendedModel; +import org.junit.Assert; +import org.junit.Test; + +import java.io.File; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.fail; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * @author Vladislav.Soroka + * @since 1/16/14 + */ +public class ModuleExtendedModelBuilderImplTest extends AbstractModelBuilderTest { + + @Test + public void testGradleSourcesSetsInterpretation() throws Exception { + final int modulesSize = 7; + + ModuleExtendedModelBuilderImpl moduleExtendedModelBuilder = new ModuleExtendedModelBuilderImpl(); + assertTrue(moduleExtendedModelBuilder.canBuild("org.jetbrains.plugins.gradle.model.ModuleExtendedModel")); + + DomainObjectSet<? extends IdeaModule> ideaModules = allModels.getIdeaProject().getModules(); + + List<ModuleExtendedModel> models = + ContainerUtil.mapNotNull(ideaModules, new Function<IdeaModule, ModuleExtendedModel>() { + @Override + public ModuleExtendedModel fun(IdeaModule module) { + ModuleExtendedModel moduleExtendedModel = allModels.getExtraProject(module, ModuleExtendedModel.class); + + assertNotNull(moduleExtendedModel); + + List<String> sourceDirectories = ContainerUtil.newArrayList(); + List<String> resourceDirectories = ContainerUtil.newArrayList(); + List<String> testResourceDirectories = ContainerUtil.newArrayList(); + List<String> testDirectories = ContainerUtil.newArrayList(); + List<String> excludeDirectories = ContainerUtil.newArrayList(); + + fillDirectories(moduleExtendedModel, + sourceDirectories, resourceDirectories, + testDirectories, testResourceDirectories, + excludeDirectories); + + if (module.getName().equals("defaultJavaModule") || module.getName().equals("moduleWithSourceSetDirBothAsResourceAndJava")) { + assertEquals(ContainerUtil.newArrayList("src/main/java"), sourceDirectories); + assertEquals(ContainerUtil.newArrayList("src/main/resources"), resourceDirectories); + assertEquals(ContainerUtil.newArrayList("src/test/java"), testDirectories); + assertEquals(ContainerUtil.newArrayList("src/test/resources"), testResourceDirectories); + assertEquals(ContainerUtil.newArrayList(".gradle", "build"), excludeDirectories); + } + else if (module.getName().equals("moduleWithCustomSourceSet")) { + assertEquals(ContainerUtil.newArrayList("src/custom/java", "src/main/java"), sourceDirectories); + assertEquals(ContainerUtil.newArrayList("src/custom/resources", "src/main/resources"), resourceDirectories); + assertEquals(ContainerUtil.newArrayList("src/test/java"), testDirectories); + assertEquals(ContainerUtil.newArrayList("src/test/resources"), testResourceDirectories); + assertEquals(ContainerUtil.newArrayList(".gradle", "build"), excludeDirectories); + } + else if (module.getName().equals("withIntegrationTests")) { + assertEquals(ContainerUtil.newArrayList("src/main/java"), sourceDirectories); + assertEquals(ContainerUtil.newArrayList("src/main/resources"), resourceDirectories); + assertEquals(ContainerUtil.newArrayList( + "src/integration-test/java", "src/integrationTest/java", "src/test/java"), testDirectories); + assertEquals(ContainerUtil.newArrayList( + "src/integration-test/resources", + "src/integrationTest/resources", + "src/test/resources"), testResourceDirectories); + assertEquals(ContainerUtil.newArrayList(".gradle", "build"), excludeDirectories); + } + else if (module.getName().equals("testGradleSourcesSetsInterpretation")) { + assertTrue(sourceDirectories.isEmpty()); + assertTrue(resourceDirectories.isEmpty()); + assertTrue(testDirectories.isEmpty()); + assertTrue(testResourceDirectories.isEmpty()); + assertEquals(ContainerUtil.newArrayList(".gradle", "build"), excludeDirectories); + } + else if (module.getName().equals("withIdeaPluginCustomization1")) { + assertEquals(ContainerUtil.newArrayList("src/main/java"), sourceDirectories); + assertEquals(ContainerUtil.newArrayList("src/main/resources"), resourceDirectories); + assertEquals(ContainerUtil.newArrayList("src/intTest/java", "src/intTest/resources", "src/test/java"), testDirectories); + assertEquals(ContainerUtil.newArrayList("src/test/resources"), testResourceDirectories); + assertEquals(ContainerUtil.newArrayList(".gradle", "build", "some-extra-exclude-folder"), excludeDirectories); + } + else if (module.getName().equals("withIdeaPluginCustomization2")) { + assertEquals(ContainerUtil.newArrayList("src/main/java", "src/test/java", "src/test/resources"), sourceDirectories); + assertEquals(ContainerUtil.newArrayList("some-extra-resource-folder", "src/main/resources"), resourceDirectories); + assertTrue(testDirectories.isEmpty()); + assertEquals(ContainerUtil.newArrayList("some-extra-test-resource-folder"), testResourceDirectories); + assertEquals(ContainerUtil.newArrayList(".gradle", "build"), excludeDirectories); + } + else { + fail(); + } + + return moduleExtendedModel; + } + }); + + assertEquals(modulesSize, models.size()); + } + + private void fillDirectories(final ModuleExtendedModel model, + List<String> sourceDirectories, List<String> resourceDirectories, + List<String> testDirectories, List<String> resourceTestDirectories, + List<String> excludeDirectories) { + for (ExtIdeaContentRoot contentRoot : model.getContentRoots()) { + sourceDirectories.addAll(getAllPaths(contentRoot.getSourceDirectories(), model.getName())); + resourceDirectories.addAll(getAllPaths(contentRoot.getResourceDirectories(), model.getName())); + testDirectories.addAll(getAllPaths(contentRoot.getTestDirectories(), model.getName())); + resourceTestDirectories.addAll(getAllPaths(contentRoot.getTestResourceDirectories(), model.getName())); + excludeDirectories.addAll(getAllPaths(contentRoot.getExcludeDirectories(), model.getName())); + } + } + + private Collection<? extends String> getAllPaths(Collection<? extends File> directories, final String moduleName) { + List<String> list = ContainerUtil.map2List(directories, new Function<File, String>() { + @Override + public String fun(File sourceDirectory) { + String path = + FileUtil.toCanonicalPath(FileUtil.getRelativePath(new File(testDir, moduleName), sourceDirectory)); + Assert.assertNotNull(path); + return path.substring(path.indexOf("/") + 1); + } + }); + Collections.sort(list); + return list; + } + + private Collection<? extends String> getAllPaths(DomainObjectSet<? extends IdeaSourceDirectory> directories, final String moduleName) { + List<String> list = ContainerUtil.map2List(directories, new Function<IdeaSourceDirectory, String>() { + @Override + public String fun(IdeaSourceDirectory sourceDirectory) { + String path = + FileUtil.toCanonicalPath(FileUtil.getRelativePath(new File(testDir, moduleName), sourceDirectory.getDirectory())); + Assert.assertNotNull(path); + return path.substring(path.indexOf("/") + 1); + } + }); + Collections.sort(list); + return list; + } + + @Override + protected Set<Class> getModels() { + return ContainerUtil.<Class>set(ModuleExtendedModel.class); + } +} + diff --git a/plugins/groovy/hotswap/agentSrc/org/groovy/debug/hotswap/ResetAgent.java b/plugins/groovy/hotswap/agentSrc/org/groovy/debug/hotswap/ResetAgent.java index a3e7023cf8f7..35b3577e258b 100644 --- a/plugins/groovy/hotswap/agentSrc/org/groovy/debug/hotswap/ResetAgent.java +++ b/plugins/groovy/hotswap/agentSrc/org/groovy/debug/hotswap/ResetAgent.java @@ -2,6 +2,7 @@ package org.groovy.debug.hotswap; import org.objectweb.asm.*; +import java.lang.String; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; import java.lang.instrument.Instrumentation; @@ -18,7 +19,15 @@ import java.security.ProtectionDomain; */ @SuppressWarnings({"UtilityClassWithoutPrivateConstructor", "UnusedDeclaration"}) public class ResetAgent { - private static final String timStampFieldStart = "__timeStamp__239_neverHappen"; + private static final String timeStampFieldStart = "__timeStamp__239_neverHappen"; + private static final byte[] timeStampFieldStartBytes; + + static { + timeStampFieldStartBytes = new byte[timeStampFieldStart.length()]; + for (int i = 0; i < timeStampFieldStart.length(); i++) { + timeStampFieldStartBytes[i] = (byte)timeStampFieldStart.charAt(i); + } + } private static boolean initialized; @@ -44,18 +53,30 @@ public class ResetAgent { }); } - private static boolean hasTimestampField(byte[] buffer) { - try { - return new String(buffer, "ISO-8859-1").contains(timStampFieldStart); - } catch (Throwable e) { - return true; + private static boolean matches(byte[] array, byte[] subArray, int start) { + for (int i = 0; i < subArray.length; i++) { + if (array[start + i] != subArray[i]) { + return false; + } + } + return true; + } + + private static boolean containsSubArray(byte[] array, byte[] subArray) { + int maxLength = array.length - subArray.length; + for (int i = 0; i < maxLength; i++) { + if (matches(array, subArray, i)) { + return true; + } } + return false; } private static byte[] removeTimestampField(byte[] newBytes) { - if (!hasTimestampField(newBytes)) { + if (!containsSubArray(newBytes, timeStampFieldStartBytes)) { return null; } + final boolean[] changed = new boolean[]{false}; final ClassWriter writer = new ClassWriter(0); @@ -76,7 +97,7 @@ public class ResetAgent { @Override public FieldVisitor visitField(int i, String name, String s1, String s2, Object o) { - if (name.startsWith(timStampFieldStart)) { + if (name.startsWith(timeStampFieldStart)) { //remove the field changed[0] = true; return null; @@ -92,7 +113,7 @@ public class ResetAgent { return new MethodAdapter(mw) { @Override public void visitFieldInsn(int opCode, String s, String name, String desc) { - if (name.startsWith(timStampFieldStart) && opCode == Opcodes.PUTSTATIC) { + if (name.startsWith(timeStampFieldStart) && opCode == Opcodes.PUTSTATIC) { visitInsn(Type.LONG_TYPE.getDescriptor().equals(desc) ? Opcodes.POP2 : Opcodes.POP); } else { super.visitFieldInsn(opCode, s, name, desc); diff --git a/plugins/groovy/hotswap/gragent.jar b/plugins/groovy/hotswap/gragent.jar Binary files differindex 8794d675ad24..b0e17671dfaf 100644 --- a/plugins/groovy/hotswap/gragent.jar +++ b/plugins/groovy/hotswap/gragent.jar diff --git a/plugins/groovy/resources/fileTemplates/code/Spock SetUp Method.groovy.ft b/plugins/groovy/resources/fileTemplates/code/Spock_SetUp_Method.groovy.ft index 303209e1a46f..303209e1a46f 100644 --- a/plugins/groovy/resources/fileTemplates/code/Spock SetUp Method.groovy.ft +++ b/plugins/groovy/resources/fileTemplates/code/Spock_SetUp_Method.groovy.ft diff --git a/plugins/groovy/resources/fileTemplates/code/Spock SetUp Method.groovy.html b/plugins/groovy/resources/fileTemplates/code/Spock_SetUp_Method.groovy.html index 379785f36c3f..379785f36c3f 100644 --- a/plugins/groovy/resources/fileTemplates/code/Spock SetUp Method.groovy.html +++ b/plugins/groovy/resources/fileTemplates/code/Spock_SetUp_Method.groovy.html diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/navbar/GrNavBarModelExtension.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/navbar/GrNavBarModelExtension.java index e5b5681fc4fa..9116dbe42fd2 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/navbar/GrNavBarModelExtension.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/navbar/GrNavBarModelExtension.java @@ -22,6 +22,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; +import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes; import org.jetbrains.plugins.groovy.GroovyFileType; import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase; @@ -32,6 +33,12 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefini */ public class GrNavBarModelExtension extends AbstractNavBarModelExtension { + @Nullable + @Override + public String getPresentableText(Object object) { + return null; + } + @Override public PsiElement adjustElement(PsiElement psiElement) { final ProjectFileIndex index = ProjectRootManager.getInstance(psiElement.getProject()).getFileIndex(); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/spock/SpockTestFramework.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/spock/SpockTestFramework.java index b7872dcc9730..9a36c8c83990 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/spock/SpockTestFramework.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/spock/SpockTestFramework.java @@ -51,7 +51,7 @@ public class SpockTestFramework extends GroovyTestFramework { @Override public FileTemplateDescriptor getSetUpMethodFileTemplateDescriptor() { - return new FileTemplateDescriptor("Spock SetUp Method.groovy"); + return new FileTemplateDescriptor("Spock_SetUp_Method.groovy"); } @Override diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgLogProvider.java b/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgLogProvider.java index 53cb43d9e457..9fc2b16aedaf 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgLogProvider.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgLogProvider.java @@ -19,20 +19,14 @@ package org.zmlx.hg4idea.log; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.VcsKey; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ArrayUtil; import com.intellij.util.Consumer; -import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import com.intellij.vcs.log.*; -import com.intellij.vcs.log.data.VcsLogBranchFilter; -import com.intellij.vcs.log.data.VcsLogDateFilter; -import com.intellij.vcs.log.data.VcsLogStructureFilter; -import com.intellij.vcs.log.data.VcsLogUserFilter; -import com.intellij.vcs.log.ui.filter.VcsLogTextFilter; +import com.intellij.vcs.log.VcsLogTextFilter; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.zmlx.hg4idea.HgNameWithHashInfo; @@ -162,33 +156,28 @@ public class HgLogProvider implements VcsLogProvider { @NotNull @Override public List<? extends VcsFullCommitDetails> getFilteredDetails(@NotNull final VirtualFile root, - @NotNull Collection<VcsLogFilter> filters, + @NotNull Collection<VcsLogBranchFilter> branchFilters, + @NotNull Collection<VcsLogUserFilter> userFilters, + @NotNull Collection<VcsLogDateFilter> dateFilters, + @NotNull Collection<VcsLogTextFilter> textFilters, + @NotNull Collection<VcsLogStructureFilter> structureFilters, int maxCount) throws VcsException { List<String> filterParameters = ContainerUtil.newArrayList(); - List<VcsLogBranchFilter> branchFilters = ContainerUtil.findAll(filters, VcsLogBranchFilter.class); + // branch filter and user filter may be used several times without delimiter + // or -r options with appropriate revset arguments delimited by '|' or 'and'. if (!branchFilters.isEmpty()) { - String branchFilter = joinFilters(branchFilters, new Function<VcsLogBranchFilter, String>() { - @Override - public String fun(VcsLogBranchFilter filter) { - return filter.getBranchName(); - } - }); - filterParameters.add(prepareParameter("branch", branchFilter)); + for (VcsLogBranchFilter branchFilter : branchFilters) { + filterParameters.add(prepareParameter("branch", branchFilter.getBranchName())); + } } - List<VcsLogUserFilter> userFilters = ContainerUtil.findAll(filters, VcsLogUserFilter.class); if (!userFilters.isEmpty()) { - String authorFilter = joinFilters(userFilters, new Function<VcsLogUserFilter, String>() { - @Override - public String fun(VcsLogUserFilter filter) { - return filter.getUserName(root); - } - }); - filterParameters.add(prepareParameter("user", authorFilter)); + for (VcsLogUserFilter authorFilter : userFilters) { + filterParameters.add(prepareParameter("user", authorFilter.getUserName(root))); + } } - List<VcsLogDateFilter> dateFilters = ContainerUtil.findAll(filters, VcsLogDateFilter.class); if (!dateFilters.isEmpty()) { StringBuilder args = new StringBuilder(); final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm"); @@ -208,7 +197,6 @@ public class HgLogProvider implements VcsLogProvider { filterParameters.add(args.toString()); } - List<VcsLogTextFilter> textFilters = ContainerUtil.findAll(filters, VcsLogTextFilter.class); if (textFilters.size() > 1) { LOG.warn("Expected only one text filter: " + textFilters); } @@ -217,7 +205,6 @@ public class HgLogProvider implements VcsLogProvider { filterParameters.add(prepareParameter("keyword", textFilter)); } - List<VcsLogStructureFilter> structureFilters = ContainerUtil.findAll(filters, VcsLogStructureFilter.class); if (!structureFilters.isEmpty()) { for (VcsLogStructureFilter filter : structureFilters) { for (VirtualFile file : filter.getFiles(root)) { @@ -249,8 +236,4 @@ public class HgLogProvider implements VcsLogProvider { private static String prepareParameter(String paramName, String value) { return "--" + paramName + "=" + value; // no value escaping needed, because the parameter itself will be quoted by GeneralCommandLine } - - private static <T> String joinFilters(List<T> filters, Function<T, String> toString) { - return StringUtil.join(filters, toString, "\\|"); - } } diff --git a/plugins/maven/jps-plugin/src/org/jetbrains/jps/maven/compiler/MavenResourcesBuilder.java b/plugins/maven/jps-plugin/src/org/jetbrains/jps/maven/compiler/MavenResourcesBuilder.java index 8f9a7b85b9cc..13bf78686482 100644 --- a/plugins/maven/jps-plugin/src/org/jetbrains/jps/maven/compiler/MavenResourcesBuilder.java +++ b/plugins/maven/jps-plugin/src/org/jetbrains/jps/maven/compiler/MavenResourcesBuilder.java @@ -1,6 +1,7 @@ package org.jetbrains.jps.maven.compiler; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.builders.BuildOutputConsumer; @@ -116,7 +117,13 @@ public class MavenResourcesBuilder extends TargetBuilder<MavenResourceRootDescri private Pattern getDelimitersPattern() { Pattern pattern = myDelimitersPattern; if (pattern == null) { - pattern = Pattern.compile(config.delimitersPattern); + if (StringUtil.isEmpty(config.escapeString)) { + pattern = Pattern.compile(config.delimitersPattern); + } + else { + String quotedEscapeString = Pattern.quote(config.escapeString); + pattern = Pattern.compile("(" + quotedEscapeString + quotedEscapeString + ")|(?:(" + quotedEscapeString + ")?(" + config.delimitersPattern + "))"); + } myDelimitersPattern = pattern; } return pattern; @@ -168,28 +175,31 @@ public class MavenResourcesBuilder extends TargetBuilder<MavenResourceRootDescri Map<String, String> resolvedProperties = resolvedPropertiesParam; final Matcher matcher = delimitersPattern.matcher(text); + + boolean hasEscapeString = !StringUtil.isEmpty(moduleConfig.escapeString); + final int groupCount = matcher.groupCount(); - final String escapeString = moduleConfig.escapeString; + int firstPropertyGroupIndex = hasEscapeString ? 3 : 0; + int last = 0; while (matcher.find()) { - if (escapeString != null) { - int escapeStringStartIndex = matcher.start() - escapeString.length(); - if (escapeStringStartIndex >= last) { - if (text.startsWith(escapeString, escapeStringStartIndex)) { - out.append(text, last, escapeStringStartIndex); - out.append(matcher.group()); - last = matcher.end(); - continue; - } - } - } - out.append(text, last, matcher.start()); last = matcher.end(); + if (hasEscapeString) { + if (matcher.group(1) != null) { + out.append(moduleConfig.escapeString).append(moduleConfig.escapeString); // double escape string + continue; + } + else if (matcher.group(2) != null) { + out.append(matcher.group(3)); // escaped value + continue; + } + } + String propertyName = null; - for (int i = 0; i < groupCount; i++) { + for (int i = firstPropertyGroupIndex; i < groupCount; i++) { propertyName = matcher.group(i + 1); if (propertyName != null) { break; diff --git a/plugins/maven/jps-plugin/src/org/jetbrains/jps/maven/model/impl/MavenModuleResourceConfiguration.java b/plugins/maven/jps-plugin/src/org/jetbrains/jps/maven/model/impl/MavenModuleResourceConfiguration.java index 3c4c47c18e56..86340adf0ea7 100644 --- a/plugins/maven/jps-plugin/src/org/jetbrains/jps/maven/model/impl/MavenModuleResourceConfiguration.java +++ b/plugins/maven/jps-plugin/src/org/jetbrains/jps/maven/model/impl/MavenModuleResourceConfiguration.java @@ -58,7 +58,7 @@ public class MavenModuleResourceConfiguration { public Set<String> filteringExclusions = new THashSet<String>(FileUtil.PATH_HASHING_STRATEGY); @OptionTag - public String escapeString = MavenProjectConfiguration.DEFAULT_ESCAPE_STRING; + public String escapeString = null; @OptionTag public boolean escapeWindowsPaths = true; diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/compiler/MavenResourceCompiler.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/compiler/MavenResourceCompiler.java index 209583b6f731..d5eb4a69d7f7 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/compiler/MavenResourceCompiler.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/compiler/MavenResourceCompiler.java @@ -167,7 +167,7 @@ public class MavenResourceCompiler implements ClassPostProcessingCompiler { Set<String> nonFilteredExtensions = collectNonFilteredExtensions(mavenProject); String escapeString = MavenJDOMUtil.findChildValueByPath(mavenProject.getPluginConfiguration("org.apache.maven.plugins", "maven-resources-plugin"), - "escapeString", "\\"); + "escapeString", null); List<MyProcessingItem> moduleItemsToProcess = new ArrayList<MyProcessingItem>(); collectProcessingItems(eachModule, mavenProject, context, properties, propertiesHashCode, @@ -245,7 +245,7 @@ public class MavenResourceCompiler implements ClassPostProcessingCompiler { Properties properties, long propertiesHashCode, Set<String> nonFilteredExtensions, - String escapeString, + @Nullable String escapeString, boolean tests, List<MyProcessingItem> result) { String outputDir = CompilerPaths.getModuleOutputPath(module, tests); @@ -305,7 +305,7 @@ public class MavenResourceCompiler implements ClassPostProcessingCompiler { final Properties properties, final long propertiesHashCode, final Set<String> nonFilteredExtensions, - final String escapeString, + @Nullable final String escapeString, final List<MyProcessingItem> result, final ProgressIndicator indicator) { VfsUtilCore.visitChildrenRecursively(currentDir, new VirtualFileVisitor() { @@ -476,7 +476,7 @@ public class MavenResourceCompiler implements ClassPostProcessingCompiler { boolean isFiltered, Properties properties, long propertiesHashCode, - String escapeString) { + @Nullable String escapeString) { myModule = module; mySourceFile = sourceFile; myOutputPath = outputPath; @@ -507,6 +507,7 @@ public class MavenResourceCompiler implements ClassPostProcessingCompiler { return myProperties; } + @Nullable public String getEscapeString() { return myEscapeString; } diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/MavenPropertyResolver.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/MavenPropertyResolver.java index 979e182bad25..66875895f278 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/MavenPropertyResolver.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/MavenPropertyResolver.java @@ -47,7 +47,7 @@ public class MavenPropertyResolver { public static void doFilterText(Module module, String text, Properties additionalProperties, - String propertyEscapeString, + @Nullable String propertyEscapeString, Appendable out) throws IOException { MavenProjectsManager manager = MavenProjectsManager.getInstance(module.getProject()); MavenProject mavenProject = manager.findProject(module); diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/importing/MavenModuleImporter.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/importing/MavenModuleImporter.java index 553175e5a07e..0d4b06b21cb0 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/importing/MavenModuleImporter.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/importing/MavenModuleImporter.java @@ -294,9 +294,7 @@ public class MavenModuleImporter { if (file == null) continue; if (libraryModel == null) { - String libraryName = artifact.getLibraryName(); - assert libraryName.startsWith(MavenArtifact.MAVEN_LIB_PREFIX); - libraryName = MavenArtifact.MAVEN_LIB_PREFIX + "ATTACHED-JAR: " + libraryName.substring(MavenArtifact.MAVEN_LIB_PREFIX.length()); + String libraryName = getAttachedJarsLibName(artifact); Library library = myModifiableModelsProvider.getLibraryByName(libraryName); if (library == null) { @@ -314,6 +312,14 @@ public class MavenModuleImporter { } @NotNull + public static String getAttachedJarsLibName(@NotNull MavenArtifact artifact) { + String libraryName = artifact.getLibraryName(); + assert libraryName.startsWith(MavenArtifact.MAVEN_LIB_PREFIX); + libraryName = MavenArtifact.MAVEN_LIB_PREFIX + "ATTACHED-JAR: " + libraryName.substring(MavenArtifact.MAVEN_LIB_PREFIX.length()); + return libraryName; + } + + @NotNull public static DependencyScope selectScope(String mavenScope) { if (MavenConstants.SCOPE_RUNTIME.equals(mavenScope)) return DependencyScope.RUNTIME; if (MavenConstants.SCOPE_TEST.equals(mavenScope)) return DependencyScope.TEST; diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenResourceCompilerConfigurationGenerator.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenResourceCompilerConfigurationGenerator.java index 523d5cbec958..c2b0aa9ba49b 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenResourceCompilerConfigurationGenerator.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenResourceCompilerConfigurationGenerator.java @@ -129,7 +129,7 @@ public class MavenResourceCompilerConfigurationGenerator { } Element pluginConfiguration = mavenProject.getPluginConfiguration("org.apache.maven.plugins", "maven-resources-plugin"); - resourceConfig.escapeString = MavenJDOMUtil.findChildValueByPath(pluginConfiguration, "escapeString", "\\"); + resourceConfig.escapeString = MavenJDOMUtil.findChildValueByPath(pluginConfiguration, "escapeString", null); String escapeWindowsPaths = MavenJDOMUtil.findChildValueByPath(pluginConfiguration, "escapeWindowsPaths"); if (escapeWindowsPaths != null) { resourceConfig.escapeWindowsPaths = Boolean.parseBoolean(escapeWindowsPaths); diff --git a/plugins/maven/src/test/java/org/jetbrains/idea/maven/compiler/ResourceFilteringTest.java b/plugins/maven/src/test/java/org/jetbrains/idea/maven/compiler/ResourceFilteringTest.java index 4c2c7815e5ff..80e738fa6a81 100644 --- a/plugins/maven/src/test/java/org/jetbrains/idea/maven/compiler/ResourceFilteringTest.java +++ b/plugins/maven/src/test/java/org/jetbrains/idea/maven/compiler/ResourceFilteringTest.java @@ -868,10 +868,15 @@ public abstract class ResourceFilteringTest extends MavenImportingTestCase { } public void testEscapingFiltering() throws Exception { + if (!useJps()) return; + createProjectSubFile("filters/filter.properties", "xxx=value"); createProjectSubFile("resources/file.properties", "value1=\\${xxx}\n" + - "value2=${xxx}\n"); + "value2=\\\\${xxx}\n" + + "value3=\\\\\\${xxx}\n" + + "value3=\\\\\\\\${xxx}\n" + + "value4=.\\.\\\\.\\\\\\."); importProject("<groupId>test</groupId>" + "<artifactId>project</artifactId>" + @@ -887,12 +892,24 @@ public abstract class ResourceFilteringTest extends MavenImportingTestCase { " <filtering>true</filtering>" + " </resource>" + " </resources>" + + " <plugins>" + + " <plugin>" + + " <groupId>org.apache.maven.plugins</groupId>" + + " <artifactId>maven-resources-plugin</artifactId>" + + " <configuration>" + + " <escapeString>\\</escapeString>" + + " </configuration>" + + " </plugin>" + + " </plugins>" + "</build>"); compileModules("project"); assertResult("target/classes/file.properties", "value1=${xxx}\n" + - "value2=value\n"); + "value2=\\\\value\n" + + "value3=\\\\${xxx}\n" + + "value3=\\\\\\\\value\n" + + "value4=.\\.\\\\.\\\\\\."); } public void testPropertyPriority() throws Exception { diff --git a/python/IntelliLang-python/src/pyInjections.xml b/python/IntelliLang-python/src/pyInjections.xml index f561c1d33016..a75015e02441 100644 --- a/python/IntelliLang-python/src/pyInjections.xml +++ b/python/IntelliLang-python/src/pyInjections.xml @@ -9,6 +9,6 @@ </injection> <injection language="SQL" injector-id="python"> <display-name>"SQL select/delete/insert/update/create"</display-name> - <place><![CDATA[pyLiteralExpression().withText(string().matchesBrics("[UuRrBb\\\"\\' \t]*(((SELECT|DELETE) .*FROM)|((INSERT|REPLACE) .*INTO)|(UPDATE .* SET)|((CREATE|DROP|ALTER) +(TABLE|INDEX))) .*"))]]></place> + <place><![CDATA[pyLiteralExpression().withText(string().matchesBrics(".*(((SELECT|DELETE) .*FROM)|((INSERT|REPLACE) .*INTO)|(UPDATE .* SET)|((CREATE|DROP|ALTER) +(TABLE|INDEX))) .*"))]]></place> </injection> </component> diff --git a/python/helpers/generator3.py b/python/helpers/generator3.py index fe24620d374d..ca61efe13be4 100644 --- a/python/helpers/generator3.py +++ b/python/helpers/generator3.py @@ -1,41 +1,13 @@ # encoding: utf-8 -from pycharm_generator_utils.module_redeclarator import * -from pycharm_generator_utils.util_methods import * -from pycharm_generator_utils.constants import * -import os import atexit import zipfile -debug_mode = False - - -def build_output_name(dirname, qualified_name): - qualifiers = qualified_name.split(".") - if dirname and not dirname.endswith("/") and not dirname.endswith("\\"): - dirname += os.path.sep # "a -> a/" - for pathindex in range(len(qualifiers) - 1): # create dirs for all qualifiers but last - subdirname = dirname + os.path.sep.join(qualifiers[0: pathindex + 1]) - if not os.path.isdir(subdirname): - action("creating subdir %r", subdirname) - os.makedirs(subdirname) - init_py = os.path.join(subdirname, "__init__.py") - if os.path.isfile(subdirname + ".py"): - os.rename(subdirname + ".py", init_py) - elif not os.path.isfile(init_py): - init = fopen(init_py, "w") - init.close() - target_name = dirname + os.path.sep.join(qualifiers) - if os.path.isdir(target_name): - fname = os.path.join(target_name, "__init__.py") - else: - fname = target_name + ".py" - - dirname = os.path.dirname(fname) +from pycharm_generator_utils.module_redeclarator import * +from pycharm_generator_utils.util_methods import * +from pycharm_generator_utils.constants import * - if not os.path.isdir(dirname): - os.makedirs(dirname) - return fname +debug_mode = False def redo_module(module_name, outfile, module_file_name, doing_builtins): @@ -263,80 +235,67 @@ def process_one(name, mod_file_name, doing_builtins, subdir): say(name) sys.stdout.flush() action("doing nothing") - outfile = None + try: - try: - fname = build_output_name(subdir, name) - action("opening %r", fname) - outfile = fopen(fname, "w") - old_modules = list(sys.modules.keys()) - imported_module_names = [] - - class MyFinder: - #noinspection PyMethodMayBeStatic - def find_module(self, fullname, path=None): - if fullname != name: - imported_module_names.append(fullname) - return None - - my_finder = None - if hasattr(sys, 'meta_path'): - my_finder = MyFinder() - sys.meta_path.append(my_finder) - else: - imported_module_names = None - - action("importing") - __import__(name) # sys.modules will fill up with what we want - - if my_finder: - sys.meta_path.remove(my_finder) - if imported_module_names is None: - imported_module_names = [m for m in sys.modules.keys() if m not in old_modules] - - redo_module(name, outfile, mod_file_name, doing_builtins) - # The C library may have called Py_InitModule() multiple times to define several modules (gtk._gtk and gtk.gdk); - # restore all of them - path = name.split(".") - redo_imports = not ".".join(path[:-1]) in MODULES_INSPECT_DIR - if imported_module_names and redo_imports: - for m in sys.modules.keys(): - action("looking at possible submodule %r", m) - # if module has __file__ defined, it has Python source code and doesn't need a skeleton - if m not in old_modules and m not in imported_module_names and m != name and not hasattr( - sys.modules[m], '__file__'): - if not quiet: - say(m) - sys.stdout.flush() - fname = build_output_name(subdir, m) - action("opening %r", fname) - subfile = fopen(fname, "w") - try: - redo_module(m, subfile, mod_file_name, doing_builtins) - finally: - action("closing %r", fname) - subfile.close() - except: - exctype, value = sys.exc_info()[:2] - msg = "Failed to process %r while %s: %s" - args = name, CURRENT_ACTION, str(value) - report(msg, *args) - if outfile is not None and not outfile.closed: - outfile.write("# encoding: %s\n" % OUT_ENCODING) - outfile.write("# module %s\n" % name) - outfile.write("# from %s\n" % mod_file_name) - outfile.write("# by generator %s\n" % VERSION) - outfile.write("\n\n") - outfile.write("# Skeleton generation error:\n#\n# " + (msg % args) + "\n") - if debug_mode: - if sys.platform == 'cli': - import traceback - traceback.print_exc(file=sys.stderr) - raise - return False - finally: - if outfile is not None and not outfile.closed: - outfile.close() + fname = build_output_name(subdir, name) + action("opening %r", fname) + old_modules = list(sys.modules.keys()) + imported_module_names = [] + + class MyFinder: + #noinspection PyMethodMayBeStatic + def find_module(self, fullname, path=None): + if fullname != name: + imported_module_names.append(fullname) + return None + + my_finder = None + if hasattr(sys, 'meta_path'): + my_finder = MyFinder() + sys.meta_path.append(my_finder) + else: + imported_module_names = None + + action("importing") + __import__(name) # sys.modules will fill up with what we want + + if my_finder: + sys.meta_path.remove(my_finder) + if imported_module_names is None: + imported_module_names = [m for m in sys.modules.keys() if m not in old_modules] + + redo_module(name, fname, mod_file_name, doing_builtins) + # The C library may have called Py_InitModule() multiple times to define several modules (gtk._gtk and gtk.gdk); + # restore all of them + path = name.split(".") + redo_imports = not ".".join(path[:-1]) in MODULES_INSPECT_DIR + if imported_module_names and redo_imports: + for m in sys.modules.keys(): + if m.startswith("pycharm_generator_utils"): continue + action("looking at possible submodule %r", m) + # if module has __file__ defined, it has Python source code and doesn't need a skeleton + if m not in old_modules and m not in imported_module_names and m != name and not hasattr( + sys.modules[m], '__file__'): + if not quiet: + say(m) + sys.stdout.flush() + fname = build_output_name(subdir, m) + action("opening %r", fname) + try: + redo_module(m, fname, mod_file_name, doing_builtins) + finally: + action("closing %r", fname) + except: + exctype, value = sys.exc_info()[:2] + msg = "Failed to process %r while %s: %s" + args = name, CURRENT_ACTION, str(value) + report(msg, *args) + if debug_mode: + if sys.platform == 'cli': + import traceback + traceback.print_exc(file=sys.stderr) + raise + return False return True diff --git a/python/helpers/pycharm/nose_utils.py b/python/helpers/pycharm/nose_utils.py index bed318a50bf0..9a6d68251cb3 100644 --- a/python/helpers/pycharm/nose_utils.py +++ b/python/helpers/pycharm/nose_utils.py @@ -1,9 +1,13 @@ -from tcmessages import TeamcityServiceMessages -import sys, traceback, datetime +import sys +import traceback +import datetime import unittest + +from tcmessages import TeamcityServiceMessages from tcunittest import strclass from tcunittest import TeamcityTestResult + try: from nose.util import isclass # backwards compat from nose.config import Config @@ -59,7 +63,10 @@ class TeamcityPlugin(ErrorClassPlugin, TextTestResult, TeamcityTestResult): def formatErr(self, err): exctype, value, tb = err if isinstance(value, str): - value = exctype(value) + try: + value = exctype(value) + except TypeError: + pass return ''.join(traceback.format_exception(exctype, value, tb)) def is_gen(self, test): diff --git a/python/helpers/pycharm_generator_utils/constants.py b/python/helpers/pycharm_generator_utils/constants.py index 6b77e73049b9..4ea8cf17309a 100644 --- a/python/helpers/pycharm_generator_utils/constants.py +++ b/python/helpers/pycharm_generator_utils/constants.py @@ -6,7 +6,7 @@ import string import time -VERSION = "1.131" +VERSION = "1.133" OUT_ENCODING = 'utf-8' diff --git a/python/helpers/pycharm_generator_utils/module_redeclarator.py b/python/helpers/pycharm_generator_utils/module_redeclarator.py index f56fb430e0c8..83778790a1c5 100644 --- a/python/helpers/pycharm_generator_utils/module_redeclarator.py +++ b/python/helpers/pycharm_generator_utils/module_redeclarator.py @@ -1,6 +1,7 @@ +import keyword + from pycharm_generator_utils.util_methods import * from pycharm_generator_utils.constants import * -import keyword, re class emptylistdict(dict): @@ -49,6 +50,11 @@ class Buf(object): return len(self.data) == 0 +class ClassBuf(Buf): + def __init__(self, name, indenter): + super(ClassBuf, self).__init__(indenter) + self.name = name + #noinspection PyUnresolvedReferences,PyBroadException class ModuleRedeclarator(object): def __init__(self, module, outfile, mod_filename, indent_size=4, doing_builtins=False): @@ -67,6 +73,7 @@ class ModuleRedeclarator(object): self.imports_buf = Buf(self) self.functions_buf = Buf(self) self.classes_buf = Buf(self) + self.classes_buffs = list() self.footer_buf = Buf(self) self.indent_size = indent_size self._indent_step = " " * self.indent_size @@ -106,8 +113,43 @@ class ModuleRedeclarator(object): return self._indent_step * level def flush(self): - for buf in (self.header_buf, self.imports_buf, self.functions_buf, self.classes_buf, self.footer_buf): - buf.flush(self.outfile) + init = None + try: + if self.mod_filename and len(self.classes_buffs) >= 30: + mod_path = self.outfile.strip(".py") + + fname = build_output_name(mod_path, "__init__") + init = fopen(fname, "w") + for buf in (self.header_buf, self.imports_buf, self.functions_buf, self.classes_buf): + buf.flush(init) + + data = "" + for buf in self.classes_buffs: + fname = build_output_name(mod_path, buf.name) + dummy = fopen(fname, "w") + self.header_buf.flush(dummy) + buf.flush(dummy) + data += "from " + if version[0] >= 3: + data += "." + data += buf.name + " import " + buf.name + "\n" + dummy.close() + + init.write(data) + self.footer_buf.flush(init) + else: + init = fopen(self.outfile, "w") + for buf in (self.header_buf, self.imports_buf, self.functions_buf, self.classes_buf): + buf.flush(init) + + for buf in self.classes_buffs: + buf.flush(init) + + self.footer_buf.flush(init) + + finally: + if init is not None and not init.closed: + init.close() # Some builtin classes effectively change __init__ signature without overriding it. # This callable serves as a placeholder to be replaced via REDEFINED_BUILTIN_SIGS @@ -150,7 +192,7 @@ class ModuleRedeclarator(object): for initializer_type, r in self._initializers: if initializer_type == a_type: return r - # NOTE: here we could handle things like defaultdict, sets, etc if we wanted + # NOTE: here we could handle things like defaultdict, sets, etc if we wanted return "None" @@ -224,7 +266,7 @@ class ModuleRedeclarator(object): seen_values.append(value) if isinstance(k, SIMPLEST_TYPES): self.fmt_value(out, value, indent + 1, prefix=repr(k) + ": ", postfix=",", - seen_values=seen_values) + seen_values=seen_values) else: # both key and value need fancy formatting self.fmt_value(out, k, indent + 1, postfix=": ", seen_values=seen_values) @@ -244,9 +286,15 @@ class ModuleRedeclarator(object): if self._defined.get(found_name, False): out(indent, prefix, found_name, postfix) else: - # a forward / circular declaration happens + # a forward / circular declaration happens notice = "" - real_value = cleanup(repr(p_value)) + try: + representation = repr(p_value) + except Exception: + import traceback + traceback.print_exc(file=sys.stderr) + return + real_value = cleanup(representation) if found_name: if found_name == as_name: notice = " # (!) real value is %r" % real_value @@ -409,7 +457,7 @@ class ModuleRedeclarator(object): def is_predefined_builtin(self, module_name, class_name, func_name): return self.doing_builtins and module_name == BUILTIN_MOD_NAME and ( - class_name, func_name) in PREDEFINED_BUILTIN_SIGS + class_name, func_name) in PREDEFINED_BUILTIN_SIGS def redo_function(self, out, p_func, p_name, indent, p_class=None, p_modname=None, classname=None, seen=None): @@ -434,7 +482,7 @@ class ModuleRedeclarator(object): return else: seen[id(p_func)] = p_name - # real work + # real work if classname is None: classname = p_class and p_class.__name__ or None if p_class and hasattr(p_class, '__mro__'): @@ -492,7 +540,7 @@ class ModuleRedeclarator(object): out(indent, "def ", p_name, sig, ": # known case of ", ofwhat) out_doc_attr(out, p_func, indent + 1, p_class) else: - # __doc__ is our best source of arglist + # __doc__ is our best source of arglist sig_note = "real signature unknown" spec = "" is_init = (p_name == "__init__" and p_class is not None) @@ -508,17 +556,17 @@ class ModuleRedeclarator(object): action("parsing doc of func %r of class %r", p_name, p_class) if isinstance(funcdoc, STR_TYPES): (spec, ret_literal, more_notes) = self.parse_func_doc(funcdoc, p_name, p_name, classname, deco, - sip_generated) + sip_generated) if spec is None and p_name == '__init__' and classname: (spec, ret_literal, more_notes) = self.parse_func_doc(funcdoc, classname, p_name, classname, deco, - sip_generated) + sip_generated) sig_restored = spec is not None if more_notes: if sig_note: sig_note += "; " sig_note += more_notes if not sig_restored: - # use an allow-all declaration + # use an allow-all declaration decl = [] if p_class: first_param = propose_first_param(deco) @@ -531,7 +579,7 @@ class ModuleRedeclarator(object): # to reduce size of stubs, don't output same docstring twice for class and its __init__ method if not is_init or funcdoc != p_class.__doc__: out_docstring(out, funcdoc, indent + 1) - # body + # body if ret_literal and not is_init: out(indent + 1, "return ", ret_literal) else: @@ -646,7 +694,7 @@ class ModuleRedeclarator(object): self.redo_function(out, item, item_name, indent + 1, p_class, p_modname, classname=p_name, seen=seen_funcs) except: handle_error_func(item_name, out) - # + # known_props = KNOWN_PROPS.get(p_modname, {}) a_setter = "lambda self, v: None" a_deleter = "lambda self: None" @@ -782,7 +830,7 @@ class ModuleRedeclarator(object): for item_name in module_dict: note("looking at %s", item_name) if item_name in ( - "__dict__", "__doc__", "__module__", "__file__", "__name__", "__builtins__", "__package__"): + "__dict__", "__doc__", "__module__", "__file__", "__name__", "__builtins__", "__package__"): continue # handled otherwise try: item = getattr(self.module, item_name) # let getters do the magic @@ -806,7 +854,7 @@ class ModuleRedeclarator(object): mod_name = getattr(item, '__module__', None) except: pass - # we assume that module foo.bar never imports foo; foo may import foo.bar. (see pygame and pygame.rect) + # we assume that module foo.bar never imports foo; foo may import foo.bar. (see pygame and pygame.rect) maybe_import_mod_name = mod_name or "" import_is_from_top = len(p_name) > len(maybe_import_mod_name) and p_name.startswith(maybe_import_mod_name) note("mod_name = %s, prospective = %s, from top = %s", mod_name, maybe_import_mod_name, import_is_from_top) @@ -906,9 +954,8 @@ class ModuleRedeclarator(object): self.functions_buf.out(0, "# no functions") # if classes: - out = self.functions_buf.out - out(0, "# classes") - out(0, "") + self.classes_buf.out(0, "# classes") + self.classes_buf.out(0, "") seen_classes = {} # sort classes so that inheritance order is preserved cls_list = [] # items are (class_name, mro_tuple) @@ -922,6 +969,9 @@ class ModuleRedeclarator(object): break # ...and need not go fartehr than first known child cls_list.insert(ins_index, (cls_name, get_mro(cls))) for item_name in [cls_item[0] for cls_item in cls_list]: + buf = ClassBuf(item_name, self) + self.classes_buffs.append(buf) + out = buf.out if item_name in omitted_names: out(0, "# definition of ", item_name, " omitted") continue @@ -974,7 +1024,7 @@ class ModuleRedeclarator(object): self.footer_buf.out(0, "# intermittent names") for value in values_to_add: self.footer_buf.out(0, value) - # imports: last, because previous parts could alter used_imports or hidden_imports + # imports: last, because previous parts could alter used_imports or hidden_imports self.output_import_froms() if self.imports_buf.isEmpty(): self.imports_buf.out(0, "# no imports") @@ -1009,7 +1059,7 @@ class ModuleRedeclarator(object): names_pack.append(n) names_pack.append(", ") right_pos += (len_n + 2) - # last line is... + # last line is... if indent_level == 0: # one line names_pack[0] = names_pack[0][:-1] # cut off lpar names_pack[-1] = "" # cut last comma diff --git a/python/helpers/pycharm_generator_utils/util_methods.py b/python/helpers/pycharm_generator_utils/util_methods.py index 8d6d3568914e..2ddbc94f4a63 100644 --- a/python/helpers/pycharm_generator_utils/util_methods.py +++ b/python/helpers/pycharm_generator_utils/util_methods.py @@ -6,7 +6,7 @@ except ImportError: inspect = None def create_named_tuple(): #TODO: user-skeleton - return """ + return """ class __namedtuple(tuple): '''A mock base class for named tuples.''' @@ -38,12 +38,12 @@ class __namedtuple(tuple): """ def create_generator(): - # Fake <type 'generator'> - if version[0] < 3: - next_name = "next" - else: - next_name = "__next__" - txt = """ + # Fake <type 'generator'> + if version[0] < 3: + next_name = "next" + else: + next_name = "__next__" + txt = """ class __generator(object): '''A mock class representing the generator function type.''' def __init__(self): @@ -59,8 +59,8 @@ class __generator(object): '''Return the next item from the container.''' pass """ % (next_name,) - if version[0] >= 3 or (version[0] == 2 and version[1] >= 5): - txt += """ + if version[0] >= 3 or (version[0] == 2 and version[1] >= 5): + txt += """ def close(self): '''Raises new GeneratorExit exception inside the generator to terminate the iteration.''' pass @@ -73,10 +73,10 @@ class __generator(object): '''Used to raise an exception inside the generator.''' pass """ - return txt + return txt def _searchbases(cls, accum): -# logic copied from inspect.py + # logic copied from inspect.py if cls not in accum: accum.append(cls) for x in cls.__bases__: @@ -84,7 +84,7 @@ def _searchbases(cls, accum): def get_mro(a_class): -# logic copied from inspect.py + # logic copied from inspect.py """Returns a tuple of MRO classes.""" if hasattr(a_class, "__mro__"): return a_class.__mro__ @@ -236,7 +236,7 @@ def transform_seq(results, toplevel=True): if toplevel and not has_item_starting_with(ret, "*"): ret.append("*more") else: - # we're in a "foo, (bar1, bar2, ...)"; make it "foo, bar_tuple" + # we're in a "foo, (bar1, bar2, ...)"; make it "foo, bar_tuple" return extract_alpha_prefix(results[0][1]) + "_tuple" else: # just name ret.append(sanitize_ident(token_name, is_clr)) @@ -271,7 +271,7 @@ def transform_optional_seq(results): if len(token) == 3: # name with value; little sense, but can happen in a deeply nested optional ret.append(sanitize_ident(token_name, is_clr) + "=" + sanitize_value(token[2])) elif token_name == '...': - # we're in a "foo, [bar, ...]"; make it "foo, *bar" + # we're in a "foo, [bar, ...]"; make it "foo, *bar" return ["*" + extract_alpha_prefix( results[1][1])] # we must return a seq; [1] is first simple, [1][1] is its name else: # just name @@ -531,7 +531,7 @@ def restore_clr(p_name, p_class): if not methods: bases = p_class.__bases__ if len(bases) == 1 and p_name in dir(bases[0]): - # skip inherited methods + # skip inherited methods return None, None return p_name + '(*args)', 'cannot find CLR method' @@ -542,3 +542,31 @@ def restore_clr(p_name, p_class): if not methods[0].IsStatic: params = ['self'] + params return build_signature(p_name, params), None + +def build_output_name(dirname, qualified_name): + qualifiers = qualified_name.split(".") + if dirname and not dirname.endswith("/") and not dirname.endswith("\\"): + dirname += os.path.sep # "a -> a/" + for pathindex in range(len(qualifiers) - 1): # create dirs for all qualifiers but last + subdirname = dirname + os.path.sep.join(qualifiers[0: pathindex + 1]) + if not os.path.isdir(subdirname): + action("creating subdir %r", subdirname) + os.makedirs(subdirname) + init_py = os.path.join(subdirname, "__init__.py") + if os.path.isfile(subdirname + ".py"): + os.rename(subdirname + ".py", init_py) + elif not os.path.isfile(init_py): + init = fopen(init_py, "w") + init.close() + target_name = dirname + os.path.sep.join(qualifiers) + if os.path.isdir(target_name): + fname = os.path.join(target_name, "__init__.py") + else: + fname = target_name + ".py" + + dirname = os.path.dirname(fname) + + if not os.path.isdir(dirname): + os.makedirs(dirname) + + return fname diff --git a/python/helpers/tools/stdlib_packages.txt b/python/helpers/tools/stdlib_packages.txt index 619651ebbec9..2ee4b5d22705 100644 --- a/python/helpers/tools/stdlib_packages.txt +++ b/python/helpers/tools/stdlib_packages.txt @@ -6,6 +6,7 @@ argparse array ast asynchat +asyncio asyncore atexit audiodev @@ -68,6 +69,8 @@ dummy_thread dummy_threading email encodings +ensurepip +enum errno exceptions fcntl @@ -150,6 +153,7 @@ os os2emxpath ossaudiodev parser +pathlib pdb pickle pickletools @@ -194,6 +198,7 @@ robotparser runpy sched select +selectors sets sgmllib sha @@ -219,6 +224,7 @@ sre_parse ssl stat statcache +statistics statvfs string StringIO @@ -254,6 +260,7 @@ token tokenize trace traceback +tracemalloc tty turtle turtledemo diff --git a/python/psi-api/src/com/jetbrains/python/psi/PyClass.java b/python/psi-api/src/com/jetbrains/python/psi/PyClass.java index e1084e826741..6bad8d10a6f8 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/PyClass.java +++ b/python/psi-api/src/com/jetbrains/python/psi/PyClass.java @@ -136,11 +136,13 @@ public interface PyClass extends PsiNameIdentifierOwner, PyStatement, NameDefine /** * Finds a property with the specified name in the class or one of its ancestors. * + * * @param name of the property + * @param inherited * @return descriptor of property accessors, or null if such property does not exist. */ @Nullable - Property findProperty(@NotNull String name); + Property findProperty(@NotNull String name, boolean inherited); /** * Apply a processor to every method, looking at superclasses in method resolution order as needed. diff --git a/python/psi-api/src/com/jetbrains/python/psi/impl/PyTypeProvider.java b/python/psi-api/src/com/jetbrains/python/psi/impl/PyTypeProvider.java index 38a0ee670050..b4e5be8753d3 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/impl/PyTypeProvider.java +++ b/python/psi-api/src/com/jetbrains/python/psi/impl/PyTypeProvider.java @@ -30,7 +30,7 @@ public interface PyTypeProvider { ExtensionPointName<PyTypeProvider> EP_NAME = ExtensionPointName.create("Pythonid.typeProvider"); @Nullable - PyType getReferenceExpressionType(PyReferenceExpression referenceExpression, TypeEvalContext context); + PyType getReferenceExpressionType(@NotNull PyReferenceExpression referenceExpression, @NotNull TypeEvalContext context); @Nullable PyType getReferenceType(@NotNull PsiElement referenceTarget, TypeEvalContext context, @Nullable PsiElement anchor); diff --git a/python/psi-api/src/com/jetbrains/python/psi/types/PyOverridingAncestorsClassMembersProvider.java b/python/psi-api/src/com/jetbrains/python/psi/types/PyOverridingAncestorsClassMembersProvider.java new file mode 100644 index 000000000000..07b53091aba1 --- /dev/null +++ b/python/psi-api/src/com/jetbrains/python/psi/types/PyOverridingAncestorsClassMembersProvider.java @@ -0,0 +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.jetbrains.python.psi.types; + +/** + * @author vlan + */ +public interface PyOverridingAncestorsClassMembersProvider extends PyClassMembersProvider { +} diff --git a/python/psi-api/src/com/jetbrains/python/psi/types/PyTypeProviderBase.java b/python/psi-api/src/com/jetbrains/python/psi/types/PyTypeProviderBase.java index dc20bad8f36f..0792b5083542 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/types/PyTypeProviderBase.java +++ b/python/psi-api/src/com/jetbrains/python/psi/types/PyTypeProviderBase.java @@ -80,7 +80,7 @@ public class PyTypeProviderBase implements PyTypeProvider { }; @Override - public PyType getReferenceExpressionType(PyReferenceExpression referenceExpression, TypeEvalContext context) { + public PyType getReferenceExpressionType(@NotNull PyReferenceExpression referenceExpression, @NotNull TypeEvalContext context) { return null; } diff --git a/python/src/META-INF/python-core.xml b/python/src/META-INF/python-core.xml index f11f009b3a5c..708a686b6490 100644 --- a/python/src/META-INF/python-core.xml +++ b/python/src/META-INF/python-core.xml @@ -76,7 +76,7 @@ <applicationService serviceInterface="com.jetbrains.python.module.PyModuleService" serviceImplementation="com.jetbrains.python.module.PyModuleServiceImpl"/> - <typedHandler implementation="com.jetbrains.python.codeInsight.KeywordTypedHandler" id="pyCommaAfterKwd"/> + <typedHandler implementation="com.jetbrains.python.codeInsight.PyKeywordTypedHandler" id="pyCommaAfterKwd"/> <typedHandler implementation="com.jetbrains.python.codeInsight.PyMethodNameTypedHandler" id="pyMethodNameTypedHandler"/> <typedHandler implementation="com.jetbrains.python.editor.PythonSpaceHandler"/> @@ -364,6 +364,8 @@ <applicationService serviceInterface="com.jetbrains.python.codeInsight.PyCodeInsightSettings" serviceImplementation="com.jetbrains.python.codeInsight.PyCodeInsightSettings"/> + <applicationService serviceInterface="com.jetbrains.python.testing.PyTestFrameworkService" + serviceImplementation="com.jetbrains.python.testing.PyTestFrameworkService"/> <autoImportOptionsProvider instance="com.jetbrains.python.codeInsight.imports.PyAutoImportOptions"/> <defaultLiveTemplatesProvider implementation="com.jetbrains.python.codeInsight.liveTemplates.PyDefaultLiveTemplatesProvider"/> diff --git a/python/src/com/jetbrains/python/codeInsight/KeywordTypedHandler.java b/python/src/com/jetbrains/python/codeInsight/PyKeywordTypedHandler.java index 5e3e6ca68b1d..3bf094dcb2c8 100644 --- a/python/src/com/jetbrains/python/codeInsight/KeywordTypedHandler.java +++ b/python/src/com/jetbrains/python/codeInsight/PyKeywordTypedHandler.java @@ -30,7 +30,7 @@ import com.jetbrains.python.psi.PyStringLiteralExpression; * User: dcheryasov * Date: May 29, 2009 4:42:03 AM */ -public class KeywordTypedHandler extends TypedHandlerDelegate { +public class PyKeywordTypedHandler extends TypedHandlerDelegate { @Override public Result beforeCharTyped(char character, Project project, Editor editor, PsiFile file, FileType fileType) { if (!(fileType instanceof PythonFileType)) return Result.CONTINUE; // else we'd mess up with other file types! @@ -51,7 +51,7 @@ public class KeywordTypedHandler extends TypedHandlerDelegate { editor.getCaretModel().moveToOffset(offset + 1); // overtype, that is, jump over return Result.STOP; } - UnindentingInsertHandler.unindentAsNeeded(project, editor, file); + PyUnindentingInsertHandler.unindentAsNeeded(project, editor, file); } return Result.CONTINUE; // the default diff --git a/python/src/com/jetbrains/python/codeInsight/PyKeywords.java b/python/src/com/jetbrains/python/codeInsight/PyKeywords.java new file mode 100644 index 000000000000..458715fbf6c4 --- /dev/null +++ b/python/src/com/jetbrains/python/codeInsight/PyKeywords.java @@ -0,0 +1,64 @@ +/* + * 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.jetbrains.python.codeInsight; + +/** + * @author traff + */ +public class PyKeywords { + public static final String DEF = "def"; + public static final String CLASS = "class"; + + public static final String IF = "if"; + public static final String ELSE = "else"; + public static final String ELIF = "elif"; + + public static final String TRY = "try"; + public static final String EXCEPT = "except"; + public static final String FINALLY = "finally"; + + public static final String WHILE = "while"; + + public static final String FOR = "for"; + public static final String WITH = "with"; + public static final String AS = "as"; + + public static final String ASSERT = "assert"; + public static final String DEL = "del"; + public static final String EXEC = "exec"; + public static final String FROM = "from"; + public static final String IMPORT = "import"; + public static final String RAISE = "raise"; + public static final String PRINT = "print"; + public static final String BREAK = "break"; + public static final String CONTINUE = "continue"; + public static final String GLOBAL = "global"; + public static final String RETURN = "return"; + public static final String YIELD = "yield"; + public static final String NONLOCAL = "nonlocal"; + + public static final String AND = "and"; + public static final String OR = "or"; + public static final String IS = "is"; + public static final String IN = "in"; + public static final String NOT = "not"; + + public static final String LAMBDA = "lambda"; + + public static final String TRUE = "True"; + public static final String FALSE = "False"; + public static final String NONE = "None"; +} diff --git a/python/src/com/jetbrains/python/codeInsight/UnindentingInsertHandler.java b/python/src/com/jetbrains/python/codeInsight/PyUnindentingInsertHandler.java index cd2902666acb..c6a69d72e275 100644 --- a/python/src/com/jetbrains/python/codeInsight/UnindentingInsertHandler.java +++ b/python/src/com/jetbrains/python/codeInsight/PyUnindentingInsertHandler.java @@ -34,10 +34,10 @@ import com.jetbrains.python.psi.PyTryExceptStatement; * User: dcheryasov * Date: Mar 2, 2010 6:48:40 PM */ -public class UnindentingInsertHandler implements InsertHandler<PythonLookupElement> { - public final static UnindentingInsertHandler INSTANCE = new UnindentingInsertHandler(); +public class PyUnindentingInsertHandler implements InsertHandler<PythonLookupElement> { + public final static PyUnindentingInsertHandler INSTANCE = new PyUnindentingInsertHandler(); - private UnindentingInsertHandler() { + private PyUnindentingInsertHandler() { } public void handleInsert(InsertionContext context, PythonLookupElement item) { @@ -46,6 +46,7 @@ public class UnindentingInsertHandler implements InsertHandler<PythonLookupEleme /** * Unindent current line to be flush with a starting part, detecting the part if necessary. + * * @param project * @param editor * @param file @@ -56,7 +57,9 @@ public class UnindentingInsertHandler implements InsertHandler<PythonLookupEleme final Document document = editor.getDocument(); int offset = editor.getCaretModel().getOffset(); CharSequence text = document.getCharsSequence(); - if (offset >= text.length()) offset = text.length() - 1; + if (offset >= text.length()) { + offset = text.length() - 1; + } int line_start_offset = document.getLineStartOffset(document.getLineNumber(offset)); int nonspace_offset = findBeginning(line_start_offset, text); @@ -64,26 +67,29 @@ public class UnindentingInsertHandler implements InsertHandler<PythonLookupEleme Class<? extends PsiElement> parentClass = null; - int last_offset = nonspace_offset + "finally".length(); // the longest of all + int last_offset = nonspace_offset + PyKeywords.FINALLY.length(); // the longest of all if (last_offset > offset) last_offset = offset; int local_length = last_offset - nonspace_offset + 1; if (local_length > 0) { - String piece = text.subSequence(nonspace_offset, last_offset+1).toString(); - final int else_len = "else".length(); + String piece = text.subSequence(nonspace_offset, last_offset + 1).toString(); + final int else_len = PyKeywords.ELSE.length(); if (local_length >= else_len) { - if ((piece.startsWith("else") || piece.startsWith("elif")) && (else_len == piece.length() || piece.charAt(else_len) < 'a' || piece.charAt(else_len) > 'z')) { + if ((piece.startsWith(PyKeywords.ELSE) || piece.startsWith(PyKeywords.ELIF)) && + (else_len == piece.length() || piece.charAt(else_len) < 'a' || piece.charAt(else_len) > 'z')) { parentClass = PyStatementWithElse.class; } } - final int except_len = "except".length(); + final int except_len = PyKeywords.EXCEPT.length(); if (local_length >= except_len) { - if (piece.startsWith("except") && (except_len == piece.length() || piece.charAt(except_len) < 'a' || piece.charAt(except_len) > 'z')) { + if (piece.startsWith(PyKeywords.EXCEPT) && + (except_len == piece.length() || piece.charAt(except_len) < 'a' || piece.charAt(except_len) > 'z')) { parentClass = PyTryExceptStatement.class; } } - final int finally_len = "finally".length(); + final int finally_len = PyKeywords.FINALLY.length(); if (local_length >= finally_len) { - if (piece.startsWith("finally") && (finally_len == piece.length() || piece.charAt(finally_len) < 'a' || piece.charAt(finally_len) > 'z')) { + if (piece.startsWith(PyKeywords.FINALLY) && + (finally_len == piece.length() || piece.charAt(finally_len) < 'a' || piece.charAt(finally_len) > 'z')) { parentClass = PyTryExceptStatement.class; } } @@ -94,14 +100,17 @@ public class UnindentingInsertHandler implements InsertHandler<PythonLookupEleme PsiDocumentManager.getInstance(project).commitDocument(document); // reparse - PsiElement token = file.findElementAt(offset-2); // -1 is our ':'; -2 is even safer. + PsiElement token = file.findElementAt(offset - 2); // -1 is our ':'; -2 is even safer. PsiElement outer = PsiTreeUtil.getParentOfType(token, parentClass); if (outer != null) { int outer_offset = outer.getTextOffset(); int outer_indent = outer_offset - document.getLineStartOffset(document.getLineNumber(outer_offset)); assert outer_indent >= 0; int current_indent = nonspace_offset - line_start_offset; - EditorActionUtil.indentLine(project, editor, document.getLineNumber(offset), outer_indent - current_indent); + int indent = outer_indent - current_indent; + EditorActionUtil.indentLine(project, editor, document.getLineNumber(offset), editor.getSettings().isUseTabCharacter(project) + ? indent * editor.getSettings().getTabSize(project) + : indent); return true; } return false; diff --git a/python/src/com/jetbrains/python/codeInsight/completion/PyKeywordCompletionContributor.java b/python/src/com/jetbrains/python/codeInsight/completion/PyKeywordCompletionContributor.java index 44ef899aeff9..6d7202457e83 100644 --- a/python/src/com/jetbrains/python/codeInsight/completion/PyKeywordCompletionContributor.java +++ b/python/src/com/jetbrains/python/codeInsight/completion/PyKeywordCompletionContributor.java @@ -33,7 +33,8 @@ import com.intellij.util.ProcessingContext; import com.jetbrains.python.PyNames; import com.jetbrains.python.PyTokenTypes; import com.jetbrains.python.PythonLanguage; -import com.jetbrains.python.codeInsight.UnindentingInsertHandler; +import com.jetbrains.python.codeInsight.PyKeywords; +import com.jetbrains.python.codeInsight.PyUnindentingInsertHandler; import com.jetbrains.python.documentation.doctest.PyDocstringFile; import com.jetbrains.python.psi.*; import org.jetbrains.annotations.NonNls; @@ -286,9 +287,9 @@ public class PyKeywordCompletionContributor extends CompletionContributor { psiElement() .inside(PyConditionalStatementPart.class) .andOr( - psiElement().afterLeaf(psiElement().withText("if")), - psiElement().afterLeaf(psiElement().withText("elif")), - psiElement().afterLeaf(psiElement().withText("while")) + psiElement().afterLeaf(psiElement().withText(PyKeywords.IF)), + psiElement().afterLeaf(psiElement().withText(PyKeywords.ELIF)), + psiElement().afterLeaf(psiElement().withText(PyKeywords.WHILE)) ); private static final PsiElementPattern.Capture<PsiElement> IN_IMPORT_STMT = @@ -378,8 +379,8 @@ public class PyKeywordCompletionContributor extends CompletionContributor { protected void addCompletions( @NotNull final CompletionParameters parameters, final ProcessingContext context, @NotNull final CompletionResultSet result ) { - putKeywords(result, TailType.NONE, "def", "class", "for", "if", "while", "with"); - putKeywords(result, TailType.CASE_COLON, "try"); + putKeywords(result, TailType.NONE, PyKeywords.DEF, PyKeywords.CLASS, PyKeywords.FOR, PyKeywords.IF, PyKeywords.WHILE, PyKeywords.WITH); + putKeywords(result, TailType.CASE_COLON, PyKeywords.TRY); } } ); @@ -402,13 +403,13 @@ public class PyKeywordCompletionContributor extends CompletionContributor { protected void addCompletions( @NotNull final CompletionParameters parameters, final ProcessingContext context, @NotNull final CompletionResultSet result ) { - putKeywords(result, TailType.SPACE, "assert", "del", "exec", "from", "import", "raise"); + putKeywords(result, TailType.SPACE, PyKeywords.ASSERT, PyKeywords.DEL, PyKeywords.EXEC, PyKeywords.FROM, PyKeywords.IMPORT, PyKeywords.RAISE); putKeywords(result, TailType.NONE, PyNames.PASS); } } ); - extend(CompletionType.BASIC, inStatement.andNot(PY3K), new PyKeywordCompletionProvider(TailType.SPACE, "print")); + extend(CompletionType.BASIC, inStatement.andNot(PY3K), new PyKeywordCompletionProvider(TailType.SPACE, PyKeywords.PRINT)); } private void addBreak() { @@ -421,7 +422,7 @@ public class PyKeywordCompletionContributor extends CompletionContributor { .andNot(IN_ARG_LIST) .and(IN_LOOP) , - new PyKeywordCompletionProvider(TailType.NONE, "break") + new PyKeywordCompletionProvider(TailType.NONE, PyKeywords.BREAK) ); } @@ -436,7 +437,7 @@ public class PyKeywordCompletionContributor extends CompletionContributor { .andNot(IN_FINALLY_NO_LOOP) .and(IN_LOOP) , - new PyKeywordCompletionProvider(TailType.NONE, "continue") + new PyKeywordCompletionProvider(TailType.NONE, PyKeywords.CONTINUE) ); } @@ -448,7 +449,7 @@ public class PyKeywordCompletionContributor extends CompletionContributor { .and(IN_BEGIN_STMT) .andNot(AFTER_QUALIFIER) , - new PyKeywordCompletionProvider("global", "return", "yield") + new PyKeywordCompletionProvider(PyKeywords.GLOBAL, PyKeywords.RETURN, PyKeywords.YIELD) ); extend( @@ -459,7 +460,7 @@ public class PyKeywordCompletionContributor extends CompletionContributor { .and(PY3K) .andNot(AFTER_QUALIFIER) , - new PyKeywordCompletionProvider("nonlocal") + new PyKeywordCompletionProvider(PyKeywords.NONLOCAL) ); } @@ -471,7 +472,7 @@ public class PyKeywordCompletionContributor extends CompletionContributor { .andOr(IN_IF_BODY, AFTER_IF) .andNot(AFTER_QUALIFIER).andNot(IN_STRING_LITERAL) , - new PyKeywordCompletionProvider(TailType.NONE, UnindentingInsertHandler.INSTANCE, "elif")); + new PyKeywordCompletionProvider(TailType.NONE, PyUnindentingInsertHandler.INSTANCE, PyKeywords.ELIF)); } private void addWithinTry() { @@ -487,8 +488,8 @@ public class PyKeywordCompletionContributor extends CompletionContributor { protected void addCompletions( @NotNull final CompletionParameters parameters, final ProcessingContext context, @NotNull final CompletionResultSet result ) { - putKeyword("except", UnindentingInsertHandler.INSTANCE, TailType.NONE, result); - putKeyword("finally", UnindentingInsertHandler.INSTANCE, TailType.CASE_COLON, result); + putKeyword(PyKeywords.EXCEPT, PyUnindentingInsertHandler.INSTANCE, TailType.NONE, result); + putKeyword(PyKeywords.FINALLY, PyUnindentingInsertHandler.INSTANCE, TailType.CASE_COLON, result); } } ); @@ -502,7 +503,7 @@ public class PyKeywordCompletionContributor extends CompletionContributor { .andOr(IN_COND_STMT, IN_EXCEPT_BODY, AFTER_COND_STMT_NO_ELSE, AFTER_LOOP_NO_ELSE, AFTER_EXCEPT) .andNot(AFTER_QUALIFIER).andNot(IN_STRING_LITERAL) , - new PyKeywordCompletionProvider(TailType.CASE_COLON, UnindentingInsertHandler.INSTANCE, "else")); + new PyKeywordCompletionProvider(TailType.CASE_COLON, PyUnindentingInsertHandler.INSTANCE, PyKeywords.ELSE)); } private void addInfixOperators() { @@ -516,7 +517,7 @@ public class PyKeywordCompletionContributor extends CompletionContributor { .andNot(AFTER_QUALIFIER). andNot(IN_STRING_LITERAL).and(IN_BEGIN_STMT) , - new PyKeywordCompletionProvider("and", "or", "is", "in") + new PyKeywordCompletionProvider(PyKeywords.AND, PyKeywords.OR, PyKeywords.IS, PyKeywords.IN) ); } @@ -530,7 +531,7 @@ public class PyKeywordCompletionContributor extends CompletionContributor { .andNot(IN_FUNCTION_HEADER) .andNot(AFTER_QUALIFIER).andNot(IN_STRING_LITERAL) , - new PyKeywordCompletionProvider("not", "lambda") + new PyKeywordCompletionProvider(PyKeywords.NOT, PyKeywords.LAMBDA) ); } @@ -545,7 +546,7 @@ public class PyKeywordCompletionContributor extends CompletionContributor { .andNot(AFTER_QUALIFIER) .andNot(IN_FUNCTION_HEADER) , - new PyKeywordCompletionProvider(TailType.NONE, "True", "False", "None") + new PyKeywordCompletionProvider(TailType.NONE, PyKeywords.TRUE, PyKeywords.FALSE, PyKeywords.NONE) ); } @@ -556,7 +557,7 @@ public class PyKeywordCompletionContributor extends CompletionContributor { .andOr(IN_IMPORT_AFTER_REF, IN_WITH_AFTER_REF, IN_EXCEPT_AFTER_REF) .andNot(AFTER_QUALIFIER) , - new PyKeywordCompletionProvider("as") + new PyKeywordCompletionProvider(PyKeywords.AS) ); } @@ -567,7 +568,7 @@ public class PyKeywordCompletionContributor extends CompletionContributor { .and(IN_FROM_IMPORT_AFTER_REF) .andNot(AFTER_QUALIFIER) , - new PyKeywordCompletionProvider("import") + new PyKeywordCompletionProvider(PyKeywords.IMPORT) ); } @@ -603,9 +604,9 @@ public class PyKeywordCompletionContributor extends CompletionContributor { .withLanguage(PythonLanguage.getInstance()) .afterLeafSkipping(psiElement().whitespace(), psiElement().inside(psiElement(PyConditionalExpression.class)) - .and(psiElement().afterLeaf("if"))) + .and(psiElement().afterLeaf(PyKeywords.IF))) , - new PyKeywordCompletionProvider(TailType.SPACE, "else")); + new PyKeywordCompletionProvider(TailType.SPACE, PyKeywords.ELSE)); } private void addRaiseFrom() { @@ -614,7 +615,7 @@ public class PyKeywordCompletionContributor extends CompletionContributor { .withLanguage(PythonLanguage.getInstance()) .and(PY3K) .afterLeaf(psiElement().inside(PyRaiseStatement.class)), - new PyKeywordCompletionProvider("from")); + new PyKeywordCompletionProvider(PyKeywords.FROM)); } private void addYieldFrom() { @@ -623,7 +624,7 @@ public class PyKeywordCompletionContributor extends CompletionContributor { .withLanguage(PythonLanguage.getInstance()) .and(PY3K) .afterLeaf(psiElement().withElementType(PyTokenTypes.YIELD_KEYWORD)), - new PyKeywordCompletionProvider("from")); + new PyKeywordCompletionProvider(PyKeywords.FROM)); } public PyKeywordCompletionContributor() { @@ -654,14 +655,14 @@ public class PyKeywordCompletionContributor extends CompletionContributor { .withLanguage(PythonLanguage.getInstance()) .inside(psiElement(PySequenceExpression.class)) .andNot(psiElement().afterLeaf(or(psiElement(PyTokenTypes.LBRACE), psiElement(PyTokenTypes.LBRACKET), psiElement(PyTokenTypes.LPAR)))), - new PyKeywordCompletionProvider("for")); + new PyKeywordCompletionProvider(PyKeywords.FOR)); } private void addInToFor() { extend(CompletionType.BASIC, psiElement() .withLanguage(PythonLanguage.getInstance()) - .and(psiElement()).afterLeaf(psiElement().afterLeaf("for")), + .and(psiElement()).afterLeaf(psiElement().afterLeaf(PyKeywords.FOR)), new PyKeywordCompletionProvider("in")); } diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/ImportToggleAliasIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/ImportToggleAliasIntention.java index f9f1ee19435a..6b79d2104f44 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/ImportToggleAliasIntention.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/ImportToggleAliasIntention.java @@ -181,13 +181,16 @@ public class ImportToggleAliasIntention implements IntentionAction { InjectedLanguageManager.getInstance(project).getInjectedPsiFiles(host); if (files != null) { for (Pair<PsiElement, TextRange> pair : files) { - final ScopeOwner scopeOwner = (ScopeOwner)pair.getFirst(); - PsiTreeUtil.processElements(scopeOwner, new PsiElementProcessor() { - public boolean execute(@NotNull PsiElement element) { - getReferences(element); - return true; - } - }); + final PsiElement first = pair.getFirst(); + if (first instanceof ScopeOwner) { + final ScopeOwner scopeOwner = (ScopeOwner)first; + PsiTreeUtil.processElements(scopeOwner, new PsiElementProcessor() { + public boolean execute(@NotNull PsiElement element) { + getReferences(element); + return true; + } + }); + } } } } diff --git a/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java index cf6b9ef19301..9e2989e09650 100644 --- a/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java @@ -37,7 +37,8 @@ import java.util.Set; * @author yole */ public class PyStdlibTypeProvider extends PyTypeProviderBase { - private static final Set<String> OPEN_FUNCTIONS = ImmutableSet.of("__builtin__.open", "io.open", "os.fdopen"); + private static final Set<String> OPEN_FUNCTIONS = ImmutableSet.of("__builtin__.open", "io.open", "os.fdopen", + "pathlib.Path.open"); private static final String BINARY_FILE_TYPE = "io.FileIO[bytes]"; private static final String TEXT_FILE_TYPE = "io.TextIOWrapper[unicode]"; @@ -134,7 +135,10 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase { for (Map.Entry<PyExpression, PyNamedParameter> entry : arguments.entrySet()) { final PyNamedParameter parameter = entry.getValue(); if ("mode".equals(parameter.getName())) { - final PyExpression argument = entry.getKey(); + PyExpression argument = entry.getKey(); + if (argument instanceof PyKeywordArgument) { + argument = ((PyKeywordArgument)argument).getValueExpression(); + } if (argument instanceof PyStringLiteralExpression) { mode = ((PyStringLiteralExpression)argument).getStringValue(); break; diff --git a/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsClassMembersProvider.java b/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsClassMembersProvider.java index a35d8fd74444..5ac7fc10751c 100644 --- a/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsClassMembersProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsClassMembersProvider.java @@ -22,6 +22,7 @@ import com.jetbrains.python.psi.PyFunction; import com.jetbrains.python.psi.PyTargetExpression; import com.jetbrains.python.psi.types.PyClassMembersProviderBase; import com.jetbrains.python.psi.types.PyClassType; +import com.jetbrains.python.psi.types.PyOverridingAncestorsClassMembersProvider; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -33,7 +34,7 @@ import java.util.List; /** * @author vlan */ -public class PyUserSkeletonsClassMembersProvider extends PyClassMembersProviderBase { +public class PyUserSkeletonsClassMembersProvider extends PyClassMembersProviderBase implements PyOverridingAncestorsClassMembersProvider { @NotNull @Override public Collection<PyDynamicMember> getMembers(@NotNull PyClassType classType, PsiElement location) { diff --git a/python/src/com/jetbrains/python/console/PydevConsoleRunner.java b/python/src/com/jetbrains/python/console/PydevConsoleRunner.java index 947b3c958e78..c7b9ceb88d2e 100644 --- a/python/src/com/jetbrains/python/console/PydevConsoleRunner.java +++ b/python/src/com/jetbrains/python/console/PydevConsoleRunner.java @@ -50,6 +50,7 @@ import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.io.StreamUtil; import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.encoding.EncodingManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.impl.source.tree.FileElement; @@ -61,16 +62,11 @@ import com.intellij.util.IJSwingUtilities; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.net.NetUtils; import com.intellij.util.ui.UIUtil; -import com.intellij.xdebugger.impl.frame.XStandaloneVariablesView; import com.jetbrains.python.PythonHelpersLocator; import com.jetbrains.python.console.completion.PydevConsoleElement; import com.jetbrains.python.console.parsing.PythonConsoleData; import com.jetbrains.python.console.pydev.ConsoleCommunication; -import com.jetbrains.python.console.pydev.ConsoleCommunicationListener; -import com.jetbrains.python.debugger.PyDebuggerEditorsProvider; import com.jetbrains.python.debugger.PySourcePosition; -import com.jetbrains.python.debugger.PyStackFrame; -import com.jetbrains.python.debugger.PyStackFrameInfo; import com.jetbrains.python.remote.PythonRemoteInterpreterManager; import com.jetbrains.python.run.ProcessRunner; import com.jetbrains.python.run.PythonCommandLineState; @@ -84,8 +80,8 @@ import org.jetbrains.annotations.Nullable; import java.awt.event.KeyEvent; import java.io.File; import java.io.IOException; +import java.nio.charset.Charset; import java.util.*; -import java.util.List; import static com.jetbrains.python.sdk.PythonEnvUtil.setPythonIOEncoding; import static com.jetbrains.python.sdk.PythonEnvUtil.setPythonUnbuffered; @@ -134,7 +130,11 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonC } public static Map<String, String> addDefaultEnvironments(Sdk sdk, Map<String, String> envs) { - setPythonIOEncoding(setPythonUnbuffered(envs), "utf-8"); + Charset defaultCharset = EncodingManager.getInstance().getDefaultCharset(); + + final String encoding = defaultCharset != null ? defaultCharset.name() : "utf-8"; + setPythonIOEncoding(setPythonUnbuffered(envs), encoding); + PythonSdkFlavor.initPythonPath(envs, true, PythonCommandLineState.getAddedPaths(sdk)); return envs; } @@ -304,7 +304,8 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonC PYDEV_PYDEVCONSOLE_PY) .getPath(), PySourcePosition.isWindowsPath( - data.getInterpreterPath()))); + data.getInterpreterPath()) + )); commandLine.getParametersList().set(2, "0"); commandLine.getParametersList().set(3, "0"); @@ -570,7 +571,7 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonC new PydevConsoleExecuteActionHandler(getConsoleView(), getProcessHandler(), myPydevConsoleCommunication); myConsoleExecuteActionHandler.setEnabled(false); myHistoryController = new ConsoleHistoryController(myConsoleType.getTypeId(), "", getLanguageConsole(), - myConsoleExecuteActionHandler.getConsoleHistoryModel()); + myConsoleExecuteActionHandler.getConsoleHistoryModel()); myHistoryController.install(); return myConsoleExecuteActionHandler; } diff --git a/python/src/com/jetbrains/python/documentation/DocStringParameterReference.java b/python/src/com/jetbrains/python/documentation/DocStringParameterReference.java index 9e4e63d8dcf4..5f7a2ee2bb0f 100644 --- a/python/src/com/jetbrains/python/documentation/DocStringParameterReference.java +++ b/python/src/com/jetbrains/python/documentation/DocStringParameterReference.java @@ -38,9 +38,9 @@ import java.util.Set; * @author yole */ public class DocStringParameterReference extends PsiReferenceBase<PsiElement> implements PsiReferenceEx { - private final String myType; + private final StructuredDocStringBase.ReferenceType myType; - public DocStringParameterReference(PsiElement element, TextRange range, String refType) { + public DocStringParameterReference(PsiElement element, TextRange range, StructuredDocStringBase.ReferenceType refType) { super(element, range); myType = refType; } @@ -55,26 +55,42 @@ public class DocStringParameterReference extends PsiReferenceBase<PsiElement> im final PyFunction init = ((PyClass)owner).findMethodByName(PyNames.INIT, false); if (init != null) { PsiElement element = resolveParameter(init); - if (element == null) - element = resolveClassVariable(owner); + if (element == null && (myType.equals(StructuredDocStringBase.ReferenceType.CLASS_VARIABLE) || + myType.equals(StructuredDocStringBase.ReferenceType.PARAMETER_TYPE))) + element = resolveClassVariable((PyClass)owner); + if (element == null && (myType.equals(StructuredDocStringBase.ReferenceType.INSTANCE_VARIABLE) || + myType.equals(StructuredDocStringBase.ReferenceType.PARAMETER_TYPE))) + element = resolveInstanceVariable((PyClass)owner); return element; } else { - return resolveClassVariable(owner); + if (myType.equals(StructuredDocStringBase.ReferenceType.CLASS_VARIABLE) || + myType.equals(StructuredDocStringBase.ReferenceType.PARAMETER_TYPE)) + return resolveClassVariable((PyClass)owner); + if (myType.equals(StructuredDocStringBase.ReferenceType.INSTANCE_VARIABLE) || + myType.equals(StructuredDocStringBase.ReferenceType.PARAMETER_TYPE)) + return resolveInstanceVariable((PyClass)owner); } } return null; } @Nullable - private PsiElement resolveClassVariable(final PyDocStringOwner owner) { - final PyStatementList statementList = ((PyClass)owner).getStatementList(); - for (PsiElement element : statementList.getChildren()) { - if (element instanceof PyAssignmentStatement) { - final PyExpression[] targets = ((PyAssignmentStatement)element).getTargets(); - if (targets.length > 0 && targets[0].getText().equals(getCanonicalText())) - return targets[0]; - } + private PsiElement resolveInstanceVariable(final PyClass owner) { + final List<PyTargetExpression> attributes = owner.getInstanceAttributes(); + for (PyTargetExpression element : attributes) { + if (getCanonicalText().equals(element.getName())) + return element; + } + return null; + } + + @Nullable + private PsiElement resolveClassVariable(@NotNull final PyClass owner) { + final List<PyTargetExpression> attributes = owner.getClassAttributes(); + for (PyTargetExpression element : attributes) { + if (getCanonicalText().equals(element.getName())) + return element; } return null; } @@ -123,7 +139,7 @@ public class DocStringParameterReference extends PsiReferenceBase<PsiElement> im return ArrayUtil.EMPTY_OBJECT_ARRAY; } - public String getType() { + public StructuredDocStringBase.ReferenceType getType() { return myType; } diff --git a/python/src/com/jetbrains/python/documentation/DocStringReferenceProvider.java b/python/src/com/jetbrains/python/documentation/DocStringReferenceProvider.java index 2891726d54cf..d1d8033b15b0 100644 --- a/python/src/com/jetbrains/python/documentation/DocStringReferenceProvider.java +++ b/python/src/com/jetbrains/python/documentation/DocStringReferenceProvider.java @@ -61,16 +61,22 @@ public class DocStringReferenceProvider extends PsiReferenceProvider { if (docString != null) { result.addAll(referencesFromNames(element, offset, docString, docString.getTagArguments(StructuredDocStringBase.PARAM_TAGS), - StructuredDocStringBase.PARAMETER)); + StructuredDocStringBase.ReferenceType.PARAMETER)); result.addAll(referencesFromNames(element, offset, docString, docString.getTagArguments(StructuredDocStringBase.PARAM_TYPE_TAGS), - StructuredDocStringBase.PARAMETER_TYPE)); + StructuredDocStringBase.ReferenceType.PARAMETER_TYPE)); result.addAll(referencesFromNames(element, offset, docString, - docString.getKeywordArgumentSubstrings(), StructuredDocStringBase.KEYWORD)); + docString.getKeywordArgumentSubstrings(), StructuredDocStringBase.ReferenceType.KEYWORD)); result.addAll(referencesFromNames(element, offset, docString, - docString.getTagArguments(StructuredDocStringBase.VARIABLE_TAGS), - StructuredDocStringBase.VARIABLE)); + docString.getTagArguments("var"), + StructuredDocStringBase.ReferenceType.VARIABLE)); + result.addAll(referencesFromNames(element, offset, docString, + docString.getTagArguments("cvar"), + StructuredDocStringBase.ReferenceType.CLASS_VARIABLE)); + result.addAll(referencesFromNames(element, offset, docString, + docString.getTagArguments("ivar"), + StructuredDocStringBase.ReferenceType.INSTANCE_VARIABLE)); result.addAll(returnTypes(element, docString, offset)); } return result.toArray(new PsiReference[result.size()]); @@ -94,7 +100,7 @@ public class DocStringReferenceProvider extends PsiReferenceProvider { int offset, StructuredDocString docString, List<Substring> paramNames, - String refType) { + StructuredDocStringBase.ReferenceType refType) { List<PsiReference> result = new ArrayList<PsiReference>(); for (Substring name : paramNames) { final String s = name.toString(); @@ -102,7 +108,7 @@ public class DocStringReferenceProvider extends PsiReferenceProvider { final TextRange range = name.getTextRange().shiftRight(offset); result.add(new DocStringParameterReference(element, range, refType)); } - if (refType.equals(StructuredDocStringBase.PARAMETER_TYPE)) { + if (refType.equals(StructuredDocStringBase.ReferenceType.PARAMETER_TYPE)) { final Substring type = docString.getParamTypeSubstring(s); if (type != null) { result.addAll(parseTypeReferences(element, type, offset)); diff --git a/python/src/com/jetbrains/python/documentation/PyDocumentationBuilder.java b/python/src/com/jetbrains/python/documentation/PyDocumentationBuilder.java index ec4becc5cc1f..4c3432c7e84a 100644 --- a/python/src/com/jetbrains/python/documentation/PyDocumentationBuilder.java +++ b/python/src/com/jetbrains/python/documentation/PyDocumentationBuilder.java @@ -95,7 +95,7 @@ class PyDocumentationBuilder { PyType type = context.getType(qual); if (type instanceof PyClassType) { cls = ((PyClassType)type).getPyClass(); - Property property = cls.findProperty(elementName); + Property property = cls.findProperty(elementName, true); if (property != null) { is_property = true; final AccessDirection dir = AccessDirection.of((PyElement)outer); diff --git a/python/src/com/jetbrains/python/documentation/StructuredDocStringBase.java b/python/src/com/jetbrains/python/documentation/StructuredDocStringBase.java index 0e3d9e35385e..9f6cecba7a84 100644 --- a/python/src/com/jetbrains/python/documentation/StructuredDocStringBase.java +++ b/python/src/com/jetbrains/python/documentation/StructuredDocStringBase.java @@ -50,11 +50,9 @@ public abstract class StructuredDocStringBase implements StructuredDocString { public static String[] RAISES_TAGS = new String[] { "raises", "raise", "except", "exception" }; public static String[] RETURN_TAGS = new String[] { "return", "returns" }; - public static String PARAMETER = "parameter"; + public enum ReferenceType {PARAMETER, PARAMETER_TYPE, KEYWORD, VARIABLE, CLASS_VARIABLE, INSTANCE_VARIABLE} + public static String TYPE = "type"; - public static String PARAMETER_TYPE = "parameter_type"; - public static String KEYWORD = "keyword"; - public static String VARIABLE = "variable"; protected StructuredDocStringBase(@NotNull String docStringText, String tagPrefix) { final Substring docString = new Substring(docStringText); diff --git a/python/src/com/jetbrains/python/formatter/PyBlock.java b/python/src/com/jetbrains/python/formatter/PyBlock.java index 2a7343dda3ed..8afd35e009f2 100644 --- a/python/src/com/jetbrains/python/formatter/PyBlock.java +++ b/python/src/com/jetbrains/python/formatter/PyBlock.java @@ -153,7 +153,9 @@ public class PyBlock implements ASTBlock { if (ourListElementTypes.contains(parentType)) { // wrapping in non-parenthesized tuple expression is not allowed (PY-1792) if ((parentType != PyElementTypes.TUPLE_EXPRESSION || grandparentType == PyElementTypes.PARENTHESIZED_EXPRESSION) && - !ourBrackets.contains(childType) && childType != PyTokenTypes.COMMA && !isSliceOperand(child) /*&& !isSubscriptionOperand(child)*/) { + !ourBrackets.contains(childType) && + childType != PyTokenTypes.COMMA && + !isSliceOperand(child) /*&& !isSubscriptionOperand(child)*/) { wrap = Wrap.createWrap(WrapType.NORMAL, true); } if (needListAlignment(child) && !isEmptyList(_node.getPsi())) { @@ -161,8 +163,9 @@ public class PyBlock implements ASTBlock { } } else if (parentType == PyElementTypes.BINARY_EXPRESSION && - (PythonDialectsTokenSetProvider.INSTANCE.getExpressionTokens().contains(childType) || PyTokenTypes.OPERATIONS.contains(childType))) { - if (isInControlStatement() ) { + (PythonDialectsTokenSetProvider.INSTANCE.getExpressionTokens().contains(childType) || + PyTokenTypes.OPERATIONS.contains(childType))) { + if (isInControlStatement()) { PyParenthesizedExpression parens = PsiTreeUtil.getParentOfType(_node.getPsi(), PyParenthesizedExpression.class, true, PyStatementPart.class); childIndent = parens != null ? Indent.getNormalIndent() : Indent.getContinuationIndent(); @@ -196,7 +199,7 @@ public class PyBlock implements ASTBlock { } } else if (parentType == PyElementTypes.DICT_LITERAL_EXPRESSION || parentType == PyElementTypes.SET_LITERAL_EXPRESSION || - parentType == PyElementTypes.SET_COMP_EXPRESSION || parentType == PyElementTypes.DICT_COMP_EXPRESSION) { + parentType == PyElementTypes.SET_COMP_EXPRESSION || parentType == PyElementTypes.DICT_COMP_EXPRESSION) { if (childType == PyTokenTypes.RBRACE || !hasLineBreaksBefore(child, 1)) { childIndent = Indent.getNoneIndent(); } @@ -210,18 +213,22 @@ public class PyBlock implements ASTBlock { } } else if (parentType == PyElementTypes.FROM_IMPORT_STATEMENT) { - if ((childType == PyElementTypes.IMPORT_ELEMENT || childType == PyTokenTypes.RPAR) && - _node.findChildByType(PyTokenTypes.LPAR) != null) { - if (myContext.getPySettings().ALIGN_MULTILINE_IMPORTS) { - childAlignment = getAlignmentForChildren(); + if (_node.findChildByType(PyTokenTypes.LPAR) != null) { + if (childType == PyElementTypes.IMPORT_ELEMENT) { + if (myContext.getPySettings().ALIGN_MULTILINE_IMPORTS) { + childAlignment = getAlignmentForChildren(); + } + else { + childIndent = Indent.getNormalIndent(); + } } - else { - childIndent = Indent.getNormalIndent(); + if (childType == PyTokenTypes.RPAR) { + childIndent = Indent.getNoneIndent(); } } } else if (parentType == PyElementTypes.KEY_VALUE_EXPRESSION) { - PyKeyValueExpression keyValue = (PyKeyValueExpression) _node.getPsi(); + PyKeyValueExpression keyValue = (PyKeyValueExpression)_node.getPsi(); if (keyValue != null && child.getPsi() == keyValue.getValue()) { childIndent = Indent.getNormalIndent(); } @@ -255,7 +262,7 @@ public class PyBlock implements ASTBlock { while (prev != null && prev.getElementType() == TokenType.WHITE_SPACE) { if (prev.getText().contains("\\") && !childIndent.equals(Indent.getContinuationIndent()) && !childIndent.equals(Indent.getContinuationIndent(true))) { - childIndent = Indent.getNormalIndent(); + childIndent = isIndentNext(child) ? Indent.getContinuationIndent() : Indent.getNormalIndent(); break; } prev = prev.getTreePrev(); @@ -264,9 +271,22 @@ public class PyBlock implements ASTBlock { return new PyBlock(this, child, childAlignment, childIndent, wrap, myContext); } + private static boolean isIndentNext(ASTNode child) { + PsiElement psi = PsiTreeUtil.getParentOfType(child.getPsi(), PyStatement.class); + + return psi instanceof PyIfStatement || + psi instanceof PyForStatement || + psi instanceof PyWithStatement || + psi instanceof PyClass || + psi instanceof PyFunction || + psi instanceof PyTryExceptStatement || + psi instanceof PyElsePart || + psi instanceof PyIfPart; + } + private static boolean isSubscriptionOperand(ASTNode child) { return child.getTreeParent().getElementType() == PyElementTypes.SUBSCRIPTION_EXPRESSION && - child.getPsi() == ((PySubscriptionExpression) child.getTreeParent().getPsi()).getOperand(); + child.getPsi() == ((PySubscriptionExpression)child.getTreeParent().getPsi()).getOperand(); } private boolean isInControlStatement() { @@ -285,10 +305,10 @@ public class PyBlock implements ASTBlock { private static boolean isEmptyList(PsiElement psi) { if (psi instanceof PyDictLiteralExpression) { - return ((PyDictLiteralExpression) psi).getElements().length == 0; + return ((PyDictLiteralExpression)psi).getElements().length == 0; } if (psi instanceof PySequenceExpression) { - return ((PySequenceExpression) psi).getElements().length == 0; + return ((PySequenceExpression)psi).getElements().length == 0; } return false; } @@ -322,7 +342,9 @@ public class PyBlock implements ASTBlock { } if (PyTokenTypes.CLOSE_BRACES.contains(childType)) { ASTNode prevNonSpace = findPrevNonSpaceNode(child); - if (prevNonSpace != null && prevNonSpace.getElementType() == PyTokenTypes.COMMA && myContext.getMode() == FormattingMode.ADJUST_INDENT) { + if (prevNonSpace != null && + prevNonSpace.getElementType() == PyTokenTypes.COMMA && + myContext.getMode() == FormattingMode.ADJUST_INDENT) { return true; } return false; @@ -358,7 +380,8 @@ public class PyBlock implements ASTBlock { private static ASTNode findPrevNonSpaceNode(ASTNode node) { do { node = node.getTreePrev(); - } while(node != null && (node.getElementType() == TokenType.WHITE_SPACE || PyTokenTypes.WHITESPACE.contains(node.getElementType()))); + } + while (node != null && (node.getElementType() == TokenType.WHITE_SPACE || PyTokenTypes.WHITESPACE.contains(node.getElementType()))); return node; } @@ -372,7 +395,7 @@ public class PyBlock implements ASTBlock { if (node != null && node.getElementType() == TokenType.WHITE_SPACE) { String prevNodeText = node.getText(); int count = 0; - for(int i=0; i<prevNodeText.length(); i++) { + for (int i = 0; i < prevNodeText.length(); i++) { if (prevNodeText.charAt(i) == '\n') { count++; if (count == minCount) { @@ -429,10 +452,32 @@ public class PyBlock implements ASTBlock { return Spacing.createSpacing(0, 0, 1, true, myContext.getSettings().KEEP_BLANK_LINES_IN_CODE); } } + + if ((node1.getElementType() == PyElementTypes.FUNCTION_DECLARATION || node1.getElementType() == PyElementTypes.CLASS_DECLARATION) + && _node.getElementType() instanceof PyFileElementType) { + + if (psi2 instanceof PsiComment) { + final PsiElement psi3 = PsiTreeUtil.getNextSiblingOfType(psi2, PyElement.class); + + if (psi3 != null) { + IElementType type3 = psi3.getNode().getElementType(); + + if (type3 == PyElementTypes.CLASS_DECLARATION || type3 == PyElementTypes.FUNCTION_DECLARATION) { + return getBlankLinesForOption(myContext.getPySettings().BLANK_LINES_AROUND_TOP_LEVEL_CLASSES_FUNCTIONS); + } + } + } + } } return myContext.getSpacingBuilder().getSpacing(this, child1, child2); } + private Spacing getBlankLinesForOption(final int option) { + int blankLines = option + 1; + return Spacing + .createSpacing(0, 0, blankLines, myContext.getSettings().KEEP_LINE_BREAKS, myContext.getSettings().KEEP_BLANK_LINES_IN_DECLARATIONS); + } + private boolean needLineBreakInStatement() { PyStatement statement = PsiTreeUtil.getParentOfType(_node.getPsi(), PyStatement.class); if (statement != null) { @@ -614,7 +659,7 @@ public class PyBlock implements ASTBlock { } if (afterNode != null && afterNode.getElementType() == PyElementTypes.KEY_VALUE_EXPRESSION) { - PyKeyValueExpression keyValue = (PyKeyValueExpression) afterNode.getPsi(); + PyKeyValueExpression keyValue = (PyKeyValueExpression)afterNode.getPsi(); if (keyValue != null && keyValue.getValue() == null) { // incomplete return Indent.getContinuationIndent(); } diff --git a/python/src/com/jetbrains/python/inspections/PyArgumentListInspection.java b/python/src/com/jetbrains/python/inspections/PyArgumentListInspection.java index 97a5a365f2ea..6faea31eabcb 100644 --- a/python/src/com/jetbrains/python/inspections/PyArgumentListInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyArgumentListInspection.java @@ -68,32 +68,29 @@ public class PyArgumentListInspection extends PyInspection { @Override public void visitPyDecoratorList(final PyDecoratorList node) { - PyDecorator[] decos = node.getDecorators(); - for (PyDecorator deco : decos) { - if (! deco.hasArgumentList()) { - // empty arglist; deco function must have a non-kwarg first arg - PyCallExpression.PyMarkedCallee mkfunc = deco.resolveCallee(resolveWithoutImplicits()); - if (mkfunc != null && !mkfunc.isImplicitlyResolved()) { - Callable callable = mkfunc.getCallable(); - int first_param_offset = mkfunc.getImplicitOffset(); - final List<PyParameter> params = PyUtil.getParameters(callable, myTypeEvalContext); - final PyNamedParameter alleged_first_param = params.size() < first_param_offset ? - null : params.get(first_param_offset-1).getAsNamed(); - if (alleged_first_param == null || alleged_first_param.isKeywordContainer()) { - // no parameters left to pass function implicitly, or wrong param type - registerProblem(deco, PyBundle.message("INSP.func.$0.lacks.first.arg", callable.getName())); // TODO: better names for anon lambdas - } - else { - // possible unfilled params - for (int i=first_param_offset; i < params.size(); i += 1) { - PyNamedParameter par = params.get(i).getAsNamed(); - // param tuples, non-starred or non-default won't do - if (par == null || (! par.isKeywordContainer() && ! par.isPositionalContainer() && !par.hasDefaultValue())) { - String par_name; - if (par != null) par_name = par.getName(); - else par_name = "(...)"; // can't be bothered to find the first non-tuple inside it - registerProblem(deco, PyBundle.message("INSP.parameter.$0.unfilled", par_name)); - } + PyDecorator[] decorators = node.getDecorators(); + for (PyDecorator deco : decorators) { + if (deco.hasArgumentList()) continue; + final PyCallExpression.PyMarkedCallee markedCallee = deco.resolveCallee(resolveWithoutImplicits()); + if (markedCallee != null && !markedCallee.isImplicitlyResolved()) { + final Callable callable = markedCallee.getCallable(); + int firstParamOffset = markedCallee.getImplicitOffset(); + final List<PyParameter> params = PyUtil.getParameters(callable, myTypeEvalContext); + final PyNamedParameter allegedFirstParam = params.size() < firstParamOffset ? + null : params.get(firstParamOffset-1).getAsNamed(); + if (allegedFirstParam == null || allegedFirstParam.isKeywordContainer()) { + // no parameters left to pass function implicitly, or wrong param type + registerProblem(deco, PyBundle.message("INSP.func.$0.lacks.first.arg", callable.getName())); // TODO: better names for anon lambdas + } + else { // possible unfilled params + for (int i = firstParamOffset; i < params.size(); i += 1) { + final PyParameter parameter = params.get(i); + if (parameter instanceof PySingleStarParameter) continue; + final PyNamedParameter par = parameter.getAsNamed(); + // param tuples, non-starred or non-default won't do + if (par == null || (!par.isKeywordContainer() && !par.isPositionalContainer() &&!par.hasDefaultValue())) { + String parameterName = par != null ? par.getName() : "(...)"; + registerProblem(deco, PyBundle.message("INSP.parameter.$0.unfilled", parameterName)); } } } diff --git a/python/src/com/jetbrains/python/inspections/PyAttributeOutsideInitInspection.java b/python/src/com/jetbrains/python/inspections/PyAttributeOutsideInitInspection.java index c94ab8daaafb..791a1b52d38b 100644 --- a/python/src/com/jetbrains/python/inspections/PyAttributeOutsideInitInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyAttributeOutsideInitInspection.java @@ -100,7 +100,7 @@ public class PyAttributeOutsideInitInspection extends PyInspection { for (Map.Entry<String, PyTargetExpression> attribute : attributes.entrySet()) { String attributeName = attribute.getKey(); if (attributeName == null) continue; - final Property property = containingClass.findProperty(attributeName); + final Property property = containingClass.findProperty(attributeName, true); if (!attributesInInit.containsKey(attributeName) && property == null) { registerProblem(attribute.getValue(), PyBundle.message("INSP.attribute.$0.outside.init", attributeName), new PyMoveAttributeToInitQuickFix()); diff --git a/python/src/com/jetbrains/python/inspections/PyPropertyAccessInspection.java b/python/src/com/jetbrains/python/inspections/PyPropertyAccessInspection.java index 425c58519486..8e48067578cb 100644 --- a/python/src/com/jetbrains/python/inspections/PyPropertyAccessInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyPropertyAccessInspection.java @@ -86,7 +86,7 @@ public class PyPropertyAccessInspection extends PyInspection { property = myPropertyCache.get(key); } else { - property = cls.findProperty(name); + property = cls.findProperty(name, true); } myPropertyCache.put(key, property); // we store nulls, too, to know that a property does not exist if (property != null) { diff --git a/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java b/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java index 4005b42dccdc..998530a016b3 100644 --- a/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java @@ -695,7 +695,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection { if (overridesGetAttr(cls, myTypeEvalContext)) { return true; } - if (cls.findProperty(name) != null) { + if (cls.findProperty(name, true) != null) { return true; } if (PyUtil.hasUnresolvedAncestors(cls, myTypeEvalContext)) { diff --git a/python/src/com/jetbrains/python/inspections/PyUnusedLocalInspectionVisitor.java b/python/src/com/jetbrains/python/inspections/PyUnusedLocalInspectionVisitor.java index f770c5d11616..f7883535d2e6 100644 --- a/python/src/com/jetbrains/python/inspections/PyUnusedLocalInspectionVisitor.java +++ b/python/src/com/jetbrains/python/inspections/PyUnusedLocalInspectionVisitor.java @@ -207,7 +207,8 @@ public class PyUnusedLocalInspectionVisitor extends PyInspectionVisitor { owner.acceptChildren(new PyRecursiveElementVisitor(){ @Override public void visitPyCallExpression(final PyCallExpression node) { - if ("locals".equals(node.getCallee().getName())){ + final PyExpression callee = node.getCallee(); + if (callee != null && "locals".equals(callee.getName())){ throw new DontPerformException(); } node.acceptChildren(this); // look at call expr in arguments @@ -290,7 +291,7 @@ public class PyUnusedLocalInspectionVisitor extends PyInspectionVisitor { isEmpty = isEmptyFunction(func); emptyFunctions.put(func, isEmpty); } - if (isEmpty) { + if (isEmpty && !mayBeField) { continue; } } diff --git a/python/src/com/jetbrains/python/inspections/quickfix/AddFieldQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/AddFieldQuickFix.java index e888add9acac..89de355bb87a 100644 --- a/python/src/com/jetbrains/python/inspections/quickfix/AddFieldQuickFix.java +++ b/python/src/com/jetbrains/python/inspections/quickfix/AddFieldQuickFix.java @@ -66,20 +66,18 @@ public class AddFieldQuickFix implements LocalQuickFix { @Nullable public static PsiElement appendToMethod(PyFunction init, Function<String, PyStatement> callback) { // add this field as the last stmt of the constructor - final PyStatementList stmt_list = init.getStatementList(); - PyStatement[] stmts = stmt_list.getStatements(); // NOTE: rather wasteful, consider iterable stmt list - PyStatement last_stmt = null; - if (stmts.length > 0) last_stmt = stmts[stmts.length-1]; + final PyStatementList statementList = init.getStatementList(); + assert statementList != null; // name of 'self' may be different for fancier styles PyParameter[] params = init.getParameterList().getParameters(); - String self_name = PyNames.CANONICAL_SELF; + String selfName = PyNames.CANONICAL_SELF; if (params.length > 0) { - self_name = params[0].getName(); + selfName = params[0].getName(); } - PyStatement new_stmt = callback.fun(self_name); - if (!FileModificationService.getInstance().preparePsiElementForWrite(stmt_list)) return null; - final PsiElement result = stmt_list.addAfter(new_stmt, last_stmt); - PyPsiUtils.removeRedundantPass(stmt_list); + PyStatement newStmt = callback.fun(selfName); + if (!FileModificationService.getInstance().preparePsiElementForWrite(statementList)) return null; + final PsiElement result = PyUtil.addElementToStatementList(newStmt, statementList, true); + PyPsiUtils.removeRedundantPass(statementList); return result; } diff --git a/python/src/com/jetbrains/python/inspections/quickfix/AddGlobalQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/AddGlobalQuickFix.java index 349a9116466a..9bf3df0661b9 100644 --- a/python/src/com/jetbrains/python/inspections/quickfix/AddGlobalQuickFix.java +++ b/python/src/com/jetbrains/python/inspections/quickfix/AddGlobalQuickFix.java @@ -80,6 +80,7 @@ public class AddGlobalQuickFix implements LocalQuickFix { statementList = ((PyFunction)owner).getStatementList(); if (((PyFunction)owner).getDocStringExpression() != null) hasDocString = true; } + assert statementList != null; PyStatement first = statementList.getStatements()[0]; if (hasDocString) first = statementList.getStatements()[1]; diff --git a/python/src/com/jetbrains/python/psi/PyUtil.java b/python/src/com/jetbrains/python/psi/PyUtil.java index e939857703d2..0cd299422694 100644 --- a/python/src/com/jetbrains/python/psi/PyUtil.java +++ b/python/src/com/jetbrains/python/psi/PyUtil.java @@ -44,6 +44,7 @@ import com.intellij.psi.*; import com.intellij.psi.stubs.StubElement; import com.intellij.psi.tree.TokenSet; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.QualifiedName; import com.intellij.ui.awt.RelativePoint; import com.intellij.util.ArrayUtil; import com.intellij.util.IncorrectOperationException; @@ -60,7 +61,6 @@ import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil; import com.jetbrains.python.codeInsight.stdlib.PyNamedTupleType; import com.jetbrains.python.psi.impl.PyBuiltinCache; import com.jetbrains.python.psi.impl.PyPsiUtils; -import com.intellij.psi.util.QualifiedName; import com.jetbrains.python.psi.resolve.PyResolveContext; import com.jetbrains.python.psi.resolve.QualifiedResolveResult; import com.jetbrains.python.psi.types.*; @@ -1313,13 +1313,31 @@ public class PyUtil { final PyStatement[] statements = statementList.getStatements(); if (toTheBeginning && statements.length > 0) { final PyDocStringOwner docStringOwner = PsiTreeUtil.getParentOfType(statementList, PyDocStringOwner.class); - final PyStatement firstStatement = statements[0]; - if (docStringOwner != null && firstStatement instanceof PyExpressionStatement && - ((PyExpressionStatement)firstStatement).getExpression() == docStringOwner.getDocStringExpression()) { - element = statementList.addAfter(element, firstStatement); + PyStatement anchor = statements[0]; + if (docStringOwner != null && anchor instanceof PyExpressionStatement && + ((PyExpressionStatement)anchor).getExpression() == docStringOwner.getDocStringExpression()) { + final PyStatement next = PsiTreeUtil.getNextSiblingOfType(anchor, PyStatement.class); + if (next == null) { + return statementList.addAfter(element, anchor); + } + anchor = next; + } + while (anchor instanceof PyExpressionStatement) { + final PyExpression expression = ((PyExpressionStatement)anchor).getExpression(); + if (expression instanceof PyCallExpression) { + final PyExpression callee = ((PyCallExpression)expression).getCallee(); + if ((isSuperCall((PyCallExpression)expression) || (callee != null && PyNames.INIT.equals(callee.getName())))) { + final PyStatement next = PsiTreeUtil.getNextSiblingOfType(anchor, PyStatement.class); + if (next == null) { + return statementList.addAfter(element, anchor); + } + anchor = next; + } + else break; + } + else break; } - else - element = statementList.addBefore(element, firstStatement); + element = statementList.addBefore(element, anchor); } else { element = statementList.add(element); diff --git a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java index 0a9959d23f64..47fb3470a972 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java @@ -585,7 +585,7 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement @Nullable @Override - public Property findProperty(@NotNull final String name) { + public Property findProperty(@NotNull final String name, boolean inherited) { Property property = findLocalProperty(name); if (property != null) { return property; @@ -593,10 +593,12 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement if (findMethodByName(name, false) != null || findClassAttribute(name, false) != null) { return null; } - for (PyClass aClass : getAncestorClasses()) { - final Property ancestorProperty = ((PyClassImpl)aClass).findLocalProperty(name); - if (ancestorProperty != null) { - return ancestorProperty; + if (inherited) { + for (PyClass aClass : getAncestorClasses()) { + final Property ancestorProperty = ((PyClassImpl)aClass).findLocalProperty(name); + if (ancestorProperty != null) { + return ancestorProperty; + } } } return null; diff --git a/python/src/com/jetbrains/python/psi/impl/PyDecoratorImpl.java b/python/src/com/jetbrains/python/psi/impl/PyDecoratorImpl.java index f15a664fcbba..f64b533f3303 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyDecoratorImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyDecoratorImpl.java @@ -74,8 +74,8 @@ public class PyDecoratorImpl extends StubBasedPsiElementBase<PyDecoratorStub> im } public boolean hasArgumentList() { - ASTNode arglist_node = getNode().findChildByType(PyElementTypes.ARGUMENT_LIST); - return (arglist_node != null) && (arglist_node.findChildByType(PyTokenTypes.LPAR) != null); + final ASTNode arglistNode = getNode().findChildByType(PyElementTypes.ARGUMENT_LIST); + return (arglistNode != null) && (arglistNode.findChildByType(PyTokenTypes.LPAR) != null); } public QualifiedName getQualifiedName() { diff --git a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java index 3f438437190e..8c71346575cc 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java @@ -174,29 +174,28 @@ public class PyFunctionImpl extends PyPresentableElementImpl<PyFunctionStub> imp @Nullable @Override public PyType getReturnType(@NotNull TypeEvalContext context, @Nullable PyQualifiedExpression callSite) { - final PyType type = getGenericReturnType(context, callSite); - + PyType type = getGenericReturnType(context, callSite); if (callSite == null) { return type; } final PyTypeChecker.AnalyzeCallResults results = PyTypeChecker.analyzeCallSite(callSite, context); - if (PyTypeChecker.hasGenerics(type, context)) { if (results != null) { final Map<PyGenericType, PyType> substitutions = PyTypeChecker.unifyGenericCall(this, results.getReceiver(), results.getArguments(), context); - if (substitutions != null) { - return PyTypeChecker.substitute(type, substitutions, context); - } + type = substitutions != null ? PyTypeChecker.substitute(type, substitutions, context) : null; + } + else { + type = null; } - return null; + } + if (results != null) { + type = replaceSelf(type, results.getReceiver(), context); } if (results != null && isDynamicallyEvaluated(results.getArguments().values(), context)) { return PyUnionType.createWeakType(type); } - else { - return type; - } + return type; } @Nullable @@ -205,16 +204,36 @@ public class PyFunctionImpl extends PyPresentableElementImpl<PyFunctionStub> imp */ public PyType getReturnTypeWithoutCallSite(@NotNull TypeEvalContext context, @Nullable PyExpression receiver) { - final PyType type = getGenericReturnType(context, null); + PyType type = getGenericReturnType(context, null); if (PyTypeChecker.hasGenerics(type, context)) { - final Map<PyGenericType, PyType> substitutions = - PyTypeChecker.unifyGenericCall(this, receiver, Maps.<PyExpression, PyNamedParameter>newHashMap(), context); + final Map<PyGenericType, PyType> substitutions = PyTypeChecker.unifyGenericCall(this, receiver, + Maps.<PyExpression, PyNamedParameter>newHashMap(), + context); if (substitutions != null) { - return PyTypeChecker.substitute(type, substitutions, context); + type = PyTypeChecker.substitute(type, substitutions, context); + } + else { + type = null; } - return null; } - return type; + return replaceSelf(type, receiver, context); + } + + @Nullable + private PyType replaceSelf(@Nullable PyType returnType, @Nullable PyExpression receiver, @NotNull TypeEvalContext context) { + if (receiver != null) { + // TODO: Currently we substitute only simple subclass types, but we could handle union and collection types as well + if (returnType instanceof PyClassType) { + final PyClassType returnClassType = (PyClassType)returnType; + if (returnClassType.getPyClass() == getContainingClass()) { + final PyType receiverType = context.getType(receiver); + if (receiverType instanceof PyClassType && PyTypeChecker.match(returnType, receiverType, context)) { + return returnClassType.isDefinition() ? receiverType : ((PyClassType)receiverType).toInstance(); + } + } + } + } + return returnType; } private static boolean isDynamicallyEvaluated(@NotNull Collection<PyNamedParameter> parameters, @NotNull TypeEvalContext context) { diff --git a/python/src/com/jetbrains/python/psi/impl/PyReferenceExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyReferenceExpressionImpl.java index 7b778a527635..455d4c597e71 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyReferenceExpressionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyReferenceExpressionImpl.java @@ -264,7 +264,7 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere if (qualifierType instanceof PyClassType) { final PyClassType classType = (PyClassType)qualifierType; PyClass pyClass = classType.getPyClass(); - Property property = pyClass.findProperty(name); + Property property = pyClass.findProperty(name, true); if (property != null) { if (classType.isDefinition()) { return Ref.<PyType>create(PyBuiltinCache.getInstance(pyClass).getObjectType(PyNames.PROPERTY)); @@ -291,7 +291,7 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere } @Nullable - private PyType getTypeFromProviders(TypeEvalContext context) { + private PyType getTypeFromProviders(@NotNull TypeEvalContext context) { for (PyTypeProvider provider : Extensions.getExtensions(PyTypeProvider.EP_NAME)) { try { final PyType type = provider.getReferenceExpressionType(this, context); diff --git a/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java index fd49c2543cc7..ae2e30f28f0a 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java @@ -241,8 +241,8 @@ public class PyTargetExpressionImpl extends PyPresentableElementImpl<PyTargetExp if (exprType instanceof PyClassType) { final PyClass cls = ((PyClassType)exprType).getPyClass(); final PyFunction enter = cls.findMethodByName(PyNames.ENTER, true); - if (enter != null) { - final PyType enterType = enter.getReturnType(context, null); + if (enter instanceof PyFunctionImpl) { + final PyType enterType = ((PyFunctionImpl)enter).getReturnTypeWithoutCallSite(context, expression); if (enterType != null) { return enterType; } diff --git a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java index 7be077f8bc17..e475429e0143 100644 --- a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java +++ b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java @@ -23,6 +23,7 @@ import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.UserDataHolderBase; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiInvalidElementAccessException; @@ -156,22 +157,9 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { } if (resolveContext.allowProperties()) { - Property property = myClass.findProperty(name); - if (property != null) { - Maybe<Callable> accessor = property.getByDirection(direction); - if (accessor.isDefined()) { - Callable accessor_code = accessor.value(); - ResolveResultList ret = new ResolveResultList(); - if (accessor_code != null) ret.poke(accessor_code, RatedResolveResult.RATE_NORMAL); - PyTargetExpression site = property.getDefinitionSite(); - if (site != null) ret.poke(site, RatedResolveResult.RATE_LOW); - if (ret.size() > 0) { - return ret; - } - else { - return null; - } // property is found, but the required accessor is explicitly absent - } + final Ref<ResolveResultList> resultRef = findProperty(name, direction, true); + if (resultRef != null) { + return resultRef.get(); } } @@ -198,6 +186,11 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { return ResolveResultList.to(classMember); } + classMember = resolveByOverridingAncestorsMembersProviders(this, name, location); + if (classMember != null) { + return ResolveResultList.to(classMember); + } + if (inherited) { for (PyClassLikeType type : myClass.getAncestorTypes(context)) { if (type instanceof PyClassType) { @@ -237,12 +230,10 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { for (PyClassLikeType type : myClass.getAncestorTypes(context)) { if (type instanceof PyClassType) { final PyClass pyClass = ((PyClassType)type).getPyClass(); - if (pyClass != null) { - PsiElement superMember = resolveByMembersProviders(new PyClassTypeImpl(pyClass, isDefinition()), name, location); + PsiElement superMember = resolveByMembersProviders(new PyClassTypeImpl(pyClass, isDefinition()), name, location); - if (superMember != null) { - return ResolveResultList.to(superMember); - } + if (superMember != null) { + return ResolveResultList.to(superMember); } } } @@ -251,6 +242,28 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { return Collections.emptyList(); } + private Ref<ResolveResultList> findProperty(String name, AccessDirection direction, boolean inherited) { + Ref<ResolveResultList> resultRef = null; + Property property = myClass.findProperty(name, inherited); + if (property != null) { + Maybe<Callable> accessor = property.getByDirection(direction); + if (accessor.isDefined()) { + Callable accessor_code = accessor.value(); + ResolveResultList ret = new ResolveResultList(); + if (accessor_code != null) ret.poke(accessor_code, RatedResolveResult.RATE_NORMAL); + PyTargetExpression site = property.getDefinitionSite(); + if (site != null) ret.poke(site, RatedResolveResult.RATE_LOW); + if (ret.size() > 0) { + resultRef = Ref.create(ret); + } + else { + resultRef = Ref.create(); + } // property is found, but the required accessor is explicitly absent + } + } + return resultRef; + } + @Nullable private PyClassType getMetaclassType() { final PyClass metaClass = PyUtil.getMetaClass(myClass); @@ -330,6 +343,17 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { } @Nullable + private static PsiElement resolveByOverridingAncestorsMembersProviders(PyClassType type, String name, @Nullable PyExpression location) { + for (PyClassMembersProvider provider : Extensions.getExtensions(PyClassMembersProvider.EP_NAME)) { + if (provider instanceof PyOverridingAncestorsClassMembersProvider) { + final PsiElement resolveResult = provider.resolveMember(type, name, location); + if (resolveResult != null) return resolveResult; + } + } + return null; + } + + @Nullable private static PsiElement resolveInner(@NotNull PyClass cls, boolean isDefinition, @NotNull String name, diff --git a/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java b/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java index 8a1bd6b60bae..aeb1efae5824 100644 --- a/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java +++ b/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java @@ -40,7 +40,10 @@ import com.intellij.util.Consumer; import com.intellij.util.Function; import com.intellij.util.IncorrectOperationException; import com.intellij.util.containers.hash.HashMap; -import com.jetbrains.python.*; +import com.jetbrains.python.PyBundle; +import com.jetbrains.python.PyNames; +import com.jetbrains.python.PythonFileType; +import com.jetbrains.python.PythonLanguage; import com.jetbrains.python.codeInsight.codeFragment.PyCodeFragment; import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache; import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; @@ -332,9 +335,12 @@ public class PyExtractMethodUtil { } } - private static void setSelectionAndCaret(Editor editor, final PsiElement callElement) { + private static void setSelectionAndCaret(Editor editor, @Nullable final PsiElement callElement) { editor.getSelectionModel().removeSelection(); - editor.getCaretModel().moveToOffset(callElement.getTextOffset()); + if (callElement != null) { + final int offset = callElement.getTextOffset(); + editor.getCaretModel().moveToOffset(offset); + } } private static PsiElement replaceElements(final List<PsiElement> elementsRange, @NotNull PsiElement callElement) { diff --git a/python/src/com/jetbrains/python/sdk/skeletons/PySkeletonRefresher.java b/python/src/com/jetbrains/python/sdk/skeletons/PySkeletonRefresher.java index 3fbc168acc1c..077ced33e0fc 100644 --- a/python/src/com/jetbrains/python/sdk/skeletons/PySkeletonRefresher.java +++ b/python/src/com/jetbrains/python/sdk/skeletons/PySkeletonRefresher.java @@ -267,6 +267,7 @@ public class PySkeletonRefresher { final String skeletonsPath = getSkeletonsPath(); final File skeletonsDir = new File(skeletonsPath); if (!skeletonsDir.exists()) { + //noinspection ResultOfMethodCallIgnored skeletonsDir.mkdirs(); } final String readablePath = FileUtil.getLocationRelativeToUserHome(homePath); @@ -446,7 +447,7 @@ public class PySkeletonRefresher { reader.close(); } } - catch (IOException e) { + catch (IOException ignored) { } return null; } diff --git a/python/src/com/jetbrains/python/testing/PyTestFrameworkService.java b/python/src/com/jetbrains/python/testing/PyTestFrameworkService.java new file mode 100644 index 000000000000..8eac0886d277 --- /dev/null +++ b/python/src/com/jetbrains/python/testing/PyTestFrameworkService.java @@ -0,0 +1,50 @@ +/* + * Copyright 2000-2013 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.jetbrains.python.testing; + +import com.intellij.openapi.components.*; +import com.intellij.util.containers.HashMap; +import com.intellij.util.xmlb.XmlSerializerUtil; + +import java.util.Map; + +@State( + name = "PyTestFrameworkService", + storages = { + @Storage( + file = StoragePathMacros.APP_CONFIG + "/other.xml" + )} +) +public class PyTestFrameworkService implements PersistentStateComponent<PyTestFrameworkService> { + + public static PyTestFrameworkService getInstance() { + return ServiceManager.getService(PyTestFrameworkService.class); + } + + public Map<String, Boolean> SDK_TO_PYTEST = new HashMap<String, Boolean>(); + public Map <String, Boolean> SDK_TO_NOSETEST = new HashMap<String, Boolean>(); + public Map <String, Boolean> SDK_TO_ATTEST = new HashMap<String, Boolean>(); + + @Override + public PyTestFrameworkService getState() { + return this; + } + + @Override + public void loadState(PyTestFrameworkService state) { + XmlSerializerUtil.copyBean(state, this); + } +} diff --git a/python/src/com/jetbrains/python/testing/VFSTestFrameworkListener.java b/python/src/com/jetbrains/python/testing/VFSTestFrameworkListener.java index 7130b1aedf78..69d1ce01dd2a 100644 --- a/python/src/com/jetbrains/python/testing/VFSTestFrameworkListener.java +++ b/python/src/com/jetbrains/python/testing/VFSTestFrameworkListener.java @@ -16,22 +16,22 @@ package com.jetbrains.python.testing; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.components.*; +import com.intellij.openapi.components.ApplicationComponent; +import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.roots.OrderRootType; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.openapi.vfs.newvfs.BulkFileListener; import com.intellij.openapi.vfs.newvfs.events.VFileContentChangeEvent; import com.intellij.openapi.vfs.newvfs.events.VFileEvent; -import com.intellij.util.containers.HashMap; import com.intellij.util.messages.MessageBus; import com.intellij.util.ui.update.MergingUpdateQueue; import com.intellij.util.ui.update.Update; -import com.intellij.util.xmlb.XmlSerializerUtil; import com.jetbrains.python.PyNames; import com.jetbrains.python.packaging.PyExternalProcessException; import com.jetbrains.python.packaging.PyPackageManager; @@ -41,25 +41,17 @@ import com.jetbrains.python.sdk.PythonSdkType; import org.jetbrains.annotations.NotNull; import java.util.List; -import java.util.Map; /** * User: catherine */ -@State( - name = "VFSTestFrameworkListener", - storages = { - @Storage( - file = StoragePathMacros.APP_CONFIG + "/other.xml" - )} -) -public class VFSTestFrameworkListener implements ApplicationComponent, PersistentStateComponent<VFSTestFrameworkListener> { - +public class VFSTestFrameworkListener implements ApplicationComponent { private static final Logger LOG = Logger.getInstance("#com.jetbrains.python.testing.VFSTestFrameworkListener"); - private static final MergingUpdateQueue myQueue = new MergingUpdateQueue("TestFrameworkChecker", 5000, true, null); + private PyTestFrameworkService myService; public VFSTestFrameworkListener() { + myService = PyTestFrameworkService.getInstance(); MessageBus messageBus = ApplicationManager.getApplication().getMessageBus(); messageBus.connect().subscribe(VirtualFileManager.VFS_CHANGES, new BulkFileListener.Adapter() { @Override @@ -76,8 +68,9 @@ public class VFSTestFrameworkListener implements ApplicationComponent, Persisten if (PySdkUtil.isRemote(sdk)) { continue; } - for (String root : sdk.getRootProvider().getUrls(OrderRootType.CLASSES)) { - if (path.contains(root)) { + for (VirtualFile virtualFile : sdk.getRootProvider().getFiles(OrderRootType.CLASSES)) { + String root = virtualFile.getCanonicalPath(); + if (root != null && path.contains(root)) { if (containsNose) { updateTestFrameworks(sdk, PyNames.NOSE_TEST); return; @@ -152,26 +145,12 @@ public class VFSTestFrameworkListener implements ApplicationComponent, Persisten return ServiceManager.getService(VFSTestFrameworkListener.class); } - public Map<String, Boolean> SDK_TO_PYTEST = new HashMap<String, Boolean>(); - public Map <String, Boolean> SDK_TO_NOSETEST = new HashMap<String, Boolean>(); - public Map <String, Boolean> SDK_TO_ATTEST = new HashMap<String, Boolean>(); - - @Override - public VFSTestFrameworkListener getState() { - return this; - } - - @Override - public void loadState(VFSTestFrameworkListener state) { - XmlSerializerUtil.copyBean(state, this); - } - public void pyTestInstalled(boolean installed, String sdkHome) { - SDK_TO_PYTEST.put(sdkHome, installed); + myService.SDK_TO_PYTEST.put(sdkHome, installed); } public boolean isPyTestInstalled(final Sdk sdk) { - Boolean isInstalled = SDK_TO_PYTEST.get(sdk.getHomePath()); + Boolean isInstalled = myService.SDK_TO_PYTEST.get(sdk.getHomePath()); if (isInstalled == null) { updateTestFrameworks(sdk, PyNames.PY_TEST); return true; @@ -180,11 +159,11 @@ public class VFSTestFrameworkListener implements ApplicationComponent, Persisten } public void noseTestInstalled(boolean installed, String sdkHome) { - SDK_TO_NOSETEST.put(sdkHome, installed); + myService.SDK_TO_NOSETEST.put(sdkHome, installed); } public boolean isNoseTestInstalled(final Sdk sdk) { - Boolean isInstalled = SDK_TO_NOSETEST.get(sdk.getHomePath()); + Boolean isInstalled = myService.SDK_TO_NOSETEST.get(sdk.getHomePath()); if (isInstalled == null) { updateTestFrameworks(sdk, PyNames.NOSE_TEST); return true; @@ -193,11 +172,11 @@ public class VFSTestFrameworkListener implements ApplicationComponent, Persisten } public void atTestInstalled(boolean installed, String sdkHome) { - SDK_TO_ATTEST.put(sdkHome, installed); + myService.SDK_TO_ATTEST.put(sdkHome, installed); } public boolean isAtTestInstalled(final Sdk sdk) { - Boolean isInstalled = SDK_TO_ATTEST.get(sdk.getHomePath()); + Boolean isInstalled = myService.SDK_TO_ATTEST.get(sdk.getHomePath()); if (isInstalled == null) { updateTestFrameworks(sdk, PyNames.AT_TEST); return true; diff --git a/python/testData/formatter/commentBetweenClasses_after.py b/python/testData/formatter/commentBetweenClasses_after.py index b8523949c0b6..e97c76b04590 100644 --- a/python/testData/formatter/commentBetweenClasses_after.py +++ b/python/testData/formatter/commentBetweenClasses_after.py @@ -2,6 +2,7 @@ class T1(object): def m1(self): pass + # comment about T2 class T2(object): diff --git a/python/testData/formatter/continuationIndentInIndentingStatement.py b/python/testData/formatter/continuationIndentInIndentingStatement.py new file mode 100644 index 000000000000..5822bc4971bb --- /dev/null +++ b/python/testData/formatter/continuationIndentInIndentingStatement.py @@ -0,0 +1,31 @@ +if True \ + or False: + pass +elif \ + False: + pass + +for i in \ + range(1, 100): + pass + +with open('file1') as file1, \ + open('file2') as file2: + pass + + +class \ + A(object): + pass + + +def \ + foo(): + pass + + +try: + pass +except \ + AttributeError: + pass
\ No newline at end of file diff --git a/python/testData/formatter/continuationIndentInIndentingStatement_after.py b/python/testData/formatter/continuationIndentInIndentingStatement_after.py new file mode 100644 index 000000000000..f38dd5199edd --- /dev/null +++ b/python/testData/formatter/continuationIndentInIndentingStatement_after.py @@ -0,0 +1,31 @@ +if True \ + or False: + pass +elif \ + False: + pass + +for i in \ + range(1, 100): + pass + +with open('file1') as file1, \ + open('file2') as file2: + pass + + +class \ + A(object): + pass + + +def \ + foo(): + pass + + +try: + pass +except \ + AttributeError: + pass
\ No newline at end of file diff --git a/python/testData/formatter/indentParensInImport.py b/python/testData/formatter/indentParensInImport.py new file mode 100644 index 000000000000..ab20f1b5571b --- /dev/null +++ b/python/testData/formatter/indentParensInImport.py @@ -0,0 +1,3 @@ +from some.module import ( + thing + )
\ No newline at end of file diff --git a/python/testData/formatter/indentParensInImport_after.py b/python/testData/formatter/indentParensInImport_after.py new file mode 100644 index 000000000000..2ad9216af71c --- /dev/null +++ b/python/testData/formatter/indentParensInImport_after.py @@ -0,0 +1,3 @@ +from some.module import ( + thing +)
\ No newline at end of file diff --git a/python/testData/formatter/twoLinesBetweenTopLevelDeclarationsWithComment.py b/python/testData/formatter/twoLinesBetweenTopLevelDeclarationsWithComment.py new file mode 100644 index 000000000000..a0f52904114e --- /dev/null +++ b/python/testData/formatter/twoLinesBetweenTopLevelDeclarationsWithComment.py @@ -0,0 +1,18 @@ +class A(object): + pass + +#comment +def one(): + pass + +# comment +def two(): + pass + +#comment +class B(object): + pass + +#comment +class C(object): + pass
\ No newline at end of file diff --git a/python/testData/formatter/twoLinesBetweenTopLevelDeclarationsWithComment_after.py b/python/testData/formatter/twoLinesBetweenTopLevelDeclarationsWithComment_after.py new file mode 100644 index 000000000000..d582036d8654 --- /dev/null +++ b/python/testData/formatter/twoLinesBetweenTopLevelDeclarationsWithComment_after.py @@ -0,0 +1,22 @@ +class A(object): + pass + + +#comment +def one(): + pass + + +# comment +def two(): + pass + + +#comment +class B(object): + pass + + +#comment +class C(object): + pass
\ No newline at end of file diff --git a/python/testData/inspections/FieldFromUnusedParameterKeyword.py b/python/testData/inspections/FieldFromUnusedParameterKeyword.py deleted file mode 100644 index 35851f0f87df..000000000000 --- a/python/testData/inspections/FieldFromUnusedParameterKeyword.py +++ /dev/null @@ -1,3 +0,0 @@ -class A: - def __init__(self, <weak_warning descr="Parameter 'foo' value is not used">f<caret>oo=True</weak_warning>): - print('hello')
\ No newline at end of file diff --git a/python/testData/inspections/FieldFromUnusedParameterKeyword_after.py b/python/testData/inspections/FieldFromUnusedParameterKeyword_after.py deleted file mode 100644 index 7e0d6627cc00..000000000000 --- a/python/testData/inspections/FieldFromUnusedParameterKeyword_after.py +++ /dev/null @@ -1,4 +0,0 @@ -class A: - def __init__(self, foo=True): - print('hello') - self.foo = foo
\ No newline at end of file diff --git a/python/testData/inspections/FieldFromUnusedParameter_after.py b/python/testData/inspections/FieldFromUnusedParameter_after.py deleted file mode 100644 index 7405ba3ec789..000000000000 --- a/python/testData/inspections/FieldFromUnusedParameter_after.py +++ /dev/null @@ -1,4 +0,0 @@ -class A: - def __init__(self, foo): - print('hello') - self.foo = foo
\ No newline at end of file diff --git a/python/testData/inspections/PyArgumentListInspection/decoratorsPy3K.py b/python/testData/inspections/PyArgumentListInspection/decoratorsPy3K.py new file mode 100644 index 000000000000..07e9942700b1 --- /dev/null +++ b/python/testData/inspections/PyArgumentListInspection/decoratorsPy3K.py @@ -0,0 +1,7 @@ + +def deco(func, *args): + return func + +@deco # <= Here is a false positive. +def myfunc(a, b): + print(a, b)
\ No newline at end of file diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/contextManagerSubclass.py b/python/testData/inspections/PyUnresolvedReferencesInspection/contextManagerSubclass.py new file mode 100644 index 000000000000..f7406dedbb6f --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/contextManagerSubclass.py @@ -0,0 +1,12 @@ +class C(object): + def __enter__(self): + return self + + +class D(C): + def foo(self): + pass + + +with D() as cm: + cm.foo() # pass diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/ivarInDocstring.py b/python/testData/inspections/PyUnresolvedReferencesInspection/ivarInDocstring.py new file mode 100644 index 000000000000..1fec85f8b63b --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/ivarInDocstring.py @@ -0,0 +1,10 @@ + +class SomeClass(object): + """ Awesome class + + @ivar someVar: great stuff + @type someVar: string + """ + + def __init__(self): + self.someVar = None
\ No newline at end of file diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/returnSelfInSuperClass.py b/python/testData/inspections/PyUnresolvedReferencesInspection/returnSelfInSuperClass.py new file mode 100644 index 000000000000..79c56a020033 --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/returnSelfInSuperClass.py @@ -0,0 +1,13 @@ +class C(object): + def get_self(self): + return self + + +class D(C): + def foo(self): + pass + + +d = D() +print(d.foo()) +print(d.get_self().foo()) # pass diff --git a/python/testData/inspections/PyUnusedLocalVariableInspection/test.py b/python/testData/inspections/PyUnusedLocalVariableInspection/test.py index 5edffda057d2..65913e27c745 100644 --- a/python/testData/inspections/PyUnusedLocalVariableInspection/test.py +++ b/python/testData/inspections/PyUnusedLocalVariableInspection/test.py @@ -303,3 +303,16 @@ def test_unused_condition_local_with_last_if_in_cycle(c): x = False #pass if c: x = True + + +# PY-7527 +def test_unused_empty_init_parameter(): + class C(object): + def __init__(self, <weak_warning descr="Parameter 'foo' value is not used">foo</weak_warning>): + pass + + def f(self, bar): + pass + + return C + diff --git a/python/testData/inspections/ReplaceNotEqOperator.py b/python/testData/inspections/ReplaceNotEqOperator.py index c0c6300b8e1f..790a765f19ca 100644 --- a/python/testData/inspections/ReplaceNotEqOperator.py +++ b/python/testData/inspections/ReplaceNotEqOperator.py @@ -1 +1 @@ -print(<warning descr="Python version 3.0, 3.1, 3.2, 3.3, 3.4 do not support <>, use != instead.">a <> b</warning>)
\ No newline at end of file +print(<warning descr="Python version 3.0, 3.1, 3.2, 3.3, 3.4 do not support <>, use != instead.">a<caret> <> b</warning>)
\ No newline at end of file diff --git a/python/testData/quickFixes/AddFieldQuickFixTest/addFieldFromInstance_after.py b/python/testData/quickFixes/AddFieldQuickFixTest/addFieldFromInstance_after.py index 686290338cf5..7027a3993765 100644 --- a/python/testData/quickFixes/AddFieldQuickFixTest/addFieldFromInstance_after.py +++ b/python/testData/quickFixes/AddFieldQuickFixTest/addFieldFromInstance_after.py @@ -1,8 +1,7 @@ class A: def __init__(self): - self.x = 1 self.y = None - + self.x = 1 a = A() a.y+1 diff --git a/python/testData/quickFixes/AddFieldQuickFixTest/addFieldFromMethod_after.py b/python/testData/quickFixes/AddFieldQuickFixTest/addFieldFromMethod_after.py index 3ac2f7ecd201..c0ce40fe9e03 100644 --- a/python/testData/quickFixes/AddFieldQuickFixTest/addFieldFromMethod_after.py +++ b/python/testData/quickFixes/AddFieldQuickFixTest/addFieldFromMethod_after.py @@ -1,7 +1,7 @@ class A: def __init__(self): - self.x = 1 self.y = None + self.x = 1 def foo(self): a = self.y diff --git a/python/testData/inspections/FieldFromUnusedParameter.py b/python/testData/quickFixes/AddFieldQuickFixTest/fromUnusedParameter.py index a9b802bb0136..a9b802bb0136 100644 --- a/python/testData/inspections/FieldFromUnusedParameter.py +++ b/python/testData/quickFixes/AddFieldQuickFixTest/fromUnusedParameter.py diff --git a/python/testData/quickFixes/AddFieldQuickFixTest/fromUnusedParameterKeyword.py b/python/testData/quickFixes/AddFieldQuickFixTest/fromUnusedParameterKeyword.py new file mode 100644 index 000000000000..27f1600fe718 --- /dev/null +++ b/python/testData/quickFixes/AddFieldQuickFixTest/fromUnusedParameterKeyword.py @@ -0,0 +1,3 @@ +class A: + def __init__(self, fo<caret>o=True): + print('hello')
\ No newline at end of file diff --git a/python/testData/quickFixes/AddFieldQuickFixTest/fromUnusedParameterKeyword_after.py b/python/testData/quickFixes/AddFieldQuickFixTest/fromUnusedParameterKeyword_after.py new file mode 100644 index 000000000000..3f86fa418459 --- /dev/null +++ b/python/testData/quickFixes/AddFieldQuickFixTest/fromUnusedParameterKeyword_after.py @@ -0,0 +1,4 @@ +class A: + def __init__(self, foo=True): + self.foo = foo + print('hello')
\ No newline at end of file diff --git a/python/testData/quickFixes/AddFieldQuickFixTest/fromUnusedParameter_after.py b/python/testData/quickFixes/AddFieldQuickFixTest/fromUnusedParameter_after.py new file mode 100644 index 000000000000..58785edcedea --- /dev/null +++ b/python/testData/quickFixes/AddFieldQuickFixTest/fromUnusedParameter_after.py @@ -0,0 +1,4 @@ +class A: + def __init__(self, foo): + self.foo = foo + print('hello')
\ No newline at end of file diff --git a/python/testData/quickFixes/AddMethodQuickFixTest/addMethodFromMethod_after.py b/python/testData/quickFixes/AddMethodQuickFixTest/addMethodFromMethod_after.py index 978acaf15275..5c47b58a48dc 100644 --- a/python/testData/quickFixes/AddMethodQuickFixTest/addMethodFromMethod_after.py +++ b/python/testData/quickFixes/AddMethodQuickFixTest/addMethodFromMethod_after.py @@ -8,6 +8,7 @@ class A: def y(self, param, a): pass + # Some comment class B: diff --git a/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/addPass_after.py b/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/addPass_after.py index da65a030ea4d..040de7386118 100644 --- a/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/addPass_after.py +++ b/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/addPass_after.py @@ -3,8 +3,8 @@ __author__ = 'ktisha' class A: def __init__(self): - self._a = 1 self.b = 1 + self._a = 1 def foo(self): pass diff --git a/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/moveToInit_after.py b/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/moveToInit_after.py index 2dc80743d949..9a83f676bfdc 100644 --- a/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/moveToInit_after.py +++ b/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/moveToInit_after.py @@ -3,8 +3,8 @@ __author__ = 'ktisha' class A: def __init__(self): - self._a = 1 self.b = 1 + self._a = 1 def foo(self): c = 1
\ No newline at end of file diff --git a/python/testData/refactoring/introduceField/py4437.after.py b/python/testData/refactoring/introduceField/py4437.after.py index e9c7d7535699..07a1331b604a 100644 --- a/python/testData/refactoring/introduceField/py4437.after.py +++ b/python/testData/refactoring/introduceField/py4437.after.py @@ -1,7 +1,7 @@ class SomeClass(): def __init__(self): - self.x = 1 self.a = '' + self.x = 1 def foo(self): self.a diff --git a/python/testSrc/com/jetbrains/python/PyClassicPropertyTest.java b/python/testSrc/com/jetbrains/python/PyClassicPropertyTest.java index 7947ffb6dc46..2e55003cddd0 100644 --- a/python/testSrc/com/jetbrains/python/PyClassicPropertyTest.java +++ b/python/testSrc/com/jetbrains/python/PyClassicPropertyTest.java @@ -38,7 +38,7 @@ public class PyClassicPropertyTest extends PyTestCase { public void testV1() throws Exception { Property p; Maybe<Callable> accessor; - p = myClass.findProperty("v1"); + p = myClass.findProperty("v1", true); assertNotNull(p); assertNull(p.getDoc()); PyTargetExpression site = p.getDefinitionSite(); @@ -62,7 +62,7 @@ public class PyClassicPropertyTest extends PyTestCase { public void testV2() throws Exception { Property p; Maybe<Callable> accessor; - p = myClass.findProperty("v2"); + p = myClass.findProperty("v2", true); assertNotNull(p); assertEquals("doc of v2", p.getDoc()); PyTargetExpression site = p.getDefinitionSite(); @@ -86,7 +86,7 @@ public class PyClassicPropertyTest extends PyTestCase { public void testV3() throws Exception { Maybe<Callable> accessor; - Property p = myClass.findProperty("v3"); + Property p = myClass.findProperty("v3", true); assertNotNull(p); assertNull(p.getDoc()); PyTargetExpression site = p.getDefinitionSite(); diff --git a/python/testSrc/com/jetbrains/python/PyDecoratedPropertyTest.java b/python/testSrc/com/jetbrains/python/PyDecoratedPropertyTest.java index 414c3558d948..ac9f83afce6a 100644 --- a/python/testSrc/com/jetbrains/python/PyDecoratedPropertyTest.java +++ b/python/testSrc/com/jetbrains/python/PyDecoratedPropertyTest.java @@ -35,7 +35,7 @@ public class PyDecoratedPropertyTest extends PyTestCase { Property p; Maybe<Callable> accessor; final String name = "w1"; - p = myClass.findProperty(name); + p = myClass.findProperty(name, true); assertNotNull(p); assertNull(p.getDoc()); assertNull(p.getDefinitionSite()); @@ -60,7 +60,7 @@ public class PyDecoratedPropertyTest extends PyTestCase { Property p; Maybe<Callable> accessor; final String name = "w2"; - p = myClass.findProperty(name); + p = myClass.findProperty(name, true); assertNotNull(p); assertNull(p.getDoc()); assertNull(p.getDefinitionSite()); diff --git a/python/testSrc/com/jetbrains/python/PyEditingTest.java b/python/testSrc/com/jetbrains/python/PyEditingTest.java index 6619bf878749..5f0e7c2de446 100644 --- a/python/testSrc/com/jetbrains/python/PyEditingTest.java +++ b/python/testSrc/com/jetbrains/python/PyEditingTest.java @@ -26,6 +26,8 @@ import com.intellij.openapi.editor.LogicalPosition; import com.intellij.openapi.editor.ex.EditorSettingsExternalizable; import com.intellij.openapi.util.Computable; import com.intellij.psi.PsiFile; +import com.jetbrains.python.documentation.DocStringFormat; +import com.jetbrains.python.documentation.PyDocumentationSettings; import com.jetbrains.python.fixtures.PyTestCase; /** @@ -142,7 +144,7 @@ public class PyEditingTest extends PyTestCase { } public void testEnterInStatement() { - doTestEnter("if a <caret>and b: pass", "if a \\\n and b: pass"); + doTestEnter("if a <caret>and b: pass", "if a \\\n and b: pass"); } public void testEnterBeforeStatement() { @@ -202,10 +204,17 @@ public class PyEditingTest extends PyTestCase { } public void testEnterStubInDocstring() { // CR-PY-144 - doTestEnter("def foo():\n \"\"\"<caret>", "def foo():\n" + - " \"\"\"\n" + - " \n" + - " \"\"\""); + final PyDocumentationSettings documentationSettings = PyDocumentationSettings.getInstance(myFixture.getModule()); + final String oldFormat = documentationSettings.getFormat(); + documentationSettings.setFormat(DocStringFormat.PLAIN); + try { + doTestEnter("def foo():\n \"\"\"<caret>", "def foo():\n" + + " \"\"\"\n" + + " \n" + + " \"\"\""); + } finally { + documentationSettings.setFormat(oldFormat); + } } public void testEnterInString() { // PY-1738 @@ -216,7 +225,7 @@ public class PyEditingTest extends PyTestCase { public void testEnterInImportWithParens() { // PY-2661 doTestEnter("from django.http import (HttpResponse,<caret>)", "from django.http import (HttpResponse,\n" + - " )"); + ")"); } public void testEnterInKeyword() { diff --git a/python/testSrc/com/jetbrains/python/PyFormatterTest.java b/python/testSrc/com/jetbrains/python/PyFormatterTest.java index d25a62f48818..3ca878b44ef7 100644 --- a/python/testSrc/com/jetbrains/python/PyFormatterTest.java +++ b/python/testSrc/com/jetbrains/python/PyFormatterTest.java @@ -126,6 +126,10 @@ public class PyFormatterTest extends PyTestCase { doTest(); } + public void testTwoLinesBetweenTopLevelDeclarationsWithComment() { // PY-9923 + doTest(); + } + public void testSpecialSlice() { // PY-1928 doTest(); } @@ -199,6 +203,10 @@ public class PyFormatterTest extends PyTestCase { doTest(); } + public void testContinuationIndentInIndentingStatement() { // PY-9573 + doTest(); + } + public void testBlankLineAfterDecorator() { doTest(); } @@ -358,6 +366,10 @@ public class PyFormatterTest extends PyTestCase { doTest(); } + public void testIndentParensInImport() { // PY-9075 + doTest(); + } + private void doTest() { doTest(false); } diff --git a/python/testSrc/com/jetbrains/python/PyQuickDocTest.java b/python/testSrc/com/jetbrains/python/PyQuickDocTest.java index 0b78bd5021b9..fb45faeeec09 100644 --- a/python/testSrc/com/jetbrains/python/PyQuickDocTest.java +++ b/python/testSrc/com/jetbrains/python/PyQuickDocTest.java @@ -20,6 +20,8 @@ import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; import com.intellij.testFramework.TestDataFile; +import com.jetbrains.python.documentation.DocStringFormat; +import com.jetbrains.python.documentation.PyDocumentationSettings; import com.jetbrains.python.documentation.PythonDocumentationProvider; import com.jetbrains.python.fixtures.LightMarkedTestCase; import com.jetbrains.python.fixtures.PyTestCase; @@ -35,12 +37,23 @@ import java.util.Map; */ public class PyQuickDocTest extends LightMarkedTestCase { private PythonDocumentationProvider myProvider; + private String myFormat; @Override protected void setUp() throws Exception { super.setUp(); // the provider is stateless, can be reused, as in real life myProvider = new PythonDocumentationProvider(); + final PyDocumentationSettings documentationSettings = PyDocumentationSettings.getInstance(myFixture.getModule()); + myFormat = documentationSettings.getFormat(); + documentationSettings.setFormat(DocStringFormat.PLAIN); + } + + @Override + public void tearDown() throws Exception { + final PyDocumentationSettings documentationSettings = PyDocumentationSettings.getInstance(myFixture.getModule()); + documentationSettings.setFormat(myFormat); + super.tearDown(); } private void checkByHTML(String text) { diff --git a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java index e0f82b0718c4..4083775e8a41 100644 --- a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java +++ b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java @@ -173,13 +173,6 @@ public class PyQuickFixTest extends PyTestCase { doInspectionTest("AddClass.py", PyUnresolvedReferencesInspection.class, "Create class 'Xyzzy'", true, true); } - public void testFieldFromUnusedParameter() { // PY-1398 - doInspectionTest("FieldFromUnusedParameter.py", PyUnusedLocalInspection.class, "Add field 'foo' to class A", true, true); - } - - public void testFieldFromUnusedParameterKeyword() { // PY-1602 - doInspectionTest("FieldFromUnusedParameterKeyword.py", PyUnusedLocalInspection.class, "Add field 'foo' to class A", true, true); - } public void testAddFunctionToModule() { // PY-1602 doInspectionTest( diff --git a/python/testSrc/com/jetbrains/python/PyStubsTest.java b/python/testSrc/com/jetbrains/python/PyStubsTest.java index 6ad2dc4bff16..7706dd7c9ef8 100644 --- a/python/testSrc/com/jetbrains/python/PyStubsTest.java +++ b/python/testSrc/com/jetbrains/python/PyStubsTest.java @@ -118,12 +118,12 @@ public class PyStubsTest extends PyTestCase { pyClass = classes.get(1); assertEquals("BarClass", pyClass.getName()); - Property prop = pyClass.findProperty("value"); + Property prop = pyClass.findProperty("value", true); Maybe<Callable> maybe_function = prop.getGetter(); assertTrue(maybe_function.isDefined()); assertEquals(pyClass.getMethods()[0], maybe_function.value()); - Property setvalueProp = pyClass.findProperty("setvalue"); + Property setvalueProp = pyClass.findProperty("setvalue", true); Maybe<Callable> setter = setvalueProp.getSetter(); assertTrue(setter.isDefined()); assertEquals("__set", setter.value().getName()); @@ -131,7 +131,7 @@ public class PyStubsTest extends PyTestCase { // properties by decorator pyClass = classes.get(2); assertEquals("BazClass", pyClass.getName()); - prop = pyClass.findProperty("x"); + prop = pyClass.findProperty("x", true); maybe_function = prop.getGetter(); assertTrue(maybe_function.isDefined()); assertEquals(pyClass.getMethods()[0], maybe_function.value()); diff --git a/python/testSrc/com/jetbrains/python/inspections/PyArgumentListInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyArgumentListInspectionTest.java index fabba0c2458f..c0b20d80a542 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyArgumentListInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyArgumentListInspectionTest.java @@ -34,6 +34,16 @@ public class PyArgumentListInspectionTest extends PyTestCase { public void testDecorators() { doTest(); } + + public void testDecoratorsPy3K() { + PythonLanguageLevelPusher.setForcedLanguageLevel(myFixture.getProject(), LanguageLevel.PYTHON30); + try { + doTest(); + } + finally { + PythonLanguageLevelPusher.setForcedLanguageLevel(myFixture.getProject(), null); + } + } public void testTupleVsLiteralList() { doTest(); diff --git a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java index 54cbf2e1aebf..53bcbc34d60e 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java @@ -133,6 +133,10 @@ public class PyUnresolvedReferencesInspectionTest extends PyTestCase { doTest(); } + public void testIvarInDocstring() { + doTest(); + } + // PY-6634 public void testModuleAttribute() { doTest(); @@ -322,6 +326,16 @@ public class PyUnresolvedReferencesInspectionTest extends PyTestCase { doTest(); } + // PY-10977 + public void testContextManagerSubclass() { + doTest(); + } + + // PY-11413 + public void testReturnSelfInSuperClass() { + doTest(); + } + private void doTest() { myFixture.configureByFile(TEST_DIRECTORY + getTestName(true) + ".py"); myFixture.enableInspections(PyUnresolvedReferencesInspection.class); diff --git a/python/testSrc/com/jetbrains/python/quickFixes/AddFieldQuickFixTest.java b/python/testSrc/com/jetbrains/python/quickFixes/AddFieldQuickFixTest.java index 5ffe1a651f2b..c93553d2c3a4 100644 --- a/python/testSrc/com/jetbrains/python/quickFixes/AddFieldQuickFixTest.java +++ b/python/testSrc/com/jetbrains/python/quickFixes/AddFieldQuickFixTest.java @@ -15,13 +15,16 @@ */ package com.jetbrains.python.quickFixes; +import com.intellij.testFramework.TestDataPath; import com.jetbrains.python.PyBundle; import com.jetbrains.python.PyQuickFixTestCase; import com.jetbrains.python.inspections.PyUnresolvedReferencesInspection; +import com.jetbrains.python.inspections.PyUnusedLocalInspection; /** * User: ktisha */ +@TestDataPath("$CONTENT_ROOT/../testData//quickFixes/AddFieldQuickFixTest/") public class AddFieldQuickFixTest extends PyQuickFixTestCase { public void testAddClassField() { @@ -44,4 +47,12 @@ public class AddFieldQuickFixTest extends PyQuickFixTestCase { doQuickFixTest(PyUnresolvedReferencesInspection.class, PyBundle.message("QFIX.NAME.add.field.$0.to.class.$1", "x", "B")); } + public void testFromUnusedParameter() { // PY-1398 + doQuickFixTest(PyUnusedLocalInspection.class, "Add field 'foo' to class A"); + } + + public void testFromUnusedParameterKeyword() { // PY-1602 + doQuickFixTest(PyUnusedLocalInspection.class, "Add field 'foo' to class A"); + } + } diff --git a/xml/dom-impl/src/com/intellij/util/xml/impl/DomAnchorImpl.java b/xml/dom-impl/src/com/intellij/util/xml/impl/DomAnchorImpl.java index 4e6ec59c62b9..01c0bf7ec468 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/impl/DomAnchorImpl.java +++ b/xml/dom-impl/src/com/intellij/util/xml/impl/DomAnchorImpl.java @@ -82,7 +82,6 @@ public abstract class DomAnchorImpl<T extends DomElement> implements DomAnchor<T final int index = values.indexOf(t); if (index < 0) { - t.equals(values.get(10)); diagnoseNegativeIndex2(t, parent, description, values); } return new IndexedAnchor<T>(parentAnchor, description, index); diff --git a/xml/impl/src/com/intellij/ide/browsers/impl/BrowserSettingsProviderImpl.java b/xml/impl/src/com/intellij/ide/browsers/impl/BrowserSettingsProviderImpl.java index 568a265d1a57..11ee96f8cd4d 100644 --- a/xml/impl/src/com/intellij/ide/browsers/impl/BrowserSettingsProviderImpl.java +++ b/xml/impl/src/com/intellij/ide/browsers/impl/BrowserSettingsProviderImpl.java @@ -43,7 +43,7 @@ public class BrowserSettingsProviderImpl extends BrowserSettingsProvider { } public boolean isModified() { - return mySettingsPanel.isModified(); + return mySettingsPanel != null && mySettingsPanel.isModified(); } public void apply() throws ConfigurationException { |