diff --git a/.idea/libraries/proxy_vole.xml b/.idea/libraries/proxy_vole.xml index 74a709c8cf82..6cb9a2e19b64 100644 --- a/.idea/libraries/proxy_vole.xml +++ b/.idea/libraries/proxy_vole.xml @@ -1,7 +1,7 @@ - + diff --git a/RegExpSupport/gen/org/intellij/lang/regexp/_RegExLexer.java b/RegExpSupport/gen/org/intellij/lang/regexp/_RegExLexer.java index b751ab59ba48..531087761da2 100644 --- a/RegExpSupport/gen/org/intellij/lang/regexp/_RegExLexer.java +++ b/RegExpSupport/gen/org/intellij/lang/regexp/_RegExLexer.java @@ -1,20 +1,21 @@ -/* The following code was generated by JFlex 1.4.3 on 10/24/14 12:41 PM */ +/* The following code was generated by JFlex 1.4.3 on 10/24/14 4:28 PM */ /* It's an automatically generated code. Do not modify it. */ package org.intellij.lang.regexp; import com.intellij.lexer.FlexLexer; -import com.intellij.psi.tree.IElementType; -import java.util.LinkedList; -import java.util.EnumSet; import com.intellij.psi.StringEscapesTokenTypes; +import com.intellij.psi.tree.IElementType; + +import java.util.ArrayList; +import java.util.EnumSet; @SuppressWarnings("ALL") /** * This class is a scanner generated by * JFlex 1.4.3 - * on 10/24/14 12:41 PM from the specification file + * on 10/24/14 4:28 PM from the specification file * /Users/bas/Projects/IntelliJ IDEA Ultimate/tools/lexer/../../community/RegExpSupport/src/org/intellij/lang/regexp/regexp-lexer.flex */ class _RegExLexer implements FlexLexer { @@ -404,12 +405,13 @@ class _RegExLexer implements FlexLexer { /* user code: */ // This adds support for nested states. I'm no JFlex pro, so maybe this is overkill, but it works quite well. - private final LinkedList states = new LinkedList(); + private final ArrayList states = new ArrayList(); // This was an idea to use the regex implementation for XML schema regexes (which use a slightly different syntax) // as well, but is currently unfinished as it requires to tweak more places than just the lexer. private boolean xmlSchemaMode; + private int capturingGroupCount = 0; private boolean allowDanglingMetacharacters; private boolean allowNestedCharacterClasses; @@ -433,11 +435,12 @@ class _RegExLexer implements FlexLexer { } private void yypushstate(int state) { - states.addFirst(yystate()); + states.add(yystate()); yybegin(state); } + private void yypopstate() { - final int state = states.removeFirst(); + final int state = states.remove(states.size() - 1); yybegin(state); } @@ -747,94 +750,85 @@ class _RegExLexer implements FlexLexer { { yybegin(YYINITIAL); return RegExpTT.GROUP_END; } case 88: break; - case 5: - { return RegExpTT.GROUP_BEGIN; - } - case 89: break; case 37: { yybegin(YYINITIAL); return RegExpTT.COLON; } - case 90: break; + case 89: break; case 6: { return RegExpTT.GROUP_END; } - case 91: break; + case 90: break; case 25: { assert false : yytext(); } - case 92: break; + case 91: break; case 18: { return commentMode ? com.intellij.psi.TokenType.WHITE_SPACE : RegExpTT.CHARACTER; } - case 93: break; + case 92: break; case 46: { return commentMode ? RegExpTT.CHARACTER : RegExpTT.REDUNDANT_ESCAPE; } - case 94: break; + case 93: break; case 23: { return RegExpTT.NAME; } - case 95: break; - case 74: - { if (allowOctalNoLeadingZero) return RegExpTT.OCT_CHAR; - return yystate() != CLASS2 ? RegExpTT.BACKREF : RegExpTT.ESC_CHARACTER; - } - case 96: break; + case 94: break; case 29: { yypopstate(); return RegExpTT.CLASS_END; } - case 97: break; + case 95: break; case 34: { yybegin(YYINITIAL); return RegExpTT.BAD_CHARACTER; } - case 98: break; + case 96: break; case 52: { yypushstate(QUOTED); return RegExpTT.QUOTE_BEGIN; } - case 99: break; + case 97: break; case 21: { return RegExpTT.NUMBER; } - case 100: break; + case 98: break; case 4: { return RegExpTT.DOT; } - case 101: break; + case 99: break; case 72: { return RegExpTT.NEG_LOOKBEHIND; } - case 102: break; + case 100: break; case 33: { yypopstate(); if (allowCategoryShorthand) return RegExpTT.CATEGORY_SHORT_HAND; else yypushback(1); } - case 103: break; + case 101: break; case 44: { return (allowOctalNoLeadingZero ? RegExpTT.OCT_CHAR : RegExpTT.BAD_OCT_VALUE); } - case 104: break; + case 102: break; case 41: // lookahead expression with fixed base length zzMarkedPos = zzStartRead + 1; { yypushstate(CLASS1); return RegExpTT.CLASS_BEGIN; } - case 105: break; + case 103: break; case 63: { return RegExpTT.NEG_LOOKAHEAD; } - case 106: break; + case 104: break; case 10: { return commentMode ? com.intellij.psi.TokenType.WHITE_SPACE : RegExpTT.CTRL_CHARACTER; } - case 107: break; + case 105: break; case 61: { return RegExpTT.NON_CAPT_GROUP; } - case 108: break; + case 106: break; case 65: { yybegin(QUOTED_NAMED_GROUP); return RegExpTT.RUBY_QUOTED_NAMED_GROUP; } - case 109: break; + case 107: break; case 66: // lookahead expression with fixed base length zzMarkedPos = zzStartRead + 1; @@ -846,7 +840,7 @@ class _RegExLexer implements FlexLexer { } return RegExpTT.CLASS_BEGIN; } - case 110: break; + case 108: break; case 28: { if (allowNestedCharacterClasses) { yypushstate(CLASS2); @@ -854,72 +848,80 @@ class _RegExLexer implements FlexLexer { } return RegExpTT.CHARACTER; } - case 111: break; + case 109: break; case 19: { if (commentMode) { yypushstate(COMMENT); return RegExpTT.COMMENT; } else return RegExpTT.CHARACTER; } - case 112: break; + case 110: break; case 51: { yypushstate(PROP); return RegExpTT.PROPERTY; } - case 113: break; + case 111: break; case 8: { yypushstate(CLASS2); return RegExpTT.CLASS_BEGIN; } - case 114: break; + case 112: break; case 71: { return RegExpTT.POS_LOOKBEHIND; } - case 115: break; - case 43: - { return yystate() != CLASS2 ? RegExpTT.BACKREF : RegExpTT.ESC_CHARACTER; - } - case 116: break; + case 113: break; case 73: { return RegExpTT.HEX_CHAR; } - case 117: break; + case 114: break; case 1: { handleOptions(); return RegExpTT.OPTIONS_ON; } - case 118: break; + case 115: break; + case 43: + { if (yystate() == CLASS2) return RegExpTT.ESC_CHARACTER; + while (yylength() > 2 && Integer.parseInt(yytext().toString().substring(1)) > capturingGroupCount) { + yypushback(1); + } + return RegExpTT.BACKREF; + } + case 116: break; case 59: { return allowNestedCharacterClasses ? RegExpTT.ANDAND : RegExpTT.CHARACTER; } - case 119: break; + case 117: break; case 2: { yypopstate(); return RegExpTT.COMMENT; } - case 120: break; + case 118: break; case 17: { return RegExpTT.MINUS; } - case 121: break; + case 119: break; case 47: { return RegExpTT.ESC_CTRL_CHARACTER; } - case 122: break; + case 120: break; case 32: { yypopstate(); yypushstate(EMBRACED); return RegExpTT.LBRACE; } - case 123: break; + case 121: break; case 13: { return RegExpTT.QUEST; } - case 124: break; + case 122: break; + case 5: + { capturingGroupCount++; return RegExpTT.GROUP_BEGIN; + } + case 123: break; case 15: { return RegExpTT.PLUS; } - case 125: break; + case 124: break; case 67: { return RegExpTT.OCT_CHAR; } - case 126: break; + case 125: break; case 39: { yybegin(YYINITIAL); return RegExpTT.QUOTE; } - case 127: break; + case 126: break; case 20: { if (allowDanglingMetacharacters) { yypopstate(); yypushback(1); @@ -927,15 +929,15 @@ class _RegExLexer implements FlexLexer { return RegExpTT.BAD_CHARACTER; } } - case 128: break; + case 127: break; case 31: { yypopstate(); yypushback(1); } - case 129: break; + case 128: break; case 27: { yybegin(CLASS1); return RegExpTT.CARET; } - case 130: break; + case 129: break; case 57: // lookahead expression with fixed base length zzMarkedPos = zzStartRead + 1; @@ -945,91 +947,100 @@ class _RegExLexer implements FlexLexer { } return RegExpTT.CHARACTER; } - case 131: break; + case 130: break; case 58: { if (!allowNestedCharacterClasses) return RegExpTT.CHARACTER; return RegExpTT.REDUNDANT_ESCAPE; } - case 132: break; + case 131: break; case 38: { yybegin(YYINITIAL); return RegExpTT.GT; } - case 133: break; + case 132: break; case 69: { yybegin(PY_NAMED_GROUP_REF); return RegExpTT.PYTHON_NAMED_GROUP_REF; } - case 134: break; + case 133: break; case 16: { return RegExpTT.UNION; } - case 135: break; + case 134: break; case 3: { return RegExpTT.CHARACTER; } - case 136: break; + case 135: break; case 64: { yybegin(NAMED_GROUP); return RegExpTT.RUBY_NAMED_GROUP; } - case 137: break; + case 136: break; case 40: { yybegin(OPTIONS); return RegExpTT.SET_OPTIONS; } - case 138: break; + case 137: break; case 26: { yybegin(CLASS2); return RegExpTT.CHARACTER; } - case 139: break; + case 138: break; case 68: { if (xmlSchemaMode) { yypushback(1); return RegExpTT.CHAR_CLASS; } else return RegExpTT.CTRL; } - case 140: break; + case 139: break; case 30: { return commentMode ? com.intellij.psi.TokenType.WHITE_SPACE : RegExpTT.ESC_CHARACTER; } - case 141: break; + case 140: break; case 12: { return RegExpTT.DOLLAR; } - case 142: break; + case 141: break; case 50: { if (xmlSchemaMode) return RegExpTT.CHAR_CLASS; else return StringEscapesTokenTypes.INVALID_CHARACTER_ESCAPE_TOKEN; } - case 143: break; + case 142: break; case 42: { return RegExpTT.REDUNDANT_ESCAPE; } - case 144: break; + case 143: break; case 55: { return (allowHexDigitClass || allowHorizontalWhitespaceClass ? RegExpTT.CHAR_CLASS : StringEscapesTokenTypes.INVALID_CHARACTER_ESCAPE_TOKEN); } - case 145: break; + case 144: break; case 48: { return yystate() != CLASS2 ? RegExpTT.BOUNDARY : RegExpTT.ESC_CHARACTER; } - case 146: break; + case 145: break; case 22: { yypopstate(); return RegExpTT.RBRACE; } - case 147: break; + case 146: break; case 54: { return StringEscapesTokenTypes.INVALID_UNICODE_ESCAPE_TOKEN; } - case 148: break; + case 147: break; case 53: { return RegExpTT.BAD_HEX_VALUE; } - case 149: break; + case 148: break; case 9: { return StringEscapesTokenTypes.INVALID_CHARACTER_ESCAPE_TOKEN; } - case 150: break; + case 149: break; case 11: { return RegExpTT.CARET; } - case 151: break; + case 150: break; case 45: { return RegExpTT.ESC_CHARACTER; } + case 151: break; + case 74: + { if (allowOctalNoLeadingZero) return RegExpTT.OCT_CHAR; + if (yystate() == CLASS2) return RegExpTT.ESC_CHARACTER; + while (yylength() > 2 && Integer.parseInt(yytext().toString().substring(1)) > capturingGroupCount) { + yypushback(1); + } + return RegExpTT.BACKREF; + } case 152: break; default: if (zzInput == YYEOF && zzStartRead == zzCurrentPos) { diff --git a/RegExpSupport/src/org/intellij/lang/regexp/RegExpLanguageHosts.java b/RegExpSupport/src/org/intellij/lang/regexp/RegExpLanguageHosts.java index ec2b9192c2d5..c1cff8aff61e 100644 --- a/RegExpSupport/src/org/intellij/lang/regexp/RegExpLanguageHosts.java +++ b/RegExpSupport/src/org/intellij/lang/regexp/RegExpLanguageHosts.java @@ -79,7 +79,12 @@ public final class RegExpLanguageHosts extends ClassExtension states = new LinkedList(); + private final ArrayList states = new ArrayList(); // This was an idea to use the regex implementation for XML schema regexes (which use a slightly different syntax) // as well, but is currently unfinished as it requires to tweak more places than just the lexer. private boolean xmlSchemaMode; + private int capturingGroupCount = 0; private boolean allowDanglingMetacharacters; private boolean allowNestedCharacterClasses; @@ -49,11 +51,12 @@ import com.intellij.psi.StringEscapesTokenTypes; } private void yypushstate(int state) { - states.addFirst(yystate()); + states.add(yystate()); yybegin(state); } + private void yypopstate() { - final int state = states.removeFirst(); + final int state = states.remove(states.size() - 1); yybegin(state); } @@ -144,15 +147,21 @@ HEX_CHAR=[0-9a-fA-F] subexpressions exist at that point in the regular expression, otherwise the parser will drop digits until the number is smaller or equal to the existing number of groups or it is one digit." - - So, for 100% compatibility, backrefs > 9 should be resolved by the parser, but - I'm not sure if it's worth the effort - at least not atm. */ {ESCAPE} [0-7]{3} { if (allowOctalNoLeadingZero) return RegExpTT.OCT_CHAR; - return yystate() != CLASS2 ? RegExpTT.BACKREF : RegExpTT.ESC_CHARACTER; + if (yystate() == CLASS2) return RegExpTT.ESC_CHARACTER; + while (yylength() > 2 && Integer.parseInt(yytext().toString().substring(1)) > capturingGroupCount) { + yypushback(1); + } + return RegExpTT.BACKREF; } -{ESCAPE} {DIGITS} { return yystate() != CLASS2 ? RegExpTT.BACKREF : RegExpTT.ESC_CHARACTER; } +{ESCAPE} {DIGITS} { if (yystate() == CLASS2) return RegExpTT.ESC_CHARACTER; + while (yylength() > 2 && Integer.parseInt(yytext().toString().substring(1)) > capturingGroupCount) { + yypushback(1); + } + return RegExpTT.BACKREF; + } {ESCAPE} "-" { return RegExpTT.ESC_CHARACTER; } {ESCAPE} {META} { return RegExpTT.ESC_CHARACTER; } @@ -256,7 +265,7 @@ HEX_CHAR=[0-9a-fA-F] { - {LPAREN} { return RegExpTT.GROUP_BEGIN; } + {LPAREN} { capturingGroupCount++; return RegExpTT.GROUP_BEGIN; } {RPAREN} { return RegExpTT.GROUP_END; } "|" { return RegExpTT.UNION; } diff --git a/RegExpSupport/testData/RETest.xml b/RegExpSupport/testData/RETest.xml index 88ace1f83294..f42b9af45c51 100644 --- a/RegExpSupport/testData/RETest.xml +++ b/RegExpSupport/testData/RETest.xml @@ -730,6 +730,14 @@ ([ab]+)=\1 OK + + (a)(b)(c)(d)(e)(f)(g)(h)(i)(j)\105 + OK + + + (.)\11 + OK + ([ab]+)=\2 ERR diff --git a/build/scripts/libLicenses.gant b/build/scripts/libLicenses.gant index 17f419dece1d..ebfcc890988a 100644 --- a/build/scripts/libLicenses.gant +++ b/build/scripts/libLicenses.gant @@ -262,7 +262,7 @@ libraryLicense(name: "Snappy-Java", libraryName: "Snappy-Java", version: "0.3", libraryLicense(name: "Cucumber-Java", libraryName: "cucumber-java", version: "1.0.14", license: "MIT License", url: "https://github.com/cucumber/cucumber-jvm/", licenseUrl: "http://www.opensource.org/licenses/mit-license.html") libraryLicense(name: "Cucumber-JVM", libraryName: "cucumber-jvm", version: "1.0.14", license: "MIT License", url: "https://github.com/cucumber/cucumber-jvm/", licenseUrl: "http://www.opensource.org/licenses/mit-license.html") libraryLicense(name: "Cucumber-Groovy", libraryName: "cucumber-groovy", version: "1.0.14", license: "MIT License", url: "https://github.com/cucumber/cucumber-jvm/", licenseUrl: "http://www.opensource.org/licenses/mit-license.html") -libraryLicense(name: "proxy-vole", libraryName: "proxy-vole", version: "20120920", license: "New BSD License", url: "http://code.google.com/p/proxy-vole/", licenseUrl: "http://opensource.org/licenses/BSD-3-Clause") +libraryLicense(name: "proxy-vole", libraryName: "proxy-vole", version: "20131209", license: "New BSD License", url: "http://code.google.com/p/proxy-vole/", licenseUrl: "http://opensource.org/licenses/BSD-3-Clause") libraryLicense(name: "Rhino JavaScript Engine", libraryName: "rhino-js-1_7R4", version: "1.7R4", license: "MPL 1.1", url: "http://www.mozilla.org/rhino/", licenseUrl: "http://www.mozilla.org/MPL/MPL-1.1.html") libraryLicense(name: "asm-4.0-all", libraryName: "asm-4.0-all", version: "4.0", attachedTo: "ByteCodeViewer", license: "BSD", url: "http://asm.objectweb.org/", licenseUrl: "http://asm.objectweb.org/license.html") libraryLicense(name: "jsr305", libraryName: "jsr305", version: "snapshot", license: "BSD", url: "http://code.google.com/p/jsr-305/", licenseUrl: "http://code.google.com/p/jsr-305/source/browse/trunk/ri/LICENSE") diff --git a/build/scripts/utils.gant b/build/scripts/utils.gant index 2799ae550865..8e2376b71c55 100644 --- a/build/scripts/utils.gant +++ b/build/scripts/utils.gant @@ -691,6 +691,26 @@ private String submapToXml(Map properties, List keys) { return buff.toString() } +private List getChildren(File file) { + if (!file.isDirectory()) return [] + return file.listFiles().sort { File f -> f.name.toLowerCase() } +} + +binding.setVariable("getCustomJDK", { File customJdkDir -> + getChildren(customJdkDir).each { + if (it.getName().endsWith(".tar.gz")) { + def customJdkFileName = it.getAbsolutePath().substring(0, it.getAbsolutePath().length() - 3) + if (new File(customJdkFileName).exists()){ ant.delete(file: customJdkFileName) } + if (new File("${home}/build/jdk/custom_jdk_${buildNumber}.tar").exists()){ + ant.delete(file: "${home}/build/jdk/custom_jdk_${buildNumber}.tar") + } + ant.gunzip(src: it.getAbsolutePath()) + projectBuilder.info("customJdkFileName: " + customJdkFileName) + ant.copy(file: customJdkFileName, tofile: "${home}/build/jdk/custom_jdk_${buildNumber}.tar") + } + } +}) + binding.setVariable("buildWinZip", { String zipPath, List paths -> projectBuilder.stage(".win.zip") diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java b/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java index a35a03630ad9..a5685017d365 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java @@ -73,7 +73,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; import org.jetbrains.jps.api.CmdlineProtoUtil; import org.jetbrains.jps.api.CmdlineRemoteProto; -import org.jetbrains.jps.api.RequestFuture; +import org.jetbrains.jps.api.TaskFuture; import org.jetbrains.jps.model.java.JavaSourceRootType; import javax.swing.*; @@ -138,7 +138,7 @@ public class CompileDriver { return; } try { - final RequestFuture future = compileInExternalProcess(compileContext, true); + final TaskFuture future = compileInExternalProcess(compileContext, true); if (future != null) { while (!future.waitFor(200L, TimeUnit.MILLISECONDS)) { if (indicator.isCanceled()) { @@ -194,7 +194,7 @@ public class CompileDriver { } @Nullable - private RequestFuture compileInExternalProcess(final @NotNull CompileContextImpl compileContext, final boolean onlyCheckUpToDate) + private TaskFuture compileInExternalProcess(final @NotNull CompileContextImpl compileContext, final boolean onlyCheckUpToDate) throws Exception { final CompileScope scope = compileContext.getCompileScope(); final Collection paths = CompileScopeUtil.fetchFiles(compileContext); @@ -400,7 +400,7 @@ public class CompileDriver { return; } - final RequestFuture future = compileInExternalProcess(compileContext, false); + final TaskFuture future = compileInExternalProcess(compileContext, false); if (future != null) { while (!future.waitFor(200L, TimeUnit.MILLISECONDS)) { if (indicator.isCanceled()) { @@ -646,7 +646,6 @@ public class CompileDriver { final Module[] scopeModules = scope.getAffectedModules(); final List modulesWithoutOutputPathSpecified = new ArrayList(); final List modulesWithoutJdkAssigned = new ArrayList(); - final CompilerConfiguration config = CompilerConfiguration.getInstance(myProject); final CompilerManager compilerManager = CompilerManager.getInstance(myProject); for (final Module module : scopeModules) { if (!compilerManager.isValidationEnabled(module)) { 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 bc31490a54b6..efaf9e2b4346 100644 --- a/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java +++ b/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java @@ -58,10 +58,7 @@ import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl; import com.intellij.openapi.roots.*; import com.intellij.openapi.startup.StartupManager; -import com.intellij.openapi.util.Disposer; -import com.intellij.openapi.util.Key; -import com.intellij.openapi.util.ShutDownTracker; -import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.*; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; @@ -112,8 +109,7 @@ import java.net.InetSocketAddress; import java.nio.charset.Charset; import java.util.*; import java.util.List; -import java.util.concurrent.RejectedExecutionException; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; import static org.jetbrains.jps.api.CmdlineRemoteProto.Message.ControllerMessage.ParametersMessage.TargetTypeBuildScope; @@ -152,8 +148,10 @@ public class BuildManager implements ApplicationComponent{ private final File mySystemDirectory; private final ProjectManager myProjectManager; - private final Map myAutomakeFutures = Collections.synchronizedMap(new HashMap()); + private final Map myAutomakeFutures = Collections.synchronizedMap(new HashMap()); private final Map myBuildsInProgress = Collections.synchronizedMap(new HashMap()); + private final Map, OSProcessHandler>>> myPreloadedBuilds = + Collections.synchronizedMap(new HashMap, OSProcessHandler>>>()); private final BuildProcessClasspathManager myClasspathManager = new BuildProcessClasspathManager(); private final SequentialTaskExecutor myRequestsProcessor = new SequentialTaskExecutor(PooledThreadExecutor.INSTANCE); private final Map myProjectDataMap = Collections.synchronizedMap(new HashMap()); @@ -385,6 +383,8 @@ public class BuildManager implements ApplicationComponent{ } public void clearState(Project project) { + cancelPreloadedBuilds(project); + final String projectPath = getProjectPath(project); synchronized (myProjectDataMap) { final ProjectData data = myProjectDataMap.get(projectPath); @@ -447,7 +447,7 @@ public class BuildManager implements ApplicationComponent{ } final List scopes = CmdlineProtoUtil.createAllModulesScopes(false); final AutoMakeMessageHandler handler = new AutoMakeMessageHandler(project); - final RequestFuture future = scheduleBuild( + final TaskFuture future = scheduleBuild( project, false, true, false, scopes, Collections.emptyList(), Collections.emptyMap(), handler ); if (future != null) { @@ -526,12 +526,12 @@ public class BuildManager implements ApplicationComponent{ return false; } - public Collection cancelAutoMakeTasks(Project project) { - final Collection futures = new SmartList(); + public Collection cancelAutoMakeTasks(Project project) { + final Collection futures = new SmartList(); synchronized (myAutomakeFutures) { - for (Map.Entry entry : myAutomakeFutures.entrySet()) { + for (Map.Entry entry : myAutomakeFutures.entrySet()) { if (entry.getValue().equals(project)) { - final RequestFuture future = entry.getKey(); + final TaskFuture future = entry.getKey(); future.cancel(false); futures.add(future); } @@ -540,211 +540,261 @@ public class BuildManager implements ApplicationComponent{ return futures; } + private void cancelPreloadedBuilds(Project project) { + final String projectPath = getProjectPath(project); + runCommand(new Runnable() { + @Override + public void run() { + Pair, OSProcessHandler> pair = takePreloadedProcess(projectPath); + if (pair != null) { + final RequestFuture future = pair.first; + myMessageDispatcher.cancelSession(future.getRequestID()); + // waiting for preloaded process from project's task queue guarantees no build is started for this project + // until this one gracefully exits and closes all its storages + getProjectData(projectPath).taskQueue.submit(new Runnable() { + @Override + public void run() { + future.waitFor(); + } + }); + } + } + }); + } + @Nullable - public RequestFuture scheduleBuild( + private Pair, OSProcessHandler> takePreloadedProcess(String projectPath) { + Pair, OSProcessHandler> result; + final Future, OSProcessHandler>> preloadProgress = myPreloadedBuilds.remove(projectPath); + try { + result = preloadProgress != null ? preloadProgress.get() : null; + } + catch (Throwable e) { + LOG.info(e); + result = null; + } + return result; + } + + @Nullable + public TaskFuture scheduleBuild( final Project project, final boolean isRebuild, final boolean isMake, final boolean onlyCheckUpToDate, final List scopes, final Collection paths, final Map userData, final DefaultMessageHandler messageHandler) { final String projectPath = getProjectPath(project); - final UUID sessionId = UUID.randomUUID(); - final boolean isAutomake = messageHandler instanceof AutoMakeMessageHandler; - final BuilderMessageHandler handler = new MessageHandlerWrapper(messageHandler) { - @Override - public void buildStarted(UUID sessionId) { - super.buildStarted(sessionId); - try { - ApplicationManager.getApplication().getMessageBus().syncPublisher(BuildManagerListener.TOPIC).buildStarted(project, sessionId, isAutomake); - } - catch (Throwable e) { - LOG.error(e); - } - } - - @Override - public void sessionTerminated(UUID sessionId) { - try { - super.sessionTerminated(sessionId); - } - finally { - try { - ApplicationManager.getApplication().getMessageBus().syncPublisher(BuildManagerListener.TOPIC).buildFinished(project, sessionId, isAutomake); - } - catch (Throwable e) { - LOG.error(e); - } - } - } - }; - // ensure server is listening - if (myListenPort < 0) { - try { - synchronized (this) { - if (myListenPort < 0) { - myListenPort = startListening(); - } - } - } - catch (Exception e) { - handler.handleFailure(sessionId, CmdlineProtoUtil.createFailure(e.getMessage(), null)); - handler.sessionTerminated(sessionId); - return null; - } + final BuilderMessageHandler handler = new NotifyingMessageHandler(project, messageHandler, messageHandler instanceof AutoMakeMessageHandler); + try { + ensureListening(); + } + catch (Exception e) { + final UUID sessionId = UUID.randomUUID(); // the actual session did not start, use random UUID + handler.handleFailure(sessionId, CmdlineProtoUtil.createFailure(e.getMessage(), null)); + handler.sessionTerminated(sessionId); + return null; } - try { - final RequestFuture future = new RequestFuture(handler, sessionId, new RequestFuture.CancelAction() { - @Override - public void cancel(RequestFuture future) throws Exception { - myMessageDispatcher.cancelSession(future.getRequestID()); + final DelegateFuture _future = new DelegateFuture(); + // by using the same queue that processes events we ensure that + // the build will be aware of all events that have happened before this request + runCommand(new Runnable() { + @Override + public void run() { + + final Pair, OSProcessHandler> preloaded = takePreloadedProcess(projectPath); + final RequestFuture preloadedFuture = preloaded != null? preloaded.first : null; + final boolean usingPreloadedProcess = preloadedFuture != null; + + final UUID sessionId; + if (usingPreloadedProcess) { + LOG.info("Using preloaded build process to compile " + projectPath); + sessionId = preloadedFuture.getRequestID(); + preloadedFuture.getMessageHandler().setDelegateHandler(handler); + } + else { + sessionId = UUID.randomUUID(); } - }); - // by using the same queue that processes events we ensure that - // the build will be aware of all events that have happened before this request - runCommand(new Runnable() { - @Override - public void run() { - if (future.isCancelled() || project.isDisposed()) { - handler.sessionTerminated(sessionId); - future.setDone(); - return; - } - final CmdlineRemoteProto.Message.ControllerMessage.GlobalSettings globals = - CmdlineRemoteProto.Message.ControllerMessage.GlobalSettings.newBuilder() - .setGlobalOptionsPath(PathManager.getOptionsPath()) - .build(); - CmdlineRemoteProto.Message.ControllerMessage.FSEvent currentFSChanges; - final SequentialTaskExecutor projectTaskQueue; - synchronized (myProjectDataMap) { - ProjectData data = myProjectDataMap.get(projectPath); - if (data == null) { - data = new ProjectData(new SequentialTaskExecutor(PooledThreadExecutor.INSTANCE)); - myProjectDataMap.put(projectPath, data); - } - if (isRebuild) { - data.dropChanges(); - } - if (IS_UNIT_TEST_MODE) { - LOG.info("Scheduling build for " + - projectPath + - "; CHANGED: " + - new HashSet(convertToStringPaths(data.myChanged)) + - "; DELETED: " + - new HashSet(convertToStringPaths(data.myDeleted))); - } - currentFSChanges = data.getAndResetRescanFlag() ? null : data.createNextEvent(); - projectTaskQueue = data.taskQueue; + final RequestFuture future = usingPreloadedProcess? preloadedFuture : new RequestFuture(handler, sessionId, new RequestFuture.CancelAction() { + @Override + public void cancel(RequestFuture future) throws Exception { + myMessageDispatcher.cancelSession(future.getRequestID()); } + }); + _future.setDelegate(future); - final CmdlineRemoteProto.Message.ControllerMessage params; + if (!usingPreloadedProcess && (future.isCancelled() || project.isDisposed())) { + // in case of preloaded process the process was already running, so the handler will be notified upon process termination + handler.sessionTerminated(sessionId); + ((BasicFuture)future).setDone(); + return; + } + + final CmdlineRemoteProto.Message.ControllerMessage.GlobalSettings globals = + CmdlineRemoteProto.Message.ControllerMessage.GlobalSettings.newBuilder().setGlobalOptionsPath(PathManager.getOptionsPath()).build(); + CmdlineRemoteProto.Message.ControllerMessage.FSEvent currentFSChanges; + final SequentialTaskExecutor projectTaskQueue; + synchronized (myProjectDataMap) { + final ProjectData data = getProjectData(projectPath); if (isRebuild) { - params = CmdlineProtoUtil.createBuildRequest(projectPath, scopes, Collections.emptyList(), userData, globals, null); + data.dropChanges(); } - else if (onlyCheckUpToDate) { - params = CmdlineProtoUtil.createUpToDateCheckRequest(projectPath, scopes, paths, userData, globals, currentFSChanges); - } - else { - params = CmdlineProtoUtil.createBuildRequest(projectPath, scopes, isMake ? Collections.emptyList() : paths, - userData, globals, currentFSChanges); + if (IS_UNIT_TEST_MODE) { + LOG.info("Scheduling build for " + + projectPath + + "; CHANGED: " + + new HashSet(convertToStringPaths(data.myChanged)) + + "; DELETED: " + + new HashSet(convertToStringPaths(data.myDeleted))); } + currentFSChanges = data.getAndResetRescanFlag() ? null : data.createNextEvent(); + projectTaskQueue = data.taskQueue; + } - myMessageDispatcher.registerBuildMessageHandler(sessionId, new MessageHandlerWrapper(handler) { + final CmdlineRemoteProto.Message.ControllerMessage params; + if (isRebuild) { + params = CmdlineProtoUtil.createBuildRequest(projectPath, scopes, Collections.emptyList(), userData, globals, null); + } + else if (onlyCheckUpToDate) { + params = CmdlineProtoUtil.createUpToDateCheckRequest(projectPath, scopes, paths, userData, globals, currentFSChanges); + } + else { + params = CmdlineProtoUtil.createBuildRequest(projectPath, scopes, isMake ? Collections.emptyList() : paths, userData, globals, currentFSChanges); + } + if (!usingPreloadedProcess) { + myMessageDispatcher.registerBuildMessageHandler(future, params); + } + + try { + projectTaskQueue.submit(new Runnable() { @Override - public void sessionTerminated(UUID sessionId) { + public void run() { + Throwable execFailure = null; try { - super.sessionTerminated(sessionId); - } - finally { - future.setDone(); - } - } - }, params); - - try { - projectTaskQueue.submit(new Runnable() { - @Override - public void run() { - Throwable execFailure = null; - try { - if (project.isDisposed()) { + if (project.isDisposed()) { + if (usingPreloadedProcess) { + future.cancel(true); + } + else { return; } - myBuildsInProgress.put(projectPath, future); - final OSProcessHandler processHandler = launchBuildProcess(project, myListenPort, sessionId); - final StringBuilder stdErrOutput = new StringBuilder(); + } + myBuildsInProgress.put(projectPath, future); + final OSProcessHandler processHandler; + final StringBuilder errorsOnLaunch = new StringBuilder(); + if (usingPreloadedProcess) { + final boolean paramsSent = myMessageDispatcher.sendBuildParameters(future.getRequestID(), params); + if (!paramsSent) { + myMessageDispatcher.cancelSession(future.getRequestID()); + } + processHandler = preloaded.second; + } + else { + processHandler = launchBuildProcess(project, myListenPort, sessionId, false); processHandler.addProcessListener(new ProcessAdapter() { @Override public void onTextAvailable(ProcessEvent event, Key outputType) { - // re-translate builder's output to idea.log - final String text = event.getText(); - if (!StringUtil.isEmptyOrSpaces(text)) { - LOG.info("BUILDER_PROCESS [" + outputType.toString() + "]: " + text.trim()); - if (stdErrOutput.length() < 1024 && ProcessOutputTypes.STDERR.equals(outputType)) { - stdErrOutput.append(text); + if (ProcessOutputTypes.STDERR.equals(outputType)) { + if (errorsOnLaunch.length() < 1024) { + final String text = event.getText(); + if (!StringUtil.isEmptyOrSpaces(text)) { + errorsOnLaunch.append(text); + } } } } }); processHandler.startNotify(); - final boolean terminated = processHandler.waitFor(); - if (terminated) { - final int exitValue = processHandler.getProcess().exitValue(); - if (exitValue != 0) { - final StringBuilder msg = new StringBuilder(); - msg.append("Abnormal build process termination: "); - if (stdErrOutput.length() > 0) { - msg.append("\n").append(stdErrOutput); - } - else { - msg.append("unknown error"); - } - handler.handleFailure(sessionId, CmdlineProtoUtil.createFailure(msg.toString(), null)); + } + + final boolean terminated = processHandler.waitFor(); + if (terminated) { + final int exitValue = processHandler.getProcess().exitValue(); + if (exitValue != 0) { + final StringBuilder msg = new StringBuilder(); + msg.append("Abnormal build process termination: "); + if (errorsOnLaunch.length() > 0) { + msg.append("\n").append(errorsOnLaunch); } - } - else { - handler.handleFailure(sessionId, CmdlineProtoUtil.createFailure("Disconnected from build process", null)); + else { + msg.append("unknown error"); + } + handler.handleFailure(sessionId, CmdlineProtoUtil.createFailure(msg.toString(), null)); } } - catch (Throwable e) { - execFailure = e; - } - finally { - myBuildsInProgress.remove(projectPath); - if (myMessageDispatcher.getAssociatedChannel(sessionId) == null) { - // either the connection has never been established (process not started or execution failed), or no messages were sent from the launched process. - // in this case the session cannot be unregistered by the message dispatcher - final BuilderMessageHandler unregistered = myMessageDispatcher.unregisterBuildMessageHandler(sessionId); - if (unregistered != null) { - if (execFailure != null) { - unregistered.handleFailure(sessionId, CmdlineProtoUtil.createFailure(execFailure.getMessage(), execFailure)); - } - unregistered.sessionTerminated(sessionId); - } - } + else { + handler.handleFailure(sessionId, CmdlineProtoUtil.createFailure("Disconnected from build process", null)); } } - }); - } - catch (Throwable e) { - final BuilderMessageHandler unregistered = myMessageDispatcher.unregisterBuildMessageHandler(sessionId); - if (unregistered != null) { - unregistered.handleFailure(sessionId, CmdlineProtoUtil.createFailure(e.getMessage(), e)); - unregistered.sessionTerminated(sessionId); + catch (Throwable e) { + execFailure = e; + } + finally { + myBuildsInProgress.remove(projectPath); + if (myMessageDispatcher.getAssociatedChannel(sessionId) == null) { + // either the connection has never been established (process not started or execution failed), or no messages were sent from the launched process. + // in this case the session cannot be unregistered by the message dispatcher + final BuilderMessageHandler unregistered = myMessageDispatcher.unregisterBuildMessageHandler(sessionId); + if (unregistered != null) { + if (execFailure != null) { + unregistered.handleFailure(sessionId, CmdlineProtoUtil.createFailure(execFailure.getMessage(), execFailure)); + } + unregistered.sessionTerminated(sessionId); + } + } + + if (Registry.is("compiler.process.preload") && !project.isDisposed()) { + runCommand(new Runnable() { + public void run() { + try { + final Future, OSProcessHandler>> preloadResult = launchPreloadedBuildProcess(project, projectTaskQueue); + myPreloadedBuilds.put(projectPath, preloadResult); + } + catch (Exception e) { + LOG.info("Error pre-loading build process for project " + projectPath, e); + } + } + }); + } + + } } + }); + } + catch (Throwable e) { + final BuilderMessageHandler unregistered = myMessageDispatcher.unregisterBuildMessageHandler(sessionId); + if (unregistered != null) { + unregistered.handleFailure(sessionId, CmdlineProtoUtil.createFailure(e.getMessage(), e)); + unregistered.sessionTerminated(sessionId); } } - }); + } + }); - return future; - } - catch (Throwable e) { - handler.handleFailure(sessionId, CmdlineProtoUtil.createFailure(e.getMessage(), e)); - handler.sessionTerminated(sessionId); - } + return _future; + } - return null; + @NotNull + private ProjectData getProjectData(String projectPath) { + synchronized (myProjectDataMap) { + ProjectData data = myProjectDataMap.get(projectPath); + if (data == null) { + data = new ProjectData(new SequentialTaskExecutor(PooledThreadExecutor.INSTANCE)); + myProjectDataMap.put(projectPath, data); + } + return data; + } + } + + private void ensureListening() throws Exception { + if (myListenPort < 0) { + synchronized (this) { + if (myListenPort < 0) { + myListenPort = startListening(); + } + } + } } @Override @@ -762,7 +812,45 @@ public class BuildManager implements ApplicationComponent{ return "com.intellij.compiler.server.BuildManager"; } - private OSProcessHandler launchBuildProcess(Project project, final int port, final UUID sessionId) throws ExecutionException { + private Future, OSProcessHandler>> launchPreloadedBuildProcess(final Project project, SequentialTaskExecutor projectTaskQueue) throws Exception { + ensureListening(); + + // launching build process from projectTaskQueue ensures that no other build process for this project is currently running + return projectTaskQueue.submit(new Callable, OSProcessHandler>>() { + public Pair, OSProcessHandler> call() throws Exception { + final RequestFuture future = new RequestFuture(new PreloadedProcessMessageHandler(project), UUID.randomUUID(), new RequestFuture.CancelAction() { + @Override + public void cancel(RequestFuture future) throws Exception { + myMessageDispatcher.cancelSession(future.getRequestID()); + } + }); + try { + myMessageDispatcher.registerBuildMessageHandler(future, null); + final OSProcessHandler processHandler = launchBuildProcess(project, myListenPort, future.getRequestID(), true); + processHandler.addProcessListener(new ProcessAdapter() { + @Override + public void onTextAvailable(ProcessEvent event, Key outputType) { + if (ProcessOutputTypes.STDERR.equals(outputType)) { + final String text = event.getText(); + if (!StringUtil.isEmptyOrSpaces(text)) { + LOG.info("PRELOADED_BUILD_PROCESS: " + text); + } + } + } + }); + + processHandler.startNotify(); + return Pair.create(future, processHandler); + } + catch (ExecutionException e) { + myMessageDispatcher.unregisterBuildMessageHandler(future.getRequestID()); + throw e; + } + } + }); + } + + private OSProcessHandler launchBuildProcess(Project project, final int port, final UUID sessionId, boolean requestProjectPreload) throws ExecutionException { final String compilerPath; final String vmExecutablePath; JavaSdkVersion sdkVersion = null; @@ -863,6 +951,11 @@ public class BuildManager implements ApplicationComponent{ } cmdLine.addParameter("-Djdt.compiler.useSingleThread=true"); // always run eclipse compiler in single-threaded mode + if (requestProjectPreload) { + cmdLine.addParameter("-Dpreload.project.path=" + FileUtil.toCanonicalPath(getProjectPath(project))); + cmdLine.addParameter("-Dpreload.config.path=" + FileUtil.toCanonicalPath(PathManager.getOptionsPath())); + } + final String shouldGenerateIndex = System.getProperty(GlobalOptions.GENERATE_CLASSPATH_INDEX_OPTION); if (shouldGenerateIndex != null) { cmdLine.addParameter("-D"+ GlobalOptions.GENERATE_CLASSPATH_INDEX_OPTION +"=" + shouldGenerateIndex); @@ -961,12 +1054,24 @@ public class BuildManager implements ApplicationComponent{ final Process process = cmdLine.createProcess(); - return new OSProcessHandler(process, null, mySystemCharset) { + final OSProcessHandler processHandler = new OSProcessHandler(process, null, mySystemCharset) { @Override protected boolean shouldDestroyProcessRecursively() { return true; } }; + processHandler.addProcessListener(new ProcessAdapter() { + @Override + public void onTextAvailable(ProcessEvent event, Key outputType) { + // re-translate builder's output to idea.log + final String text = event.getText(); + if (!StringUtil.isEmptyOrSpaces(text)) { + LOG.info("BUILDER_PROCESS [" + outputType.toString() + "]: " + text.trim()); + } + } + }); + + return processHandler; } public File getBuildSystemDirectory() { @@ -1086,6 +1191,50 @@ public class BuildManager implements ApplicationComponent{ } } + private static class NotifyingMessageHandler extends DelegatingMessageHandler { + private final Project myProject; + private final BuilderMessageHandler myDelegateHandler; + private boolean myIsAutomake; + + public NotifyingMessageHandler(@NotNull Project project, @NotNull BuilderMessageHandler delegateHandler, final boolean isAutomake) { + myProject = project; + myDelegateHandler = delegateHandler; + myIsAutomake = isAutomake; + } + + @Override + protected BuilderMessageHandler getDelegateHandler() { + return myDelegateHandler; + } + + @Override + public void buildStarted(UUID sessionId) { + super.buildStarted(sessionId); + try { + ApplicationManager + .getApplication().getMessageBus().syncPublisher(BuildManagerListener.TOPIC).buildStarted(myProject, sessionId, myIsAutomake); + } + catch (Throwable e) { + LOG.error(e); + } + } + + @Override + public void sessionTerminated(UUID sessionId) { + try { + super.sessionTerminated(sessionId); + } + finally { + try { + ApplicationManager.getApplication().getMessageBus().syncPublisher(BuildManagerListener.TOPIC).buildFinished(myProject, sessionId, myIsAutomake); + } + catch (Throwable e) { + LOG.error(e); + } + } + } + } + private class ProjectWatcher extends ProjectManagerAdapter { private final Map myConnections = new HashMap(); @@ -1169,7 +1318,8 @@ public class BuildManager implements ApplicationComponent{ @Override public void projectClosing(Project project) { - for (RequestFuture future : cancelAutoMakeTasks(project)) { + cancelPreloadedBuilds(project); + for (TaskFuture future : cancelAutoMakeTasks(project)) { future.waitFor(500, TimeUnit.MILLISECONDS); } } @@ -1185,13 +1335,14 @@ public class BuildManager implements ApplicationComponent{ } private static class ProjectData { + @NotNull final SequentialTaskExecutor taskQueue; private final Set myChanged = new THashSet(); private final Set myDeleted = new THashSet(); private long myNextEventOrdinal = 0L; private boolean myNeedRescan = true; - private ProjectData(SequentialTaskExecutor taskQueue) { + private ProjectData(@NotNull SequentialTaskExecutor taskQueue) { this.taskQueue = taskQueue; } @@ -1328,5 +1479,88 @@ public class BuildManager implements ApplicationComponent{ return "/"; } } - + + private static final class DelegateFuture implements TaskFuture { + @Nullable + private TaskFuture myDelegate; + private Boolean myRequestedCancelState = null; + + @NotNull + public synchronized TaskFuture getDelegate() { + TaskFuture delegate = myDelegate; + while (delegate == null) { + try { + wait(); + } + catch (InterruptedException ignored) { + } + delegate = myDelegate; + } + return delegate; + } + + public synchronized boolean setDelegate(@NotNull TaskFuture delegate) { + if (myDelegate == null) { + try { + myDelegate = delegate; + if (myRequestedCancelState != null) { + myDelegate.cancel(myRequestedCancelState); + } + } + finally { + notifyAll(); + } + return true; + } + return false; + } + + public synchronized boolean cancel(boolean mayInterruptIfRunning) { + final TaskFuture delegate = myDelegate; + if (delegate == null) { + myRequestedCancelState = mayInterruptIfRunning; + return true; + } + return delegate.cancel(mayInterruptIfRunning); + } + + public void waitFor() { + getDelegate().waitFor(); + } + + public boolean waitFor(long timeout, TimeUnit unit) { + return getDelegate().waitFor(timeout, unit); + } + + public boolean isCancelled() { + final TaskFuture delegate; + synchronized (this) { + delegate = myDelegate; + if (delegate == null) { + return myRequestedCancelState != null; + } + } + return delegate.isCancelled(); + } + + public boolean isDone() { + final TaskFuture delegate; + synchronized (this) { + delegate = myDelegate; + if (delegate == null) { + return false; + } + } + return delegate.isDone(); + } + + public T get() throws InterruptedException, java.util.concurrent.ExecutionException { + return getDelegate().get(); + } + + public T get(long timeout, TimeUnit unit) throws InterruptedException, java.util.concurrent.ExecutionException, TimeoutException { + return getDelegate().get(timeout, unit); + } + } + } diff --git a/java/compiler/impl/src/com/intellij/compiler/server/BuildMessageDispatcher.java b/java/compiler/impl/src/com/intellij/compiler/server/BuildMessageDispatcher.java index d324a620155b..59c19f0ad7fe 100644 --- a/java/compiler/impl/src/com/intellij/compiler/server/BuildMessageDispatcher.java +++ b/java/compiler/impl/src/com/intellij/compiler/server/BuildMessageDispatcher.java @@ -21,10 +21,12 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.util.AttributeKey; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.io.SimpleChannelInboundHandlerAdapter; import org.jetbrains.jps.api.CmdlineProtoUtil; import org.jetbrains.jps.api.CmdlineRemoteProto; +import org.jetbrains.jps.api.RequestFuture; import java.util.Map; import java.util.Set; @@ -41,19 +43,34 @@ class BuildMessageDispatcher extends SimpleChannelInboundHandlerAdapter SESSION_DATA = AttributeKey.valueOf("BuildMessageDispatcher.sessionData"); - private final Map myMessageHandlers = new ConcurrentHashMap(16, 0.75f, 1); + private final Map mySessionDescriptors = new ConcurrentHashMap(16, 0.75f, 1); private final Set myCanceledSessions = new ConcurrentHashSet(); - public void registerBuildMessageHandler(UUID sessionId, - BuilderMessageHandler handler, - CmdlineRemoteProto.Message.ControllerMessage params) { - myMessageHandlers.put(sessionId, new SessionData(sessionId, handler, params)); + public void registerBuildMessageHandler(@NotNull final RequestFuture future, @Nullable CmdlineRemoteProto.Message.ControllerMessage params) { + final BuilderMessageHandler wrappedHandler = new DelegatingMessageHandler() { + @Override + protected BuilderMessageHandler getDelegateHandler() { + return future.getMessageHandler(); + } + + @Override + public void sessionTerminated(UUID sessionId) { + try { + super.sessionTerminated(sessionId); + } + finally { + future.setDone(); + } + } + }; + final UUID sessionId = future.getRequestID(); + mySessionDescriptors.put(sessionId, new SessionData(sessionId, wrappedHandler, params)); } @Nullable public BuilderMessageHandler unregisterBuildMessageHandler(UUID sessionId) { myCanceledSessions.remove(sessionId); - final SessionData data = myMessageHandlers.remove(sessionId); + final SessionData data = mySessionDescriptors.remove(sessionId); return data != null? data.handler : null; } @@ -74,11 +91,36 @@ class BuildMessageDispatcher extends SimpleChannelInboundHandlerAdapter, // Type @NotNull private static final Set SUPPORTED_TYPES = ContainerUtilRt.newLinkedHashSet( - FIELD, CONSTRUCTOR, METHOD, CLASS, INTERFACE, ENUM, GETTER, SETTER, OVERRIDDEN + FIELD, INIT_BLOCK, CONSTRUCTOR, METHOD, CLASS, INTERFACE, ENUM, GETTER, SETTER, OVERRIDDEN ); // Modifier @NotNull private static final Set SUPPORTED_MODIFIERS = @@ -79,6 +81,9 @@ public class JavaRearranger implements Rearranger, @NotNull private static final Collection> MUTEXES = ContainerUtilRt.newArrayList(); + private static final Set TYPES_WITH_DISABLED_ORDER = ContainerUtil.newHashSet(); + private static final Set TYPES_WITH_DISABLED_NAME_MATCH = ContainerUtil.newHashSet(); + static { Set visibilityModifiers = ContainerUtilRt.newHashSet(PUBLIC, PROTECTED, PACKAGE_PRIVATE, PRIVATE); MUTEXES.add(visibilityModifiers); @@ -96,6 +101,11 @@ public class JavaRearranger implements Rearranger, MODIFIERS_BY_TYPE.put(GETTER, ContainerUtilRt.newHashSet()); MODIFIERS_BY_TYPE.put(SETTER, ContainerUtilRt.newHashSet()); MODIFIERS_BY_TYPE.put(OVERRIDDEN, ContainerUtilRt.newHashSet()); + MODIFIERS_BY_TYPE.put(INIT_BLOCK, ContainerUtilRt.newHashSet(STATIC)); + + TYPES_WITH_DISABLED_ORDER.add(INIT_BLOCK); + + TYPES_WITH_DISABLED_NAME_MATCH.add(INIT_BLOCK); } private static final Map> GROUPING_RULES = ContainerUtilRt.newLinkedHashMap(); @@ -129,6 +139,8 @@ public class JavaRearranger implements Rearranger, for (ArrangementSettingsToken modifier : visibility) { and(matchRules, FIELD, STATIC, modifier); } + and(matchRules, INIT_BLOCK, STATIC); + for (ArrangementSettingsToken modifier : visibility) { and(matchRules, FIELD, FINAL, modifier); } @@ -136,6 +148,7 @@ public class JavaRearranger implements Rearranger, and(matchRules, FIELD, modifier); } and(matchRules, FIELD); + and(matchRules, INIT_BLOCK); and(matchRules, CONSTRUCTOR); and(matchRules, METHOD, STATIC); and(matchRules, METHOD); @@ -304,10 +317,14 @@ public class JavaRearranger implements Rearranger, } CommonCodeStyleSettings commonSettings = settings.getCommonSettings(JavaLanguage.INSTANCE); + JavaCodeStyleSettings javaSettings = settings.getCustomSettings(JavaCodeStyleSettings.class); if (FIELD.equals(target.getType())) { if (parent != null && parent.getType() == INTERFACE) { return commonSettings.BLANK_LINES_AROUND_FIELD_IN_INTERFACE; } + else if (INIT_BLOCK.equals(previous.getType())) { + return javaSettings.BLANK_LINES_AROUND_INITIALIZER; + } else { return commonSettings.BLANK_LINES_AROUND_FIELD; } @@ -323,6 +340,9 @@ public class JavaRearranger implements Rearranger, else if (CLASS.equals(target.getType())) { return commonSettings.BLANK_LINES_AROUND_CLASS; } + else if (INIT_BLOCK.equals(target.getType())) { + return javaSettings.BLANK_LINES_AROUND_INITIALIZER; + } else { return -1; } @@ -363,9 +383,10 @@ public class JavaRearranger implements Rearranger, @Override public boolean isEnabled(@NotNull ArrangementSettingsToken token, @Nullable ArrangementMatchCondition current) { - if (SUPPORTED_TYPES.contains(token) || SUPPORTED_ORDERS.contains(token) || StdArrangementTokens.Regexp.NAME.equals(token)) { + if (SUPPORTED_TYPES.contains(token)) { return true; } + ArrangementSettingsToken type = null; if (current != null) { type = ArrangementUtil.parseType(current); @@ -373,6 +394,15 @@ public class JavaRearranger implements Rearranger, if (type == null) { type = NO_TYPE; } + + if (SUPPORTED_ORDERS.contains(token)) { + return !TYPES_WITH_DISABLED_ORDER.contains(type); + } + + if (StdArrangementTokens.Regexp.NAME.equals(token)) { + return !TYPES_WITH_DISABLED_NAME_MATCH.contains(type); + } + Set modifiers = MODIFIERS_BY_TYPE.get(type); return modifiers != null && modifiers.contains(token); } diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/AbstractJavaBlock.java b/java/java-impl/src/com/intellij/psi/formatter/java/AbstractJavaBlock.java index a49cede74175..c092223ea47a 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/AbstractJavaBlock.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/AbstractJavaBlock.java @@ -39,6 +39,7 @@ import com.intellij.psi.impl.source.tree.java.ClassElement; import com.intellij.psi.jsp.JspElementType; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.text.CharArrayUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -90,6 +91,8 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo private final JavaWrapManager myWrapManager; private final AlignmentInColumnsHelper myAlignmentInColumnsHelper; + private Map myPreferredWraps; + private AbstractJavaBlock myParentBlock; protected AbstractJavaBlock(@NotNull final ASTNode node, final Wrap wrap, @@ -505,12 +508,9 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo mySettings.ALIGN_MULTILINE_PARAMETERS_IN_CALLS); } else if (childType == JavaTokenType.LPARENTH && nodeType == JavaElementType.PARAMETER_LIST) { - // There is a possible case that particular annotated method definition is too long. We may wrap either after annotation - // or after opening lbrace then. Our strategy is to wrap after annotation whenever possible. - Wrap wrap = Wrap.createWrap(getWrapType(mySettings.METHOD_PARAMETERS_WRAP), false); - child = processParenthesisBlock(result, child, - WrappingStrategy.createDoNotWrapCommaStrategy(wrap), - mySettings.ALIGN_MULTILINE_PARAMETERS); + Wrap wrapToUse = getMethodParametersWrap(); + WrappingStrategy wrapStrategy = WrappingStrategy.createDoNotWrapCommaStrategy(wrapToUse); + child = processParenthesisBlock(result, child, wrapStrategy, mySettings.ALIGN_MULTILINE_PARAMETERS); } else if (childType == JavaTokenType.LPARENTH && nodeType == JavaElementType.RESOURCE_LIST) { Wrap wrap = Wrap.createWrap(getWrapType(mySettings.RESOURCE_LIST_WRAP), false); @@ -572,6 +572,25 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo return child; } + @NotNull + private Wrap getMethodParametersWrap() { + Wrap preferredWrap = getModifierListWrap(); + if (preferredWrap == null) { + return Wrap.createWrap(getWrapType(mySettings.METHOD_PARAMETERS_WRAP), false); + } else { + return Wrap.createChildWrap(preferredWrap, getWrapType(mySettings.METHOD_PARAMETERS_WRAP), false); + } + } + + @Nullable + private Wrap getModifierListWrap() { + AbstractJavaBlock parentBlock = getParentBlock(); + if (parentBlock != null) { + return parentBlock.getReservedWrap(JavaElementType.MODIFIER_LIST); + } + return null; + } + private ASTNode processField(@NotNull final List result, ASTNode child, @NotNull final AlignmentStrategy alignmentStrategy, @@ -1135,7 +1154,7 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo @Nullable @Override public Wrap getReservedWrap(IElementType elementType) { - return null; + return myPreferredWraps != null ? myPreferredWraps.get(elementType) : null; } /** @@ -1150,7 +1169,11 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo * @param reservedWrap reserved wrap instance * @param operationType target operation type to associate with the given wrap instance */ - protected void setReservedWrap(final Wrap reservedWrap, final IElementType operationType) { + public void setReservedWrap(final Wrap reservedWrap, final IElementType operationType) { + if (myPreferredWraps == null) { + myPreferredWraps = ContainerUtil.newHashMap(); + } + myPreferredWraps.put(operationType, reservedWrap); } @Nullable @@ -1262,6 +1285,14 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo return null; } + public AbstractJavaBlock getParentBlock() { + return myParentBlock; + } + + public void setParentBlock(@NotNull AbstractJavaBlock parentBlock) { + myParentBlock = parentBlock; + } + /** * Allows to answer if special 'variable declaration alignment' strategy should be used for the given node. * I.e. given node is supposed to be parent node of sub-nodes that should be aligned 'by-columns'. diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/BlockContainingJavaBlock.java b/java/java-impl/src/com/intellij/psi/formatter/java/BlockContainingJavaBlock.java index a97d6d12646f..098993124bdf 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/BlockContainingJavaBlock.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/BlockContainingJavaBlock.java @@ -61,8 +61,13 @@ public class BlockContainingJavaBlock extends AbstractJavaBlock{ buildChildren(result, childAlignment, childWrap); - return result; + for (Block block : result) { + if (block instanceof AbstractJavaBlock) { + ((AbstractJavaBlock)block).setParentBlock(this); + } + } + return result; } private void buildChildren(final ArrayList result, final Alignment childAlignment, final Wrap childWrap) { diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java b/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java index 84c15d5db1a9..8a89027e2d56 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java @@ -425,13 +425,8 @@ public class JavaSpacePropertyProcessor extends JavaElementVisitor { myResult = Spacing.createSpacing(0, 0, 1, mySettings.KEEP_LINE_BREAKS, mySettings.KEEP_BLANK_LINES_IN_CODE); } else if (myRole1 == ChildRole.FIELD) { - int lines = Math.max(getLinesAroundField(), getLinesAroundMethod()) + 1; - // IJ has been keeping initialization block which starts at the same line as a field for a while. - // However, it's not convenient for a situation when particular code is created via PSI - it's easier to not bothering - // with whitespace elements when inserting, say, new initialization blocks. That's why we don't enforce new line - // only during explicit reformatting ('Reformat' action). - //int minLineFeeds = FormatterUtil.isFormatterCalledExplicitly() ? 0 : 1; - myResult = Spacing.createSpacing(0, mySettings.SPACE_BEFORE_CLASS_LBRACE ? 1 : 0, 1, true, mySettings.KEEP_BLANK_LINES_BEFORE_RBRACE, lines); + int blankLines = myJavaSettings.BLANK_LINES_AROUND_INITIALIZER + 1; + myResult = Spacing.createSpacing(0, mySettings.SPACE_BEFORE_CLASS_LBRACE ? 1 : 0, blankLines, true, mySettings.KEEP_BLANK_LINES_BEFORE_RBRACE); } else if (myRole1 == ChildRole.CLASS) { setAroundClassSpacing(); @@ -450,7 +445,7 @@ public class JavaSpacePropertyProcessor extends JavaElementVisitor { setAroundClassSpacing(); } else { - final int blankLines = getLinesAroundMethod() + 1; + final int blankLines = myJavaSettings.BLANK_LINES_AROUND_INITIALIZER + 1; myResult = Spacing.createSpacing(0, Integer.MAX_VALUE, blankLines, mySettings.KEEP_LINE_BREAKS, mySettings.KEEP_BLANK_LINES_IN_DECLARATIONS); } } diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/SimpleJavaBlock.java b/java/java-impl/src/com/intellij/psi/formatter/java/SimpleJavaBlock.java index c74b9c05b19b..478f8ba8d065 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/SimpleJavaBlock.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/SimpleJavaBlock.java @@ -127,7 +127,7 @@ public class SimpleJavaBlock extends AbstractJavaBlock { } @Override - protected void setReservedWrap(final Wrap reservedWrap, final IElementType operationType) { + public void setReservedWrap(final Wrap reservedWrap, final IElementType operationType) { myReservedWrap.put(operationType, reservedWrap); } diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/wrap/JavaWrapManager.java b/java/java-impl/src/com/intellij/psi/formatter/java/wrap/JavaWrapManager.java index 50bbcbc3b5d6..54965f9eca1e 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/wrap/JavaWrapManager.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/wrap/JavaWrapManager.java @@ -19,8 +19,8 @@ import com.intellij.formatting.ASTBlock; import com.intellij.formatting.Block; import com.intellij.formatting.Wrap; import com.intellij.lang.ASTNode; -import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.codeStyle.CommonCodeStyleSettings; +import com.intellij.psi.formatter.java.AbstractJavaBlock; import com.intellij.psi.formatter.java.wrap.impl.JavaChildBlockWrapFactory; import com.intellij.psi.formatter.java.wrap.impl.JavaChildWrapArranger; import org.jetbrains.annotations.Nullable; @@ -79,7 +79,7 @@ public class JavaWrapManager { */ @Nullable public Wrap arrangeChildWrap(ASTNode child, ASTNode parent, CommonCodeStyleSettings settings, Wrap suggestedWrap, - ReservedWrapsProvider reservedWrapsProvider) + AbstractJavaBlock reservedWrapsProvider) { return myChildArranger.arrange(child, parent, settings, suggestedWrap, reservedWrapsProvider); } diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/wrap/impl/JavaChildWrapArranger.java b/java/java-impl/src/com/intellij/psi/formatter/java/wrap/impl/JavaChildWrapArranger.java index 5b05ced2d5a1..579e9ae86180 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/wrap/impl/JavaChildWrapArranger.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/wrap/impl/JavaChildWrapArranger.java @@ -22,6 +22,7 @@ import com.intellij.psi.*; import com.intellij.psi.codeStyle.CommonCodeStyleSettings; import com.intellij.psi.codeStyle.JavaCodeStyleSettings; import com.intellij.psi.formatter.FormatterUtil; +import com.intellij.psi.formatter.java.AbstractJavaBlock; import com.intellij.psi.formatter.java.JavaFormatterUtil; import com.intellij.psi.formatter.java.wrap.JavaWrapManager; import com.intellij.psi.formatter.java.wrap.ReservedWrapsProvider; @@ -70,7 +71,7 @@ public class JavaChildWrapArranger { ASTNode parent, CommonCodeStyleSettings settings, Wrap suggestedWrap, - ReservedWrapsProvider reservedWrapsProvider) { + AbstractJavaBlock reservedWrapsProvider) { final JavaCodeStyleSettings javaSettings = settings.getRootSettings().getCustomSettings(JavaCodeStyleSettings.class); ASTNode directParent = child.getTreeParent(); int role = ((CompositeElement)directParent).getChildRole(child); @@ -188,8 +189,9 @@ public class JavaChildWrapArranger { if (javaSettings.DO_NOT_WRAP_AFTER_SINGLE_ANNOTATION && isFieldModifierListWithSingleAnnotation(parent)) { return Wrap.createWrap(WrapType.NONE, false); } - - return Wrap.createWrap(getWrapType(getAnnotationWrapType(parent.getTreeParent(), child, settings)), true); + Wrap wrap = Wrap.createWrap(getWrapType(getAnnotationWrapType(parent.getTreeParent(), child, settings)), true); + putPreferredWrapInParentBlock(reservedWrapsProvider, wrap); + return wrap; } return null; @@ -263,6 +265,13 @@ public class JavaChildWrapArranger { return suggestedWrap; } + private static void putPreferredWrapInParentBlock(@NotNull AbstractJavaBlock block, @NotNull Wrap preferredWrap) { + AbstractJavaBlock parentBlock = block.getParentBlock(); + if (parentBlock != null) { + parentBlock.setReservedWrap(preferredWrap, JavaElementType.MODIFIER_LIST); + } + } + private static boolean isFieldModifierListWithSingleAnnotation(@NotNull ASTNode elem) { ASTNode parent = elem.getTreeParent(); if (parent != null && parent.getElementType() == JavaElementType.FIELD) { diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ExtractLightMethodObjectHandler.java b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ExtractLightMethodObjectHandler.java index 968b537bb71d..c008f7f7a609 100644 --- a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ExtractLightMethodObjectHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ExtractLightMethodObjectHandler.java @@ -92,12 +92,28 @@ public class ExtractLightMethodObjectHandler { final TextRange range = originalContext.getTextRange(); final PsiElement originalAnchor = CodeInsightUtil.findElementInRange(copy, range.getStartOffset(), range.getEndOffset(), originalContext.getClass()); - //todo before this or super, not found etc - final PsiElement anchor = RefactoringUtil.getParentStatement(originalAnchor, false); - if (anchor == null) { + + final PsiClass containingClass = PsiTreeUtil.getParentOfType(originalAnchor, PsiClass.class); + if (containingClass == null) { return null; } - final PsiElement container = anchor.getParent(); + + PsiElement anchor = RefactoringUtil.getParentStatement(originalAnchor, false); + if (anchor == null) { + if (PsiTreeUtil.getParentOfType(originalAnchor, PsiCodeBlock.class) != null) { + anchor = originalAnchor; + } + } + + final PsiElement container; + if (anchor == null) { + container = ((PsiClassInitializer)containingClass.add(elementFactory.createClassInitializer())).getBody(); + anchor = container.getLastChild(); + } + else { + container = anchor.getParent(); + } + final PsiElement firstElementCopy = container.addRangeBefore(elements[0], elements[elements.length - 1], anchor); final PsiElement[] elementsCopy = CodeInsightUtil.findStatementsInRange(copy, firstElementCopy.getTextRange().getStartOffset(), diff --git a/java/java-impl/src/com/intellij/refactoring/util/RefactoringUtil.java b/java/java-impl/src/com/intellij/refactoring/util/RefactoringUtil.java index a411917b5255..d181d3c49351 100644 --- a/java/java-impl/src/com/intellij/refactoring/util/RefactoringUtil.java +++ b/java/java-impl/src/com/intellij/refactoring/util/RefactoringUtil.java @@ -870,16 +870,22 @@ public class RefactoringUtil { JavaCodeStyleManager.getInstance(declaration.getProject()).shortenClassReferences(declaration); if (loopBodyCopy != null) codeBlock.add(loopBodyCopy); } else if (container instanceof PsiLambdaExpression) { - final PsiLambdaExpression lambdaExpression = (PsiLambdaExpression)container; - final PsiElement lambdaExpressionBody = lambdaExpression.getBody(); - LOG.assertTrue(lambdaExpressionBody != null); + PsiLambdaExpression lambdaExpression = (PsiLambdaExpression)container; + final PsiElement invalidBody = lambdaExpression.getBody(); + if (invalidBody == null) return declaration; final PsiLambdaExpression expressionFromText = (PsiLambdaExpression)elementFactory - .createExpressionFromText(lambdaExpression.getParameterList().getText() + " -> {}", lambdaExpression); + .createExpressionFromText(lambdaExpression.getParameterList().getText() + " -> {}", lambdaExpression.getParent()); PsiCodeBlock newBody = (PsiCodeBlock)expressionFromText.getBody(); LOG.assertTrue(newBody != null); newBody.add(declaration); + lambdaExpression = + (PsiLambdaExpression)lambdaExpression.replace(elementFactory.createExpressionFromText( + lambdaExpression.getParameterList().getText() + " -> " + invalidBody.getText(), lambdaExpression)); + + final PsiElement lambdaExpressionBody = lambdaExpression.getBody(); + LOG.assertTrue(lambdaExpressionBody != null); final PsiStatement lastBodyStatement; if (LambdaUtil.getFunctionalInterfaceReturnType(lambdaExpression) == PsiType.VOID) { lastBodyStatement = elementFactory.createStatementFromText("a;", lambdaExpression); diff --git a/java/java-psi-api/src/com/intellij/psi/GenericsUtil.java b/java/java-psi-api/src/com/intellij/psi/GenericsUtil.java index 0818ea4e41a0..c49580e97c85 100644 --- a/java/java-psi-api/src/com/intellij/psi/GenericsUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/GenericsUtil.java @@ -255,41 +255,55 @@ public class GenericsUtil { final PsiSubstitutor substitutor, final PsiElement context, final boolean allowUncheckedConversion) { - nextTypeParam: for (PsiTypeParameter typeParameter : typeParams) { - PsiType substituted = substitutor.substitute(typeParameter); - if (substituted == null) return null; - if (context != null) { - substituted = PsiUtil.captureToplevelWildcards(substituted, context); + PsiType boundError = findTypeParameterBoundError(typeParameter, typeParameter.getExtendsListTypes(), + substitutor, context, allowUncheckedConversion); + if (boundError != null) { + return Pair.create(typeParameter, boundError); } + } + return null; + } - PsiClassType[] extendsTypes = typeParameter.getExtendsListTypes(); - for (PsiClassType type : extendsTypes) { - PsiType extendsType = substitutor.substitute(type); - if (substituted instanceof PsiWildcardType) { - if (((PsiWildcardType)substituted).isSuper()) { - continue; - } - final PsiType extendsBound = ((PsiWildcardType)substituted).getExtendsBound(); - if (acceptExtendsBound(extendsType, extendsBound)) { - continue nextTypeParam; - } + public static PsiType findTypeParameterBoundError(PsiTypeParameter typeParameter, + PsiType[] extendsTypes, + PsiSubstitutor substitutor, + PsiElement context, + boolean allowUncheckedConversion) { + PsiType substituted = substitutor.substitute(typeParameter); + if (substituted == null) return null; + if (context != null) { + substituted = PsiUtil.captureToplevelWildcards(substituted, context); + } + + if (substituted instanceof PsiWildcardType) { + if (((PsiWildcardType)substituted).isSuper()) { + return null; + } + } + + for (PsiType type : extendsTypes) { + PsiType extendsType = substitutor.substitute(type); + if (substituted instanceof PsiWildcardType) { + final PsiType extendsBound = ((PsiWildcardType)substituted).getExtendsBound(); + if (acceptExtendsBound(extendsType, extendsBound)) { + return null; } - else if (substituted instanceof PsiIntersectionType) { - for (PsiType extendsBound : ((PsiIntersectionType)substituted).getConjuncts()) { - if (acceptExtendsBound(extendsType, extendsBound)) continue nextTypeParam; - } + } + else if (substituted instanceof PsiIntersectionType) { + for (PsiType extendsBound : ((PsiIntersectionType)substituted).getConjuncts()) { + if (acceptExtendsBound(extendsType, extendsBound)) return null; } - else if (substituted instanceof PsiCapturedWildcardType) { - final PsiType extendsBound = ((PsiCapturedWildcardType)substituted).getUpperBound(); - if (acceptExtendsBound(extendsType, extendsBound)) { - continue nextTypeParam; - } - } - if (extendsType != null && !TypeConversionUtil.isAssignable(extendsType, substituted, allowUncheckedConversion)) { - return Pair.create(typeParameter, extendsType); + } + else if (substituted instanceof PsiCapturedWildcardType) { + final PsiType extendsBound = ((PsiCapturedWildcardType)substituted).getUpperBound(); + if (acceptExtendsBound(extendsType, extendsBound)) { + return null; } } + if (extendsType != null && !TypeConversionUtil.isAssignable(extendsType, substituted, allowUncheckedConversion)) { + return extendsType; + } } return null; } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java index 51350e124dd3..22beb02e68b8 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java @@ -254,12 +254,12 @@ public class InferenceSession { } } - final PsiSubstitutor substitutor = resolveBounds(myInferenceVariables, mySiteSubstitutor); + final PsiSubstitutor substitutor = resolveBounds(myInferenceVariables, PsiSubstitutor.EMPTY); if (substitutor != null) { if (myContext != null) { myContext.putUserData(ERASED, myErased); } - mySiteSubstitutor = substitutor; + mySiteSubstitutor = mySiteSubstitutor.putAll(substitutor); for (InferenceVariable variable : myInferenceVariables) { variable.setInstantiation(substitutor.substitute(variable.getParameter())); } @@ -403,7 +403,7 @@ public class InferenceSession { } public PsiSubstitutor retrieveNonPrimitiveEqualsBounds(Collection variables) { - PsiSubstitutor substitutor = mySiteSubstitutor; + PsiSubstitutor substitutor = PsiSubstitutor.EMPTY; for (InferenceVariable variable : variables) { final PsiType equalsBound = getEqualsBound(variable, substitutor); if (!(equalsBound instanceof PsiPrimitiveType)) { @@ -751,6 +751,22 @@ public class InferenceSession { return isProperType(bound) ? bound : substitutor.substitute(bound); } + private static boolean hasBoundProblems(final List typeParams, + final PsiSubstitutor substitutor, + final PsiElement context) { + for (InferenceVariable typeParameter : typeParams) { + if (typeParameter.getCallContext() != context) { + continue; + } + final List extendsTypes = typeParameter.getBounds(InferenceBound.UPPER); + final PsiType[] bounds = extendsTypes.toArray(new PsiType[extendsTypes.size()]); + if (GenericsUtil.findTypeParameterBoundError(typeParameter, bounds, substitutor, context, true) != null) { + return true; + } + } + return false; + } + private PsiSubstitutor resolveBounds(final Collection inferenceVariables, PsiSubstitutor substitutor) { final Collection allVars = new ArrayList(inferenceVariables); @@ -759,8 +775,7 @@ public class InferenceSession { if (!myIncorporationPhase.hasCaptureConstraints(vars)) { PsiSubstitutor firstSubstitutor = resolveSubset(vars, substitutor); if (firstSubstitutor != null) { - final Set parameters = firstSubstitutor.getSubstitutionMap().keySet(); - if (GenericsUtil.findTypeParameterWithBoundError(parameters.toArray(new PsiTypeParameter[parameters.size()]), firstSubstitutor, myContext, true) != null) { + if (hasBoundProblems(vars, firstSubstitutor, myContext)) { firstSubstitutor = null; } } @@ -837,7 +852,7 @@ public class InferenceSession { for (InferenceVariable var : vars) { LOG.assertTrue(var.getInstantiation() == PsiType.NULL); final PsiTypeParameter typeParameter = var.getParameter(); - if (substitutor.getSubstitutionMap().containsKey(typeParameter) && var.getCallContext() != myContext) { + if (substitutor.putAll(mySiteSubstitutor).getSubstitutionMap().containsKey(typeParameter) && var.getCallContext() != myContext) { continue;//todo } @@ -849,7 +864,7 @@ public class InferenceSession { type = PsiType.getJavaLangRuntimeException(myManager, GlobalSearchScope.allScope(myManager.getProject())); } else { - if (substitutor.getSubstitutionMap().get(typeParameter) != null) continue; + if (substitutor.putAll(mySiteSubstitutor).getSubstitutionMap().get(typeParameter) != null) continue; type = myErased ? null : getUpperBound(var, substitutor); } } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/ExpressionCompatibilityConstraint.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/ExpressionCompatibilityConstraint.java index b59cf70bdb69..f7b73ba5eee2 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/ExpressionCompatibilityConstraint.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/ExpressionCompatibilityConstraint.java @@ -90,13 +90,9 @@ public class ExpressionCompatibilityConstraint extends InputOutputConstraintForm if (method != null && !method.isConstructor()) { returnType = method.getReturnType(); if (returnType != null) { - List params = new ArrayList(); - for (PsiTypeParameter parameter : PsiUtil.typeParametersIterable(method)) { - params.add(parameter); - } - typeParams = params.toArray(new PsiTypeParameter[params.size()]); + typeParams = method.getTypeParameters(); } - } else if (myExpression instanceof PsiNewExpression) { //default constructor + } else if (myExpression instanceof PsiNewExpression) { final PsiJavaCodeReferenceElement classReference = ((PsiNewExpression)myExpression).getClassOrAnonymousClassReference(); if (classReference != null) { final PsiElement psiClass = classReference.resolve(); @@ -108,7 +104,9 @@ public class ExpressionCompatibilityConstraint extends InputOutputConstraintForm } if (typeParams != null) { - final InferenceSession callSession = new InferenceSession(typeParams, PsiSubstitutor.EMPTY, myExpression.getManager(), myExpression); + PsiSubstitutor siteSubstitutor = + resolveResult instanceof MethodCandidateInfo && method != null && !method.isConstructor() ? ((MethodCandidateInfo)resolveResult).getSiteSubstitutor() : PsiSubstitutor.EMPTY; + final InferenceSession callSession = new InferenceSession(typeParams, siteSubstitutor, myExpression.getManager(), myExpression); callSession.propagateVariables(session.getInferenceVariables()); if (method != null) { final PsiExpression[] args = argumentList.getExpressions(); @@ -120,7 +118,7 @@ public class ExpressionCompatibilityConstraint extends InputOutputConstraintForm if (!accepted) { return false; } - callSession.registerReturnTypeConstraints(returnType, myT); + callSession.registerReturnTypeConstraints(siteSubstitutor.substitute(returnType), myT); if (callSession.repeatInferencePhases(true)) { session.registerNestedSession(callSession); } else { diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/TypeEqualityConstraint.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/TypeEqualityConstraint.java index 62f1ff1d819c..03b20b3fb443 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/TypeEqualityConstraint.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/TypeEqualityConstraint.java @@ -91,7 +91,7 @@ public class TypeEqualityConstraint implements ConstraintFormula { final PsiClassType.ClassResolveResult sResult = ((PsiClassType)myS).resolveGenerics(); final PsiClass tClass = tResult.getElement(); //equal erasure - if (tClass != null && tClass.equals(sResult.getElement())) { + if (tClass != null && tClass.getManager().areElementsEquivalent(tClass, sResult.getElement())) { final PsiSubstitutor tSubstitutor = tResult.getSubstitutor(); final PsiSubstitutor sSubstitutor = sResult.getSubstitutor(); for (PsiTypeParameter typeParameter : tClass.getTypeParameters()) { diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/advHighlighting/FinalVariableMightNotHaveBeenInitializedInsideLambda.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/advHighlighting/FinalVariableMightNotHaveBeenInitializedInsideLambda.java new file mode 100644 index 000000000000..c8e80cc743c4 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/advHighlighting/FinalVariableMightNotHaveBeenInitializedInsideLambda.java @@ -0,0 +1,66 @@ +class Test { + final Runnable runnable; + + { + runnable = () -> System.out.println(runnable); + } + + final Runnable runnable1; + { + runnable1 = new Runnable() { + @Override + public void run() { + System.out.println(runnable1); + } + }; + } + +} + +abstract class TestInnerAnonymous { + + + void foo() { + new Object() { + final Runnable runnable; + + { + runnable = () -> System.out.println(runnable); + } + + final Runnable runnable1; + + { + runnable1 = new Runnable() { + @Override + public void run() { + System.out.println(runnable1); + } + }; + } + + }; + } + + + private static class MyObject { + final Runnable runnable; + + { + runnable = () -> System.out.println(runnable); + } + + final Runnable runnable1; + + { + runnable1 = new Runnable() { + @Override + public void run() { + System.out.println(runnable1); + } + }; + } + + } +} + diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/graphInference/SiteSubstitutionInExpressionConstraints.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/graphInference/SiteSubstitutionInExpressionConstraints.java new file mode 100644 index 000000000000..927dc965042d --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/graphInference/SiteSubstitutionInExpressionConstraints.java @@ -0,0 +1,49 @@ +import java.util.Map; +import java.util.List; + +//expected type parameter +class Sample { + + public Map test() { + return null; + } + + public void fun(T t, V v) { + } + + M bar() { + return null; + } + + void run(Sample sample) { + fun(test(), bar()); + sample.fun(test(), sample.bar()); + sample.fun(test(), bar()); + fun(test(), sample.bar()); + + fun(sample.test(), bar()); + fun(sample.test(), sample.bar()); + sample.fun(sample.test(), bar()); + sample.fun(sample.test(), sample.bar()); + } +} + +//expected generic type +class Sample1 { + public List reverse() { + return null; + } + + public void foo(Sample1 t) + { + newTreeSet(t.reverse()); + newTreeSet(reverse()); + + t.newTreeSet(t.reverse()); + t.newTreeSet(reverse()); + } + + + public void newTreeSet(List comparator) {} +} + diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/methodRef/ReturnTypeSpecific.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/methodRef/ReturnTypeSpecific.java index 0696aadf6179..6b4de4e565a8 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/methodRef/ReturnTypeSpecific.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/methodRef/ReturnTypeSpecific.java @@ -67,7 +67,7 @@ class MyTest1 { } public static void main(String[] args) { - m(Foo::new); + m(Foo::new); } } class MyTest2 { @@ -104,6 +104,6 @@ class MyTest2 { } public static void main(String[] args) { - m(Foo::new); + m(Foo::new); } } diff --git a/java/java-tests/testData/refactoring/extractMethodObject4Debugger/OnClosingBrace.java b/java/java-tests/testData/refactoring/extractMethodObject4Debugger/OnClosingBrace.java new file mode 100644 index 000000000000..48a25567d205 --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethodObject4Debugger/OnClosingBrace.java @@ -0,0 +1,10 @@ +class Sample { + + void a() { + System.out.println(""); + } + + private int foo() { + return 1; + } +} diff --git a/java/java-tests/testData/refactoring/extractMethodObject4Debugger/OnClosingBraceLocalClass.java b/java/java-tests/testData/refactoring/extractMethodObject4Debugger/OnClosingBraceLocalClass.java new file mode 100644 index 000000000000..010af145aaca --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethodObject4Debugger/OnClosingBraceLocalClass.java @@ -0,0 +1,12 @@ +class Sample { + + void a() { + class Local { + + } + } + + private int foo() { + return 1; + } +} diff --git a/java/java-tests/testData/refactoring/extractMethodObject4Debugger/OnEmptyMethod.java b/java/java-tests/testData/refactoring/extractMethodObject4Debugger/OnEmptyMethod.java new file mode 100644 index 000000000000..6436369d9d84 --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethodObject4Debugger/OnEmptyMethod.java @@ -0,0 +1,7 @@ +class Sample { + static {} + + int foo() { + return 1; + } +} diff --git a/java/java-tests/testData/refactoring/extractMethodObject4Debugger/OnFieldInitialization.java b/java/java-tests/testData/refactoring/extractMethodObject4Debugger/OnFieldInitialization.java new file mode 100644 index 000000000000..e1746e57ed23 --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethodObject4Debugger/OnFieldInitialization.java @@ -0,0 +1,7 @@ +class Sample { + int i = 0; + + int foo() { + return 1; + } +} diff --git a/java/java-tests/testData/refactoring/extractMethodObject4Debugger/OnSuperConstructorCall.java b/java/java-tests/testData/refactoring/extractMethodObject4Debugger/OnSuperConstructorCall.java new file mode 100644 index 000000000000..69d6f91a4643 --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethodObject4Debugger/OnSuperConstructorCall.java @@ -0,0 +1,9 @@ +class Sample { + public Sample() { + super(); + } + + int foo() { + return 1; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/introduceVariable/PutInLambdaBody.after.java b/java/java-tests/testData/refactoring/introduceVariable/PutInLambdaBody.after.java new file mode 100644 index 000000000000..afdfb96db8e2 --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceVariable/PutInLambdaBody.after.java @@ -0,0 +1,20 @@ +class Foo { + interface I { + T m(Object op); + } + + interface J { + int m(T o); + } + + + void f(J r) {} + void f(I r) {} + + { + f((Object a) -> { + int c = 1; + return c; + }); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/introduceVariable/PutInLambdaBody.java b/java/java-tests/testData/refactoring/introduceVariable/PutInLambdaBody.java new file mode 100644 index 000000000000..3d54e3b88646 --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceVariable/PutInLambdaBody.java @@ -0,0 +1,17 @@ +class Foo { + interface I { + T m(Object op); + } + + interface J { + int m(T o); + } + + + void f(J r) {} + void f(I r) {} + + { + f((Object a) -> 1); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/introduceVariable/PutInLambdaBodyVoidValueConflict.after.java b/java/java-tests/testData/refactoring/introduceVariable/PutInLambdaBodyVoidValueConflict.after.java new file mode 100644 index 000000000000..6315c2590032 --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceVariable/PutInLambdaBodyVoidValueConflict.after.java @@ -0,0 +1,20 @@ +class Foo { + interface I { + T m(Object op); + } + + interface J { + void m(T o); + } + + + void f(J r) {} + void f(I r) {} + + { + f((a) -> { + int c = 1; + return c; + }); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/introduceVariable/PutInLambdaBodyVoidValueConflict.java b/java/java-tests/testData/refactoring/introduceVariable/PutInLambdaBodyVoidValueConflict.java new file mode 100644 index 000000000000..bdea215a08a4 --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceVariable/PutInLambdaBodyVoidValueConflict.java @@ -0,0 +1,17 @@ +class Foo { + interface I { + T m(Object op); + } + + interface J { + void m(T o); + } + + + void f(J r) {} + void f(I r) {} + + { + f((a) -> 1); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/GraphInferenceHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/GraphInferenceHighlightingTest.java index 538b833b5e3b..122e776fda9d 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/GraphInferenceHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/GraphInferenceHighlightingTest.java @@ -172,6 +172,10 @@ public class GraphInferenceHighlightingTest extends LightDaemonAnalyzerTestCase doTest(); } + public void testSiteSubstitutionInExpressionConstraints() throws Exception { + doTest(); + } + public void testIncorporationWithEqualsBoundsSubstitution() throws Exception { doTest(); } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LightAdvHighlightingJdk8Test.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LightAdvHighlightingJdk8Test.java index 08a2b63550ff..4ecf0b2b434e 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LightAdvHighlightingJdk8Test.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LightAdvHighlightingJdk8Test.java @@ -39,6 +39,10 @@ public class LightAdvHighlightingJdk8Test extends LightDaemonAnalyzerTestCase { doTest(); } + public void testFinalVariableMightNotHaveBeenInitializedInsideLambda() throws Exception { + doTest(); + } + private void doTest() { IdeaTestUtil.setTestVersion(JavaSdkVersion.JDK_1_8, getModule(), getTestRootDisposable()); doTest(BASE_PATH + "/" + getTestName(false) + ".java", true, false); diff --git a/java/java-tests/testSrc/com/intellij/psi/codeStyle/arrangement/JavaRearrangerByTypeAndModifierTest.groovy b/java/java-tests/testSrc/com/intellij/psi/codeStyle/arrangement/JavaRearrangerByTypeAndModifierTest.groovy index 23f6d1b566c2..1cc9c93bc9c5 100644 --- a/java/java-tests/testSrc/com/intellij/psi/codeStyle/arrangement/JavaRearrangerByTypeAndModifierTest.groovy +++ b/java/java-tests/testSrc/com/intellij/psi/codeStyle/arrangement/JavaRearrangerByTypeAndModifierTest.groovy @@ -83,9 +83,10 @@ class Test { expected: '''\ class Test { public int j; - { j = 1; } protected int k; private int i; + + { j = 1; } }''', rules: [rule(FIELD, PUBLIC), rule(FIELD, PROTECTED), rule(FIELD, PRIVATE)]) } @@ -101,10 +102,11 @@ class Test { }''', expected: '''\ class Test { - { j = 1; } public int j; protected int k; private int i; + + { j = 1; } }''', rules: [rule(FIELD, PUBLIC), rule(FIELD, PROTECTED), rule(FIELD, PRIVATE)]) } diff --git a/java/java-tests/testSrc/com/intellij/psi/codeStyle/arrangement/JavaRearrangerByTypeTest.groovy b/java/java-tests/testSrc/com/intellij/psi/codeStyle/arrangement/JavaRearrangerByTypeTest.groovy index 7f15babc1e93..62fedc613f7f 100644 --- a/java/java-tests/testSrc/com/intellij/psi/codeStyle/arrangement/JavaRearrangerByTypeTest.groovy +++ b/java/java-tests/testSrc/com/intellij/psi/codeStyle/arrangement/JavaRearrangerByTypeTest.groovy @@ -569,4 +569,85 @@ class B extends A { rules: [rule(PUBLIC, METHOD), rule(PRIVATE, METHOD)] ) } + + void "test initializer block after fields"() { + doTest( + initial: '''\ +public class NewOneClass { + + { + a = 1; + } + + int a; + + { + b = 5; + } + + int b; + +} +''', + expected: '''\ +public class NewOneClass { + + int a; + int b; + + { + a = 1; + } + + { + b = 5; + } + +} +''', + rules: [rule(FIELD), rule(INIT_BLOCK)] + ) + } + + void "test static initializer block"() { + doTest( + initial: '''\ +public class NewOneClass { + + static { + a = 1; + } + + static int a; + + { + b = 5; + } + + int b; + +} +''', + expected: '''\ +public class NewOneClass { + + static int a; + + static { + a = 1; + } + + int b; + + { + b = 5; + } + +} +''', + rules: [rule(STATIC, FIELD), rule(STATIC, INIT_BLOCK), rule(FIELD), rule(INIT_BLOCK)] + ) + } + + } diff --git a/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterBlankLinesTest.java b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterBlankLinesTest.java index fe005a84db3f..e0c845d066c6 100644 --- a/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterBlankLinesTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterBlankLinesTest.java @@ -25,6 +25,7 @@ public class JavaFormatterBlankLinesTest extends AbstractJavaFormatterTest { public void testBlankLinesAroundClassInitializationBlock() throws Exception { getSettings().BLANK_LINES_AROUND_METHOD = 3; + getJavaSettings().BLANK_LINES_AROUND_INITIALIZER = 3; doTextTest( "class T {\n" + " private final DecimalFormat fmt = new DecimalFormat();\n" + diff --git a/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterWrapTest.java b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterWrapTest.java index b16e23e83e37..fa960980d297 100644 --- a/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterWrapTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterWrapTest.java @@ -260,7 +260,6 @@ public class JavaFormatterWrapTest extends AbstractJavaFormatterTest { ); } - @Bombed(user = "Roman Shevchenko", year = 2014, month = Calendar.MARCH, day = 14) public void testWrapMethodAnnotationBeforeParams() { // Inspired by IDEA-59536 getSettings().RIGHT_MARGIN = 90; diff --git a/java/java-tests/testSrc/com/intellij/refactoring/ExtractMethodObject4DebuggerTest.java b/java/java-tests/testSrc/com/intellij/refactoring/ExtractMethodObject4DebuggerTest.java index af1fee9363dc..d6440fd5de19 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/ExtractMethodObject4DebuggerTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/ExtractMethodObject4DebuggerTest.java @@ -214,6 +214,56 @@ public class ExtractMethodObject4DebuggerTest extends LightRefactoringTestCase { " }", false); } + public void testOnClosingBrace() throws Exception { + doTest(" foo()", "int result = new Test().invoke();", + + "public class Test {\n" + + " public int invoke() {\n" + + " return foo();\n" + + " }\n" + + " }"); + } + + public void testOnClosingBraceLocalClass() throws Exception { + doTest(" foo()", "int result = new Test().invoke();", + + "public class Test {\n" + + " public int invoke() {\n" + + " return foo();\n" + + " }\n" + + " }"); + } + + public void testOnFieldInitialization() throws Exception { + doTest(" foo()", "int result = new Test().invoke();", + + "public class Test {\n" + + " public int invoke() {\n" + + " return foo();\n" + + " }\n" + + " }"); + } + + public void testOnEmptyMethod() throws Exception { + doTest(" foo()", "int result = Test.invoke();", + + "public static class Test {\n" + + " public static int invoke() {\n" + + " return foo();\n" + + " }\n" + + " }"); + } + + public void testOnSuperConstructorCall() throws Exception { + doTest(" foo()", "int result = new Test().invoke();", + + "public class Test {\n" + + " public int invoke() {\n" + + " return foo();\n" + + " }\n" + + " }"); + } + @Override protected Sdk getProjectJDK() { return IdeaTestUtil.getMockJdk18(); diff --git a/java/java-tests/testSrc/com/intellij/refactoring/IntroduceVariableTest.java b/java/java-tests/testSrc/com/intellij/refactoring/IntroduceVariableTest.java index 4e993aac90aa..2e2a6cc488a3 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/IntroduceVariableTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/IntroduceVariableTest.java @@ -437,6 +437,14 @@ public class IntroduceVariableTest extends LightCodeInsightTestCase { doTest(new MockIntroduceVariableHandler("c", false, false, false, "int")); } + public void testPutInLambdaBody() { + doTest(new MockIntroduceVariableHandler("c", false, false, false, "int")); + } + + public void testPutInLambdaBodyVoidValueConflict() { + doTest(new MockIntroduceVariableHandler("c", false, false, false, "int")); + } + public void testNormalizeDeclarations() { doTest(new MockIntroduceVariableHandler("i3", false, false, false, "int")); } diff --git a/jps/jps-builders/src/org/jetbrains/jps/api/BasicFuture.java b/jps/jps-builders/src/org/jetbrains/jps/api/BasicFuture.java index 0717de1c61e7..514ec5efc8d4 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/api/BasicFuture.java +++ b/jps/jps-builders/src/org/jetbrains/jps/api/BasicFuture.java @@ -15,14 +15,17 @@ */ package org.jetbrains.jps.api; -import java.util.concurrent.*; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; /** * @author Eugene Zhuravlev * Date: 5/3/12 */ -public class BasicFuture implements Future { +public class BasicFuture implements TaskFuture { protected final Semaphore mySemaphore = new Semaphore(1); private final AtomicBoolean myDone = new AtomicBoolean(false); private final AtomicBoolean myCanceledState = new AtomicBoolean(false); diff --git a/jps/jps-builders/src/org/jetbrains/jps/api/TaskFuture.java b/jps/jps-builders/src/org/jetbrains/jps/api/TaskFuture.java new file mode 100644 index 000000000000..c09c8446422b --- /dev/null +++ b/jps/jps-builders/src/org/jetbrains/jps/api/TaskFuture.java @@ -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. + */ +package org.jetbrains.jps.api; + +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +public interface TaskFuture extends Future { + void waitFor(); + + boolean waitFor(long timeout, TimeUnit unit); +} diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Mappings.java b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Mappings.java index add3dfca0fbb..3274a7ef896c 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Mappings.java +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Mappings.java @@ -2347,6 +2347,13 @@ public class Mappings { } } + @Nullable + public Collection getClassSources(int className) { + synchronized (myLock) { + return myClassToSourceFile.get(className); + } + } + public void close() { synchronized (myLock) { myClassToSubclasses.close(); diff --git a/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildMain.java b/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildMain.java index 8a082740ebee..7837b954da81 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildMain.java +++ b/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildMain.java @@ -16,6 +16,7 @@ package org.jetbrains.jps.cmdline; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.io.FileSystemUtil; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import io.netty.bootstrap.Bootstrap; @@ -35,7 +36,11 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.api.CmdlineProtoUtil; import org.jetbrains.jps.api.CmdlineRemoteProto; import org.jetbrains.jps.api.GlobalOptions; +import org.jetbrains.jps.incremental.MessageHandler; import org.jetbrains.jps.incremental.Utils; +import org.jetbrains.jps.incremental.fs.BuildFSState; +import org.jetbrains.jps.incremental.fs.FSState; +import org.jetbrains.jps.incremental.messages.BuildMessage; import org.jetbrains.jps.service.SharedThreadPool; import java.io.*; @@ -49,6 +54,9 @@ import java.util.concurrent.TimeUnit; */ @SuppressWarnings("UseOfSystemOutOrSystemErr") public class BuildMain { + private static final String PRELOAD_PROJECT_PATH = "preload.project.path"; + private static final String PRELOAD_CONFIG_PATH = "preload.config.path"; + private static final String LOG_CONFIG_FILE_NAME = "build-log.xml"; private static final String LOG_FILE_NAME = "build.log"; private static final String DEFAULT_LOGGER_CONFIG = "defaultLogConfig.xml"; @@ -65,19 +73,10 @@ public class BuildMain { private static final int SYSTEM_DIR_ARG = SESSION_ID_ARG + 1; private static NioEventLoopGroup ourEventLoopGroup; + @Nullable + private static PreloadedData ourPreloadedData; public static void main(String[] args){ - final long start = System.nanoTime(); - Runtime.getRuntime().addShutdownHook(new Thread("TimeLogger"){ - public void run() { - final long totalTime = (System.nanoTime() - start) / 1000000L; - - final long millis = totalTime % 1000L ; - final long seconds = totalTime / 1000L; - - LOG.info("Total build time " + seconds + " sec " + millis + " ms"); - } - }); System.out.println("Build process started. Classpath: " + System.getProperty("java.class.path")); final String host = args[HOST_ARG]; final int port = Integer.parseInt(args[PORT_ARG]); @@ -123,6 +122,60 @@ public class BuildMain { final ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port)).awaitUninterruptibly(); final boolean success = future.isSuccess(); if (success) { + final String projectPathToPreload = System.getProperty(PRELOAD_PROJECT_PATH, null); + final String globalsPathToPreload = System.getProperty(PRELOAD_CONFIG_PATH, null); + if (projectPathToPreload != null && globalsPathToPreload != null) { + final long preloadStart = System.currentTimeMillis(); + final PreloadedData data = new PreloadedData(); + ourPreloadedData = data; + try { + FileSystemUtil.getAttributes(projectPathToPreload); // this will pre-load all FS optimizations + + final BuildRunner runner = new BuildRunner(new JpsModelLoaderImpl(projectPathToPreload, globalsPathToPreload, null)); + data.setRunner(runner); + + final File dataStorageRoot = Utils.getDataStorageRoot(projectPathToPreload); + final BuildFSState fsState = new BuildFSState(false); + final ProjectDescriptor pd = runner.load(new MessageHandler() { + @Override + public void processMessage(BuildMessage msg) { + data.addMessage(msg); + } + }, dataStorageRoot, fsState); + data.setProjectDescriptor(pd); + + try { + final File fsStateFile = new File(dataStorageRoot, BuildSession.FS_STATE_FILE); + final DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream(fsStateFile))); + try { + final int version = in.readInt(); + if (version == FSState.VERSION) { + final long savedOrdinal = in.readLong(); + in.readBoolean(); // must skip "has-work-to-do" flag + fsState.load(in, pd.getModel(), pd.getBuildRootIndex()); + data.setFsEventOrdinal(savedOrdinal); + } + } + finally { + in.close(); + } + } + catch (FileNotFoundException ignored) { + } + catch (IOException e) { + LOG.info("Error pre-loading FS state", e); + fsState.clearAll(); + } + LOG.info("Pre-loaded project model in " + (System.currentTimeMillis() - preloadStart) + " ms"); + } + catch (Throwable e) { + LOG.info("Failed to pre-load project " + projectPathToPreload, e); + // just failed to preload the project, the situation will be handled later, when real build starts + } + } + else if (projectPathToPreload != null || globalsPathToPreload != null){ + LOG.info("Skipping project pre-loading step: both paths to project configuration files and path to global settings must be specified"); + } future.channel().writeAndFlush(CmdlineProtoUtil.toMessage(sessionId, CmdlineProtoUtil.createParamRequest())); } else { @@ -159,7 +212,7 @@ public class BuildMain { case BUILD_PARAMETERS: { if (mySession == null) { final CmdlineRemoteProto.Message.ControllerMessage.FSEvent delta = controllerMessage.hasFsEvent()? controllerMessage.getFsEvent() : null; - final BuildSession session = new BuildSession(mySessionId, channel, controllerMessage.getParamsMessage(), delta); + final BuildSession session = new BuildSession(mySessionId, channel, controllerMessage.getParamsMessage(), delta, ourPreloadedData); mySession = session; SharedThreadPool.getInstance().executeOnPooledThread(new Runnable() { @Override diff --git a/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildRunner.java b/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildRunner.java index a03c127af2c6..1b893a45a302 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildRunner.java +++ b/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildRunner.java @@ -58,14 +58,20 @@ public class BuildRunner { public static final boolean PARALLEL_BUILD_ENABLED = Boolean.parseBoolean(System.getProperty(GlobalOptions.COMPILE_PARALLEL_OPTION, "false")); private static final boolean STORE_TEMP_CACHES_IN_MEMORY = PARALLEL_BUILD_ENABLED || Boolean.valueOf(System.getProperty(GlobalOptions.USE_MEMORY_TEMP_CACHE_OPTION, "true")); private final JpsModelLoader myModelLoader; - private final List myFilePaths; - private final Map myBuilderParams; + private List myFilePaths = Collections.emptyList(); + private Map myBuilderParams = Collections.emptyMap(); private boolean myForceCleanCaches; - public BuildRunner(JpsModelLoader modelLoader, List filePaths, Map builderParams) { + public BuildRunner(JpsModelLoader modelLoader) { myModelLoader = modelLoader; - myFilePaths = filePaths; - myBuilderParams = builderParams; + } + + public void setFilePaths(List filePaths) { + myFilePaths = filePaths != null? filePaths : Collections.emptyList(); + } + + public void setBuilderParams(Map builderParams) { + myBuilderParams = builderParams != null? builderParams : Collections.emptyMap(); } public ProjectDescriptor load(MessageHandler msgHandler, File dataStorageRoot, BuildFSState fsState) throws IOException { diff --git a/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildSession.java b/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildSession.java index 9dd87443eae0..0fc6039dfb90 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildSession.java +++ b/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildSession.java @@ -25,6 +25,7 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.concurrency.SequentialTaskExecutor; import com.intellij.util.io.DataOutputStream; import io.netty.channel.Channel; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.TimingLog; import org.jetbrains.jps.api.*; @@ -57,9 +58,11 @@ import static org.jetbrains.jps.api.CmdlineRemoteProto.Message.ControllerMessage */ final class BuildSession implements Runnable, CanceledStatus { private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.cmdline.BuildSession"); - private static final String FS_STATE_FILE = "fs_state.dat"; + public static final String FS_STATE_FILE = "fs_state.dat"; private final UUID mySessionId; private final Channel myChannel; + @Nullable + private final PreloadedData myPreloadedData; private volatile boolean myCanceled = false; private final String myProjectPath; @Nullable @@ -70,6 +73,7 @@ final class BuildSession implements Runnable, CanceledStatus { private volatile ProjectDescriptor myProjectDescriptor; private final Map, ConstantSearchFuture> mySearchTasks = Collections.synchronizedMap(new HashMap, ConstantSearchFuture>()); private final ConstantSearch myConstantSearch = new ConstantSearch(); + @NotNull private final BuildRunner myBuildRunner; private final boolean myForceModelLoading; private final BuildType myBuildType; @@ -78,24 +82,31 @@ final class BuildSession implements Runnable, CanceledStatus { BuildSession(UUID sessionId, Channel channel, CmdlineRemoteProto.Message.ControllerMessage.ParametersMessage params, - @Nullable CmdlineRemoteProto.Message.ControllerMessage.FSEvent delta) { + @Nullable CmdlineRemoteProto.Message.ControllerMessage.FSEvent delta, @Nullable PreloadedData preloaded) { mySessionId = sessionId; myChannel = channel; - + myPreloadedData = preloaded; + final CmdlineRemoteProto.Message.ControllerMessage.GlobalSettings globals = params.getGlobalSettings(); myProjectPath = FileUtil.toCanonicalPath(params.getProjectId()); String globalOptionsPath = FileUtil.toCanonicalPath(globals.getGlobalOptionsPath()); myBuildType = convertCompileType(params.getBuildType()); myScopes = params.getScopeList(); List filePaths = params.getFilePathList(); - Map builderParams = new HashMap(); + final Map builderParams = new HashMap(); for (CmdlineRemoteProto.Message.KeyValuePair pair : params.getBuilderParameterList()) { builderParams.put(pair.getKey(), pair.getValue()); } myInitialFSDelta = delta; - JpsModelLoaderImpl loader = new JpsModelLoaderImpl(myProjectPath, globalOptionsPath, null); - myForceModelLoading = Boolean.parseBoolean(builderParams.get(BuildParametersKeys.FORCE_MODEL_LOADING)); - myBuildRunner = new BuildRunner(loader, filePaths, builderParams); + if (preloaded == null || preloaded.getRunner() == null) { + myBuildRunner = new BuildRunner(new JpsModelLoaderImpl(myProjectPath, globalOptionsPath, null)); + } + else { + myBuildRunner = preloaded.getRunner(); + } + myBuildRunner.setFilePaths(filePaths); + myBuildRunner.setBuilderParams(builderParams); + myForceModelLoading = (preloaded != null && preloaded.getProjectDescriptor() != null) || Boolean.parseBoolean(builderParams.get(BuildParametersKeys.FORCE_MODEL_LOADING)); } @Override @@ -185,8 +196,9 @@ final class BuildSession implements Runnable, CanceledStatus { // invoked the very first time for this project myBuildRunner.setForceCleanCaches(true); } - - final DataInputStream fsStateStream = createFSDataStream(dataStorageRoot); + final ProjectDescriptor preloadedProject = myPreloadedData != null? myPreloadedData.getProjectDescriptor() : null; + final DataInputStream fsStateStream = + preloadedProject != null || myInitialFSDelta == null /*this will force FS rescan*/? null : createFSDataStream(dataStorageRoot, myInitialFSDelta.getOrdinal()); if (fsStateStream != null) { // optimization: check whether we can skip the build @@ -198,27 +210,56 @@ final class BuildSession implements Runnable, CanceledStatus { } } - final BuildFSState fsState = new BuildFSState(false); + final BuildFSState fsState = preloadedProject != null? preloadedProject.fsState : new BuildFSState(false); try { - final ProjectDescriptor pd = myBuildRunner.load(msgHandler, dataStorageRoot, fsState); - TimingLog.LOG.debug("Project descriptor loaded"); - myProjectDescriptor = pd; - if (fsStateStream != null) { - try { - try { - fsState.load(fsStateStream, pd.getModel(), pd.getBuildRootIndex()); - applyFSEvent(pd, myInitialFSDelta, false); - TimingLog.LOG.debug("FS Delta loaded"); - } - finally { - fsStateStream.close(); + final ProjectDescriptor pd; + if (preloadedProject != null) { + pd = preloadedProject; + final List preloadMessages = myPreloadedData.getLoadMessages(); + if (!preloadMessages.isEmpty()) { + // replay preload-time messages, so that they are delivered to the IDE + for (BuildMessage message : preloadMessages) { + msgHandler.processMessage(message); } } - catch (Throwable e) { - LOG.error(e); - fsState.clearAll(); + if (myInitialFSDelta == null || myPreloadedData.getFsEventOrdinal() + 1L != myInitialFSDelta.getOrdinal()) { + // FS rescan was forced + fsState.clearAll(); + } + else { + // apply events to already loaded state + try { + applyFSEvent(pd, myInitialFSDelta, false); + } + catch (Throwable e) { + LOG.error(e); + fsState.clearAll(); + } } } + else { + // standard case + pd = myBuildRunner.load(msgHandler, dataStorageRoot, fsState); + TimingLog.LOG.debug("Project descriptor loaded"); + if (fsStateStream != null) { + try { + try { + fsState.load(fsStateStream, pd.getModel(), pd.getBuildRootIndex()); + applyFSEvent(pd, myInitialFSDelta, false); + TimingLog.LOG.debug("FS Delta loaded"); + } + finally { + fsStateStream.close(); + } + } + catch (Throwable e) { + LOG.error(e); + fsState.clearAll(); + } + } + } + myProjectDescriptor = pd; + myLastEventOrdinal = myInitialFSDelta != null? myInitialFSDelta.getOrdinal() : 0L; // free memory @@ -469,11 +510,7 @@ final class BuildSession implements Runnable, CanceledStatus { } @Nullable - private DataInputStream createFSDataStream(File dataStorageRoot) { - if (myInitialFSDelta == null) { - // this will force FS rescan - return null; - } + private static DataInputStream createFSDataStream(File dataStorageRoot, final long currentEventOrdinal) { try { final File file = new File(dataStorageRoot, FS_STATE_FILE); byte[] bytes; @@ -490,7 +527,7 @@ final class BuildSession implements Runnable, CanceledStatus { return null; } final long savedOrdinal = in.readLong(); - if (savedOrdinal + 1L != myInitialFSDelta.getOrdinal()) { + if (savedOrdinal + 1L != currentEventOrdinal) { return null; } return in; diff --git a/jps/jps-builders/src/org/jetbrains/jps/cmdline/PreloadedData.java b/jps/jps-builders/src/org/jetbrains/jps/cmdline/PreloadedData.java new file mode 100644 index 000000000000..bf5ece5d6fdd --- /dev/null +++ b/jps/jps-builders/src/org/jetbrains/jps/cmdline/PreloadedData.java @@ -0,0 +1,71 @@ +/* + * 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.jps.cmdline; + +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jps.incremental.messages.BuildMessage; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Eugene Zhuravlev + * Date: 22-Oct-14 + */ +public class PreloadedData { + @Nullable + private BuildRunner runner; + @Nullable + private ProjectDescriptor projectDescriptor; + + private final List loadMessages = new ArrayList(); + + private long fsEventOrdinal; + + @Nullable + public BuildRunner getRunner() { + return runner; + } + + public void setRunner(@Nullable BuildRunner runner) { + this.runner = runner; + } + + @Nullable + public ProjectDescriptor getProjectDescriptor() { + return projectDescriptor; + } + + public void setProjectDescriptor(@Nullable ProjectDescriptor projectDescriptor) { + this.projectDescriptor = projectDescriptor; + } + + public long getFsEventOrdinal() { + return fsEventOrdinal; + } + + public void setFsEventOrdinal(long fsEventOrdinal) { + this.fsEventOrdinal = fsEventOrdinal; + } + + public List getLoadMessages() { + return loadMessages; + } + + public void addMessage(BuildMessage msg) { + loadMessages.add(msg); + } +} diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/JavacMain.java b/jps/jps-builders/src/org/jetbrains/jps/javac/JavacMain.java index ac4f4a0b78fc..751b74b459e2 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/javac/JavacMain.java +++ b/jps/jps-builders/src/org/jetbrains/jps/javac/JavacMain.java @@ -168,7 +168,16 @@ public class JavacMain { diagnosticConsumer.report(new PlainMessageDiagnostic(Diagnostic.Kind.ERROR, e.getMessage())); } catch (CompilationCanceledException ignored) { - diagnosticConsumer.report(new PlainMessageDiagnostic(Diagnostic.Kind.OTHER, "Compilation was canceled")); + handleCancelException(diagnosticConsumer); + } + catch (RuntimeException e) { + final Throwable cause = e.getCause(); + if (cause instanceof CompilationCanceledException) { + handleCancelException(diagnosticConsumer); + } + else { + throw e; + } } finally { fileManager.close(); @@ -179,6 +188,10 @@ public class JavacMain { return false; } + private static void handleCancelException(DiagnosticOutputConsumer diagnosticConsumer) { + diagnosticConsumer.report(new PlainMessageDiagnostic(Diagnostic.Kind.OTHER, "Compilation was canceled")); + } + private static List getSourceTransformers() { final Class transformerClass = JavaSourceTransformer.class; final ServiceLoader loader = ServiceLoader.load(transformerClass, transformerClass.getClassLoader()); diff --git a/jps/model-serialization/src/org/jetbrains/jps/model/serialization/JpsLoaderBase.java b/jps/model-serialization/src/org/jetbrains/jps/model/serialization/JpsLoaderBase.java index 18ad56192055..cf8c0cba3667 100644 --- a/jps/model-serialization/src/org/jetbrains/jps/model/serialization/JpsLoaderBase.java +++ b/jps/model-serialization/src/org/jetbrains/jps/model/serialization/JpsLoaderBase.java @@ -15,6 +15,7 @@ */ package org.jetbrains.jps.model.serialization; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.JDOMUtil; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtilRt; @@ -30,6 +31,8 @@ import java.io.IOException; * @author nik */ public abstract class JpsLoaderBase { + private static final Logger LOG = Logger.getInstance(JpsLoaderBase.class); + private static final int MAX_ATTEMPTS = 5; private final JpsMacroExpander myMacroExpander; protected JpsLoaderBase(JpsMacroExpander macroExpander) { @@ -66,7 +69,7 @@ public abstract class JpsLoaderBase { protected static Element loadRootElement(final File file, final JpsMacroExpander macroExpander) { try { - final Element element = JDOMUtil.loadDocument(file).getRootElement(); + final Element element = tryLoadRootElement(file); macroExpander.substitute(element, SystemInfo.isFileSystemCaseSensitive); return element; } @@ -78,6 +81,24 @@ public abstract class JpsLoaderBase { } } + private static Element tryLoadRootElement(File file) throws IOException, JDOMException { + for (int i = 0; i < MAX_ATTEMPTS - 1; i++) { + try { + return JDOMUtil.loadDocument(file).getRootElement(); + } + catch (Exception e) { + LOG.info("Loading attempt #" + i + " failed", e); + } + //most likely configuration file is being written by IDE so we'll wait a little + try { + //noinspection BusyWait + Thread.sleep(300); + } + catch (InterruptedException ignored) { } + } + return JDOMUtil.loadDocument(file).getRootElement(); + } + protected static boolean isXmlFile(File file) { return file.isFile() && FileUtilRt.extensionEquals(file.getName(), "xml"); } diff --git a/jps/standalone-builder/src/org/jetbrains/jps/build/Standalone.java b/jps/standalone-builder/src/org/jetbrains/jps/build/Standalone.java index 979fd78703f1..7b7d425f1a09 100644 --- a/jps/standalone-builder/src/org/jetbrains/jps/build/Standalone.java +++ b/jps/standalone-builder/src/org/jetbrains/jps/build/Standalone.java @@ -193,8 +193,7 @@ public class Standalone { public static void runBuild(JpsModelLoader loader, File dataStorageRoot, MessageHandler messageHandler, List scopes, boolean includeDependenciesToScope) throws Exception { - final BuildRunner buildRunner = new BuildRunner(loader, Collections.emptyList(), Collections.emptyMap() - ); + final BuildRunner buildRunner = new BuildRunner(loader); ProjectDescriptor descriptor = buildRunner.load(messageHandler, dataStorageRoot, new BuildFSState(true)); try { buildRunner.runBuild(descriptor, CanceledStatus.NULL, null, messageHandler, BuildType.BUILD, scopes, includeDependenciesToScope); diff --git a/json/src/com/intellij/json/psi/impl/JsonTreeChangePreprocessor.java b/json/src/com/intellij/json/psi/impl/JsonTreeChangePreprocessor.java index 5f51296b37d9..6bb2ee9ee166 100644 --- a/json/src/com/intellij/json/psi/impl/JsonTreeChangePreprocessor.java +++ b/json/src/com/intellij/json/psi/impl/JsonTreeChangePreprocessor.java @@ -1,6 +1,7 @@ package com.intellij.json.psi.impl; import com.intellij.json.JsonLanguage; +import com.intellij.json.psi.JsonFile; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFileSystemItem; @@ -20,6 +21,8 @@ public class JsonTreeChangePreprocessor implements PsiTreeChangePreprocessor { */ @Override public void treeChanged(@NotNull PsiTreeChangeEventImpl event) { + if (!(event.getFile() instanceof JsonFile)) return; + final PsiElement element = event.getParent(); if (element == null || !(element.getManager() instanceof PsiManagerImpl)) { return; diff --git a/json/tests/test/com/intellij/json/JsonStructureViewTest.java b/json/tests/test/com/intellij/json/JsonStructureViewTest.java index aa5e43cf89c5..24d563e6ef77 100644 --- a/json/tests/test/com/intellij/json/JsonStructureViewTest.java +++ b/json/tests/test/com/intellij/json/JsonStructureViewTest.java @@ -66,7 +66,9 @@ public class JsonStructureViewTest extends JsonTestCase { " -object\n" + " nestedObject\n" + " -array\n" + - " +array\n" + + " -array\n" + + " -object\n" + + " deepNestedObject\n" + " -object\n" + " siblingObject\n"); } diff --git a/lib/proxy-vole_20120920.jar b/lib/proxy-vole_20131209.jar similarity index 59% rename from lib/proxy-vole_20120920.jar rename to lib/proxy-vole_20131209.jar index 29db3d34eec3..70927e4f6c96 100644 Binary files a/lib/proxy-vole_20120920.jar and b/lib/proxy-vole_20131209.jar differ diff --git a/lib/required_for_dist.txt b/lib/required_for_dist.txt index 997eb52a6fc6..a989728f72e1 100644 --- a/lib/required_for_dist.txt +++ b/lib/required_for_dist.txt @@ -52,7 +52,7 @@ netty-all-4.1.0.Beta3.jar oromatcher.jar picocontainer.jar protobuf-2.5.0.jar -proxy-vole_20120920.jar +proxy-vole_20131209.jar pty4j-0.3.jar purejavacomm.jar resolver.jar diff --git a/platform/core-api/src/com/intellij/ide/WelcomeWizardUtil.java b/platform/core-api/src/com/intellij/ide/WelcomeWizardUtil.java new file mode 100644 index 000000000000..dabb6ac530ea --- /dev/null +++ b/platform/core-api/src/com/intellij/ide/WelcomeWizardUtil.java @@ -0,0 +1,83 @@ +/* + * 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.ide; + +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +public class WelcomeWizardUtil { + private static volatile String ourDefaultLAF; + private static volatile String ourWizardLAF; + private static volatile String ourWizardMacKeymap; + private static volatile String ourWizardEditorScheme; + private static volatile Boolean ourAutoScrollToSource; + private static volatile Set ourFeaturedPluginsToInstall = new HashSet(); + + public static void setDefaultLAF(String laf) { + ourDefaultLAF = laf; + } + + public static String getDefaultLAF() { + return ourDefaultLAF; + } + + public static void setWizardLAF(String laf) { + ourWizardLAF = laf; + } + + public static String getWizardLAF() { + return ourWizardLAF; + } + + public static void setWizardKeymap(@Nullable String keymap) { + ourWizardMacKeymap = keymap; + } + + @Nullable + public static String getWizardMacKeymap() { + return ourWizardMacKeymap; + } + + public static void setWizardEditorScheme(@Nullable String wizardEditorScheme) { + ourWizardEditorScheme = wizardEditorScheme; + } + + @Nullable + public static String getWizardEditorScheme() { + return ourWizardEditorScheme; + } + + @Nullable + public static Boolean getAutoScrollToSource() { + return ourAutoScrollToSource; + } + + public static void setAutoScrollToSource(@Nullable Boolean autoScrollToSource) { + ourAutoScrollToSource = autoScrollToSource; + } + + public static Set getFeaturedPluginsToInstall() { + return Collections.unmodifiableSet(ourFeaturedPluginsToInstall); + } + + public static void setFeaturedPluginsToInstall(Set pluginsToInstall) { + ourFeaturedPluginsToInstall.clear(); + ourFeaturedPluginsToInstall.addAll(pluginsToInstall); + } +} diff --git a/platform/core-api/src/com/intellij/lang/FCTSBackedLighterAST.java b/platform/core-api/src/com/intellij/lang/FCTSBackedLighterAST.java index b393f8d37707..2df20519e18d 100644 --- a/platform/core-api/src/com/intellij/lang/FCTSBackedLighterAST.java +++ b/platform/core-api/src/com/intellij/lang/FCTSBackedLighterAST.java @@ -23,7 +23,7 @@ import org.jetbrains.annotations.NotNull; import java.util.List; -class FCTSBackedLighterAST extends LighterAST { +public class FCTSBackedLighterAST extends LighterAST { private final FlyweightCapableTreeStructure myTreeStructure; public FCTSBackedLighterAST(final CharTable charTable, final FlyweightCapableTreeStructure treeStructure) { diff --git a/platform/core-api/src/com/intellij/lang/FileASTNode.java b/platform/core-api/src/com/intellij/lang/FileASTNode.java index 136530822a23..193e2421ebd1 100644 --- a/platform/core-api/src/com/intellij/lang/FileASTNode.java +++ b/platform/core-api/src/com/intellij/lang/FileASTNode.java @@ -17,6 +17,7 @@ package com.intellij.lang; import com.intellij.util.CharTable; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * Top-level node in AST, corresponds to PsiFile. @@ -28,4 +29,7 @@ public interface FileASTNode extends ASTNode { CharTable getCharTable(); boolean isParsed(); + + @Nullable + LighterAST getLighterAST(); } diff --git a/platform/core-api/src/com/intellij/lang/LighterAST.java b/platform/core-api/src/com/intellij/lang/LighterAST.java index 71305c5937be..de2c44aa1299 100644 --- a/platform/core-api/src/com/intellij/lang/LighterAST.java +++ b/platform/core-api/src/com/intellij/lang/LighterAST.java @@ -15,8 +15,6 @@ */ package com.intellij.lang; -import com.intellij.psi.tree.IFileElementType; -import com.intellij.psi.tree.ILightStubFileElementType; import com.intellij.util.CharTable; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -46,19 +44,4 @@ public abstract class LighterAST { @NotNull public abstract List getChildren(@NotNull final LighterASTNode parent); - - public static @Nullable LighterAST getLighterASTFromFileAST(@NotNull FileASTNode node, @NotNull Language language) { - final IFileElementType contentType = LanguageParserDefinitions.INSTANCE.forLanguage(language).getFileNodeType(); - assert contentType instanceof ILightStubFileElementType; - - final LighterAST tree; - if (!node.isParsed()) { - final ILightStubFileElementType type = (ILightStubFileElementType)contentType; - tree = new FCTSBackedLighterAST(node.getCharTable(), type.parseContentsLight(node)); - } - else { - tree = new TreeBackedLighterAST(node); - } - return tree; - } } \ No newline at end of file diff --git a/platform/core-api/src/com/intellij/psi/stubs/LightStubBuilder.java b/platform/core-api/src/com/intellij/psi/stubs/LightStubBuilder.java index 102288e1a9a0..e9dec24fca2c 100644 --- a/platform/core-api/src/com/intellij/psi/stubs/LightStubBuilder.java +++ b/platform/core-api/src/com/intellij/psi/stubs/LightStubBuilder.java @@ -47,7 +47,7 @@ public class LightStubBuilder implements StubBuilder { LOG.error("File is not of ILightStubFileElementType: " + contentType + ", " + file); return null; } - final LighterAST tree = LighterAST.getLighterASTFromFileAST(file.getNode(), language); + final LighterAST tree = file.getNode().getLighterAST(); if (tree == null) return null; final StubElement rootStub = createStubForFile(file, tree); diff --git a/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java b/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java index d80d36822eb1..c4681c4d77e9 100644 --- a/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java +++ b/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java @@ -30,7 +30,10 @@ import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.io.StreamUtil; import com.intellij.openapi.util.io.ZipFileCache; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.util.*; +import com.intellij.util.ArrayUtil; +import com.intellij.util.Function; +import com.intellij.util.PlatformUtilsCore; +import com.intellij.util.ReflectionUtil; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.MultiMap; import com.intellij.util.execution.ParametersListUtil; @@ -166,7 +169,7 @@ public class PluginManagerCore { return app != null && app.isUnitTestMode(); } - public static void savePluginsList(Collection ids, boolean append, File plugins) throws IOException { + public static void savePluginsList(@NotNull Collection ids, boolean append, @NotNull File plugins) throws IOException { if (!plugins.isFile()) { FileUtil.ensureCanCreateFile(plugins); } @@ -700,7 +703,13 @@ public class PluginManagerCore { List result, @Nullable StartupProgress progress, int pluginsCount) { - final File pluginsHome = new File(pluginsPath); + loadDescriptors(new File(pluginsPath), result, progress, pluginsCount); + } + + public static void loadDescriptors(@NotNull File pluginsHome, + List result, + @Nullable StartupProgress progress, + int pluginsCount) { final File[] files = pluginsHome.listFiles(); if (files != null) { int i = result.size(); diff --git a/platform/core-impl/src/com/intellij/lang/impl/PsiBuilderImpl.java b/platform/core-impl/src/com/intellij/lang/impl/PsiBuilderImpl.java index e03244e0e0e9..0bc853c76815 100644 --- a/platform/core-impl/src/com/intellij/lang/impl/PsiBuilderImpl.java +++ b/platform/core-impl/src/com/intellij/lang/impl/PsiBuilderImpl.java @@ -89,6 +89,7 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder { private final ASTNode myOriginalTree; private final MyTreeStructure myParentLightTree; + private final int myOffset; private static TokenSet ourAnyLanguageWhitespaceTokens = TokenSet.EMPTY; @@ -132,7 +133,7 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder { @Nullable ASTNode originalTree, @Nullable MyTreeStructure parentLightTree) { this(project, containingFile, parserDefinition.getWhitespaceTokens(), parserDefinition.getCommentTokens(), lexer, charTable, text, - originalTree, parentLightTree); + originalTree, parentLightTree, 0); } public PsiBuilderImpl(Project project, @@ -144,6 +145,19 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder { @NotNull final CharSequence text, @Nullable ASTNode originalTree, @Nullable MyTreeStructure parentLightTree) { + this(project, containingFile, whiteSpaces, comments, lexer, charTable, text, originalTree, parentLightTree, 0); + } + + private PsiBuilderImpl(Project project, + PsiFile containingFile, + @NotNull TokenSet whiteSpaces, + @NotNull TokenSet comments, + @NotNull Lexer lexer, + CharTable charTable, + @NotNull final CharSequence text, + @Nullable ASTNode originalTree, + @Nullable MyTreeStructure parentLightTree, + int offset) { myProject = project; myFile = containingFile; @@ -156,6 +170,7 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder { myCharTable = charTable; myOriginalTree = originalTree; myParentLightTree = parentLightTree; + myOffset = offset; cacheLexemes(); } @@ -174,8 +189,8 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder { @NotNull final Lexer lexer, @NotNull final LighterLazyParseableNode chameleon, @NotNull final CharSequence text) { - this(project, chameleon.getContainingFile(), parserDefinition, lexer, chameleon.getCharTable(), text, - null, ((LazyParseableToken)chameleon).myParent); + this(project, chameleon.getContainingFile(), parserDefinition.getWhitespaceTokens(), parserDefinition.getCommentTokens(), lexer, + chameleon.getCharTable(), text, null, ((LazyParseableToken)chameleon).myParent, chameleon.getStartOffset()); } private void cacheLexemes() { @@ -335,12 +350,12 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder { @Override public int getStartOffset() { - return myBuilder.myLexStarts[myLexemeIndex]; + return myBuilder.myLexStarts[myLexemeIndex] + myBuilder.myOffset; } @Override public int getEndOffset() { - return myBuilder.myLexStarts[myDoneMarker.myLexemeIndex]; + return myBuilder.myLexStarts[myDoneMarker.myLexemeIndex] + myBuilder.myOffset; } @Override @@ -487,12 +502,12 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder { @Override public int getEndOffset() { - return myTokenEnd; + return myTokenEnd + myBuilder.myOffset; } @Override public int getStartOffset() { - return myTokenStart; + return myTokenStart + myBuilder.myOffset; } public CharSequence getText() { @@ -636,12 +651,12 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder { @Override public int getEndOffset() { - return myBuilder.myLexStarts[myLexemeIndex]; + return myBuilder.myLexStarts[myLexemeIndex] + myBuilder.myOffset; } @Override public int getStartOffset() { - return myBuilder.myLexStarts[myLexemeIndex]; + return myBuilder.myLexStarts[myLexemeIndex] + myBuilder.myOffset; } @Override @@ -1474,6 +1489,7 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder { private final LimitedPool myPool; private final LimitedPool myLazyPool; private final StartMarker myRoot; + private final int myOffset; public MyTreeStructure(@NotNull StartMarker root, @Nullable final MyTreeStructure parentTree) { if (parentTree == null) { @@ -1499,10 +1515,12 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder { return new LazyParseableToken(); } }); + myOffset = 0; } else { myPool = parentTree.myPool; myLazyPool = parentTree.myLazyPool; + myOffset = parentTree.getRoot().getStartOffset(); } myRoot = root; } diff --git a/platform/core-impl/src/com/intellij/psi/impl/source/tree/FileElement.java b/platform/core-impl/src/com/intellij/psi/impl/source/tree/FileElement.java index d3071ddbca7d..a2348eabbe97 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/source/tree/FileElement.java +++ b/platform/core-impl/src/com/intellij/psi/impl/source/tree/FileElement.java @@ -16,16 +16,19 @@ package com.intellij.psi.impl.source.tree; -import com.intellij.lang.ASTNode; -import com.intellij.lang.FileASTNode; +import com.intellij.lang.*; import com.intellij.openapi.util.Getter; +import com.intellij.openapi.util.Key; import com.intellij.psi.PsiElement; import com.intellij.psi.impl.PsiManagerEx; import com.intellij.psi.impl.source.CharTableImpl; import com.intellij.psi.impl.source.PsiFileImpl; import com.intellij.psi.tree.IElementType; +import com.intellij.psi.tree.IFileElementType; +import com.intellij.psi.tree.ILightStubFileElementType; import com.intellij.util.CharTable; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public class FileElement extends LazyParseableElement implements FileASTNode, Getter { private volatile CharTable myCharTable = new CharTableImpl(); @@ -47,6 +50,35 @@ public class FileElement extends LazyParseableElement implements FileASTNode, Ge return myCharTable; } + private static final Key ourTreeKey = Key.create("lighter.key"); + + @Nullable + @Override + public LighterAST getLighterAST() { + final IFileElementType contentType = (IFileElementType)getElementType(); + assert contentType instanceof ILightStubFileElementType; + + LighterAST tree; + if (!isParsed()) { + tree = getUserData(ourTreeKey); + if (tree == null) { + final ILightStubFileElementType type = (ILightStubFileElementType)contentType; + tree = new FCTSBackedLighterAST(getCharTable(), type.parseContentsLight(this)); + putUserData(ourTreeKey, tree); + } + } + else { + tree = new TreeBackedLighterAST(this); + } + return tree; + } + + @Override + protected void resetCachesOnParsedStateUpdate() { + putUserData(ourTreeKey, null); + super.resetCachesOnParsedStateUpdate(); + } + public FileElement(IElementType type, CharSequence text) { super(type, text); } diff --git a/platform/core-impl/src/com/intellij/psi/impl/source/tree/LazyParseableElement.java b/platform/core-impl/src/com/intellij/psi/impl/source/tree/LazyParseableElement.java index ebc699aaf181..85a28f195282 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/source/tree/LazyParseableElement.java +++ b/platform/core-impl/src/com/intellij/psi/impl/source/tree/LazyParseableElement.java @@ -181,8 +181,8 @@ public class LazyParseableElement extends CompositeElement { if (rawFirstChild() != null) { LOG.error("Reentrant parsing?"); } - - myText = null; + + resetCachesOnParsedStateUpdate(); if (parsedNode == null) return; super.rawAddChildrenWithoutNotifications((TreeElement)parsedNode); @@ -199,6 +199,10 @@ public class LazyParseableElement extends CompositeElement { } } + protected void resetCachesOnParsedStateUpdate() { + myText = null; + } + @Override public void rawAddChildrenWithoutNotifications(@NotNull TreeElement first) { if (myText() != null) { diff --git a/platform/duplicates-analysis/src/com/intellij/dupLocator/LightDuplicateProfile.java b/platform/duplicates-analysis/src/com/intellij/dupLocator/LightDuplicateProfile.java new file mode 100644 index 000000000000..c3011c11454e --- /dev/null +++ b/platform/duplicates-analysis/src/com/intellij/dupLocator/LightDuplicateProfile.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.dupLocator; + +import com.intellij.lang.LighterAST; +import com.intellij.lang.LighterASTNode; +import com.intellij.psi.PsiFile; + +/** + * Created by Maxim.Mossienko on 10/1/2014. + */ +public interface LightDuplicateProfile { + void process(PsiFile file, Callback callback); + + interface Callback { + void process(LighterAST ast, LighterASTNode node, int hash); + } +} diff --git a/platform/duplicates-analysis/src/com/intellij/dupLocator/index/DuplicatesIndex.java b/platform/duplicates-analysis/src/com/intellij/dupLocator/index/DuplicatesIndex.java index 55367ae4a19d..cf4249cecd65 100644 --- a/platform/duplicates-analysis/src/com/intellij/dupLocator/index/DuplicatesIndex.java +++ b/platform/duplicates-analysis/src/com/intellij/dupLocator/index/DuplicatesIndex.java @@ -18,15 +18,18 @@ package com.intellij.dupLocator.index; import com.intellij.dupLocator.DuplicatesProfile; import com.intellij.dupLocator.DuplocateVisitor; import com.intellij.dupLocator.DuplocatorState; +import com.intellij.dupLocator.LightDuplicateProfile; import com.intellij.dupLocator.treeHash.FragmentsCollector; import com.intellij.dupLocator.util.PsiFragment; import com.intellij.lang.Language; +import com.intellij.lang.LighterAST; +import com.intellij.lang.LighterASTNode; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.fileTypes.LanguageFileType; -import com.intellij.openapi.util.Computable; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiFile; import com.intellij.util.SystemProperties; import com.intellij.util.indexing.*; import com.intellij.util.io.DataExternalizer; @@ -52,6 +55,7 @@ import java.util.Map; public class DuplicatesIndex extends FileBasedIndexExtension implements PsiDependentIndex { static boolean ourEnabled = SystemProperties.getBooleanProperty("idea.enable.duplicates.online.calculation", isEnabledByDefault()); + static boolean ourEnabledLightProfiles = true; private static boolean isEnabledByDefault() { Application application = ApplicationManager.getApplication(); @@ -59,7 +63,7 @@ public class DuplicatesIndex extends FileBasedIndexExtension NAME = ID.create("DuplicatesIndex"); - private static final int myBaseVersion = 10; + private static final int myBaseVersion = 12; private final FileBasedIndex.InputFilter myInputFilter = new FileBasedIndex.InputFilter() { @Override @@ -107,21 +111,31 @@ public class DuplicatesIndex extends FileBasedIndexExtension map(@NotNull final FileContent inputData) { - return ApplicationManager.getApplication().runReadAction(new Computable>() { - @Override - public Map compute() { - FileType type = inputData.getFileType(); + FileType type = inputData.getFileType(); - DuplicatesProfile profile = findDuplicatesProfile(type); - if (profile == null) return Collections.emptyMap(); + DuplicatesProfile profile = findDuplicatesProfile(type); + if (profile == null) return Collections.emptyMap(); - MyFragmentsCollector collector = new MyFragmentsCollector(profile, ((LanguageFileType)type).getLanguage()); - DuplocateVisitor visitor = profile.createVisitor(collector, true); - visitor.visitNode(((FileContentImpl)inputData).getPsiFileAccountingForUnsavedDocument()); + PsiFile psiFile = ((FileContentImpl)inputData).getPsiFileAccountingForUnsavedDocument(); - return collector.getMap(); - } - }); + if (profile instanceof LightDuplicateProfile && ourEnabledLightProfiles) { + final THashMap result = new THashMap(); + ((LightDuplicateProfile)profile).process(psiFile, new LightDuplicateProfile.Callback() { + @Override + public void process(LighterAST ast, LighterASTNode node, int hash) { + TIntArrayList list = result.get(hash); + if (list == null) { result.put(hash, list = new TIntArrayList(1)); } + list.add(node.getStartOffset()); + } + }); + return result; + } + MyFragmentsCollector collector = new MyFragmentsCollector(profile, ((LanguageFileType)type).getLanguage()); + DuplocateVisitor visitor = profile.createVisitor(collector, true); + + visitor.visitNode(psiFile); + + return collector.getMap(); } }; @@ -135,7 +149,7 @@ public class DuplicatesIndex extends FileBasedIndexExtension descriptors = new SmartList(); + final Ref myProcessorRef = new Ref(); - final TreeMap reportedRanges = new TreeMap(); - final TIntObjectHashMap reportedFiles = new TIntObjectHashMap(); - final TIntObjectHashMap reportedPsi = new TIntObjectHashMap(); - final TIntIntHashMap reportedOffsetInOtherFiles = new TIntIntHashMap(); - final TIntIntHashMap fragmentSize = new TIntIntHashMap(); + if (profile instanceof LightDuplicateProfile && DuplicatesIndex.ourEnabledLightProfiles) { - profile.createVisitor(new FragmentsCollector() { - @Override - public void add(int hash, final int cost, @Nullable final PsiFragment frag) { - if (!DuplicatesIndex.isIndexedFragment(frag, cost, profile, state)) { - return; + ((LightDuplicateProfile)profile).process(psiFile, new LightDuplicateProfile.Callback() { + DuplicatedCodeProcessor myProcessor; + @Override + public void process(final LighterAST ast, final LighterASTNode node, int hash) { + class LightDuplicatedCodeProcessor extends DuplicatedCodeProcessor { + + LightDuplicatedCodeProcessor(VirtualFile file, Project project) { + super(file, project); + } + + @Override + protected TextRange getRangeInElement(LighterASTNode node) { + return null; + } + + @Override + protected PsiElement getPsi(LighterASTNode node) { + return ((TreeBackedLighterAST)ast).unwrap(node).getPsi(); + } + + @Override + protected int getStartOffset(LighterASTNode node) { + return node.getStartOffset(); + } + + @Override + protected int getEndOffset(LighterASTNode node) { + return node.getEndOffset(); + } + } + if (myProcessor == null) { + myProcessor = new LightDuplicatedCodeProcessor(virtualFile, psiFile.getProject()); + myProcessorRef.set(myProcessor); + } + myProcessor.process(hash, node); } + }); + } else { + final DuplocatorState state = profile.getDuplocatorState(psiFile.getLanguage()); + profile.createVisitor(new FragmentsCollector() { + DuplicatedCodeProcessor myProcessor; + @Override + public void add(int hash, final int cost, @Nullable final PsiFragment frag) { + if (!DuplicatesIndex.isIndexedFragment(frag, cost, profile, state)) { + return; + } - ProgressManager.checkCanceled(); - FileBasedIndex.getInstance().processValues(DuplicatesIndex.NAME, hash, null, new FileBasedIndex.ValueProcessor() { - final ProjectFileIndex myProjectFileIndex = ProjectFileIndex.SERVICE.getInstance(psiFile.getProject()); - @Override - public boolean process(final VirtualFile file, final TIntArrayList list) { - for(int i = 0, len = list.size(); i < len; ++i) { - ProgressManager.checkCanceled(); + class OldDuplicatedCodeProcessor extends DuplicatedCodeProcessor { - int value = list.getQuick(i); + OldDuplicatedCodeProcessor(VirtualFile file, Project project) { + super(file, project); + } - if (myProjectFileIndex.isInSource(virtualFile) && !myProjectFileIndex.isInSource(file)) return true; - if (!myProjectFileIndex.isInSource(virtualFile) && myProjectFileIndex.isInSource(file)) return true; - final int startOffset = frag.getStartOffset(); - final int endOffset = frag.getEndOffset(); - if (file.equals(virtualFile) && value >= startOffset && value < endOffset) continue; - - PsiElement[] elements = frag.getElements(); - PsiElement target = elements[0]; + @Override + protected TextRange getRangeInElement(PsiFragment node) { + PsiElement[] elements = node.getElements(); TextRange rangeInElement = null; if (elements.length > 1) { - PsiElement firstElement = elements[0]; - target = firstElement.getParent(); + PsiElement lastElement = elements[elements.length - 1]; rangeInElement = new TextRange( elements[0].getStartOffsetInParent(), lastElement.getStartOffsetInParent() + lastElement.getTextLength() ); } + return rangeInElement; + } - Integer fragmentStartOffsetInteger = startOffset; - SortedMap map = reportedRanges.subMap(fragmentStartOffsetInteger, endOffset); - int newFragmentSize = !map.isEmpty() ? 0:1; + @Override + protected PsiElement getPsi(PsiFragment node) { + PsiElement[] elements = node.getElements(); - Iterator iterator = map.keySet().iterator(); - while(iterator.hasNext()) { - Integer next = iterator.next(); - iterator.remove(); - reportedFiles.remove(next); - reportedOffsetInOtherFiles.remove(next); - reportedPsi.remove(next); - newFragmentSize += fragmentSize.remove(next); - } + return elements.length > 1 ? elements[0].getParent() : elements[0]; + } - reportedRanges.put(fragmentStartOffsetInteger, rangeInElement); - reportedFiles.put(fragmentStartOffsetInteger, file); - reportedOffsetInOtherFiles.put(fragmentStartOffsetInteger, value); - reportedPsi.put(fragmentStartOffsetInteger, target); - fragmentSize.put(fragmentStartOffsetInteger, newFragmentSize); + @Override + protected int getStartOffset(PsiFragment node) { + return node.getStartOffset(); + } - return false; + @Override + protected int getEndOffset(PsiFragment node) { + return node.getEndOffset(); } - return true; } - }, GlobalSearchScope.projectScope(psiFile.getProject())); + if (myProcessor == null) { + myProcessor = new OldDuplicatedCodeProcessor(virtualFile, psiFile.getProject()); + myProcessorRef.set(myProcessor); + } + myProcessor.process(hash, frag); + } + }, true).visitNode(psiFile); + } + + DuplicatedCodeProcessor processor = myProcessorRef.get(); + + final SmartList descriptors = new SmartList(); + + if (processor != null) { + + for(Map.Entry entry:processor.reportedRanges.entrySet()) { + final Integer offset = entry.getKey(); + // todo 3 statements constant + if (processor.fragmentSize.get(offset) < 3) continue; + final VirtualFile file = processor.reportedFiles.get(offset); + String message = "Found duplicated code in " + file.getPath(); + + PsiElement targetElement = processor.reportedPsi.get(offset); + TextRange rangeInElement = entry.getValue(); + final int offsetInOtherFile = processor.reportedOffsetInOtherFiles.get(offset); + + LocalQuickFix fix = createNavigateToDupeFix(file, offsetInOtherFile); + ProblemDescriptor descriptor = manager + .createProblemDescriptor(targetElement, rangeInElement, message, ProblemHighlightType.WEAK_WARNING, isOnTheFly, fix); + descriptors.add(descriptor); } - }, true).visitNode(psiFile); - - for(Map.Entry entry:reportedRanges.entrySet()) { - final Integer offset = entry.getKey(); - // todo 3 statements constant - if (fragmentSize.get(offset) < 3) continue; - final VirtualFile file = reportedFiles.get(offset); - String message = "Found duplicated code in " + file.getPath(); - - PsiElement targetElement = reportedPsi.get(offset); - TextRange rangeInElement = entry.getValue(); - final int offsetInOtherFile = reportedOffsetInOtherFiles.get(offset); - - LocalQuickFix fix = createNavigateToDupeFix(file, offsetInOtherFile); - ProblemDescriptor descriptor = manager - .createProblemDescriptor(targetElement, rangeInElement, message, ProblemHighlightType.WEAK_WARNING, isOnTheFly, fix); - descriptors.add(descriptor); } return descriptors.isEmpty() ? null : descriptors.toArray(new ProblemDescriptor[descriptors.size()]); @@ -131,4 +169,75 @@ public class DuplicatesInspectionBase extends LocalInspectionTool { protected LocalQuickFix createNavigateToDupeFix(@NotNull VirtualFile file, int offsetInOtherFile) { return null; } + + static abstract class DuplicatedCodeProcessor implements FileBasedIndex.ValueProcessor { + final TreeMap reportedRanges = new TreeMap(); + final TIntObjectHashMap reportedFiles = new TIntObjectHashMap(); + final TIntObjectHashMap reportedPsi = new TIntObjectHashMap(); + final TIntIntHashMap reportedOffsetInOtherFiles = new TIntIntHashMap(); + final TIntIntHashMap fragmentSize = new TIntIntHashMap(); + final VirtualFile virtualFile; + final Project project; + final ProjectFileIndex myProjectFileIndex; + T myNode; + + DuplicatedCodeProcessor(VirtualFile file, Project project) { + virtualFile = file; + this.project = project; + myProjectFileIndex = ProjectFileIndex.SERVICE.getInstance(project); + } + + void process(int hash, T node) { + ProgressManager.checkCanceled(); + myNode = node; + FileBasedIndex.getInstance().processValues(DuplicatesIndex.NAME, hash, null, this, GlobalSearchScope.projectScope(project)); + } + + @Override + public boolean process(VirtualFile file, TIntArrayList list) { + for(int i = 0, len = list.size(); i < len; ++i) { + ProgressManager.checkCanceled(); + + int value = list.getQuick(i); + + if (myProjectFileIndex.isInSource(virtualFile) && !myProjectFileIndex.isInSource(file)) return true; + if (!myProjectFileIndex.isInSource(virtualFile) && myProjectFileIndex.isInSource(file)) return true; + final int startOffset = getStartOffset(myNode); + final int endOffset = getEndOffset(myNode); + if (file.equals(virtualFile) && value >= startOffset && value < endOffset) continue; + + PsiElement target = getPsi(myNode); + TextRange rangeInElement = getRangeInElement(myNode); + + Integer fragmentStartOffsetInteger = startOffset; + SortedMap map = reportedRanges.subMap(fragmentStartOffsetInteger, endOffset); + int newFragmentSize = !map.isEmpty() ? 0:1; + + Iterator iterator = map.keySet().iterator(); + while(iterator.hasNext()) { + Integer next = iterator.next(); + iterator.remove(); + reportedFiles.remove(next); + reportedOffsetInOtherFiles.remove(next); + reportedPsi.remove(next); + newFragmentSize += fragmentSize.remove(next); + } + + reportedRanges.put(fragmentStartOffsetInteger, rangeInElement); + reportedFiles.put(fragmentStartOffsetInteger, file); + reportedOffsetInOtherFiles.put(fragmentStartOffsetInteger, value); + reportedPsi.put(fragmentStartOffsetInteger, target); + fragmentSize.put(fragmentStartOffsetInteger, newFragmentSize); + + return false; + } + return true; + } + + protected abstract TextRange getRangeInElement(T node); + protected abstract PsiElement getPsi(T node); + + protected abstract int getStartOffset(T node); + protected abstract int getEndOffset(T node); + } } diff --git a/platform/dvcs-api/src/com/intellij/dvcs/push/PushSettings.java b/platform/dvcs-api/src/com/intellij/dvcs/push/PushSettings.java new file mode 100644 index 000000000000..e1250c3dfeb4 --- /dev/null +++ b/platform/dvcs-api/src/com/intellij/dvcs/push/PushSettings.java @@ -0,0 +1,61 @@ +/* + * 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.dvcs.push; + +import com.intellij.openapi.components.PersistentStateComponent; +import com.intellij.openapi.components.State; +import com.intellij.openapi.components.Storage; +import com.intellij.openapi.components.StoragePathMacros; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.xmlb.annotations.AbstractCollection; +import com.intellij.util.xmlb.annotations.Tag; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Set; + +@State(name = "Push.Settings", storages = {@Storage(file = StoragePathMacros.WORKSPACE_FILE)}) +public class PushSettings implements PersistentStateComponent { + + private State myState = new State(); + + public static class State { + @Tag("excluded-roots") + @AbstractCollection(surroundWithTag = false, elementTag = "path") + public Set EXCLUDED_ROOTS = ContainerUtil.newHashSet(); + } + + @Nullable + @Override + public State getState() { + return myState; + } + + @Override + public void loadState(State state) { + myState = state; + } + + @NotNull + public Set getExcludedRepoRoots() { + return myState.EXCLUDED_ROOTS; + } + + public void saveExcludedRepoRoots(@NotNull Set roots) { + myState.EXCLUDED_ROOTS = roots; + } +} + diff --git a/platform/dvcs-impl/src/META-INF/dvcs.xml b/platform/dvcs-impl/src/META-INF/dvcs.xml index aa81616f24f6..dc8a45b5f9b1 100644 --- a/platform/dvcs-impl/src/META-INF/dvcs.xml +++ b/platform/dvcs-impl/src/META-INF/dvcs.xml @@ -8,5 +8,8 @@ + + + diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/push/PushController.java b/platform/dvcs-impl/src/com/intellij/dvcs/push/PushController.java index 5de06ffd7969..e4a8c1a64826 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/push/PushController.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/push/PushController.java @@ -18,8 +18,8 @@ package com.intellij.dvcs.push; import com.intellij.dvcs.DvcsUtil; import com.intellij.dvcs.push.ui.*; import com.intellij.dvcs.repo.Repository; -import com.intellij.dvcs.repo.RepositoryManager; import com.intellij.openapi.Disposable; +import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.Task; @@ -53,6 +53,8 @@ public class PushController implements Disposable { @NotNull private final List> myPushSupports; @NotNull private final PushLog myPushLog; @NotNull private final VcsPushDialog myDialog; + @NotNull private final PushSettings myExcludedSettings; + @NotNull private final Set myExcludedRepositoryRoots; private boolean mySingleRepoProject; private static final int DEFAULT_CHILDREN_PRESENTATION_NUMBER = 20; private final ExecutorService myExecutorService = Executors.newSingleThreadExecutor(); @@ -64,6 +66,8 @@ public class PushController implements Disposable { @NotNull VcsPushDialog dialog, @NotNull List preselectedRepositories) { myProject = project; + myExcludedSettings = ServiceManager.getService(project, PushSettings.class); + myExcludedRepositoryRoots = ContainerUtil.newHashSet(myExcludedSettings.getExcludedRepoRoots()); myPreselectedRepositories = preselectedRepositories; myPushSupports = getAffectedSupports(myProject); mySingleRepoProject = isSingleRepoProject(myPushSupports); @@ -125,7 +129,8 @@ public class PushController implements Disposable { Map others = ContainerUtil.newLinkedHashMap(); for (Map.Entry entry : myView2Model.entrySet()) { MyRepoModel model = entry.getValue(); - if (myPreselectedRepositories.contains(model.getRepository())) { + Repository repository = model.getRepository(); + if (preselectByUser(repository)) { priorityLoading.put(entry.getKey(), model); } else if (model.getSupport().shouldRequestIncomingChangesForNotCheckedRepositories()) { @@ -188,6 +193,7 @@ public class PushController implements Disposable { @Override public void onTargetChanged(T newTarget) { repoNode.setChecked(true); + myExcludedRepositoryRoots.remove(model.getRepository().getRoot().getPath()); model.setTarget(newTarget); model.clearErrors(); loadCommits(model, repoNode, false); @@ -196,9 +202,14 @@ public class PushController implements Disposable { @Override public void onSelectionChanged(boolean isSelected) { myDialog.enableOkActions(isPushAllowed()); - if (isSelected && !model.hasCommitInfo() && !model.getSupport().shouldRequestIncomingChangesForNotCheckedRepositories()) { - //download incoming if was not loaded before and marked as selected - loadCommits(model, repoNode, false); + if (isSelected) { + boolean forceLoad = myExcludedRepositoryRoots.remove(model.getRepository().getRoot().getPath()); + if (!model.hasCommitInfo() && (forceLoad || !model.getSupport().shouldRequestIncomingChangesForNotCheckedRepositories())) { + loadCommits(model, repoNode, false); + } + } + else { + myExcludedRepositoryRoots.add(model.getRepository().getRoot().getPath()); } } }); @@ -235,9 +246,54 @@ public class PushController implements Disposable { public boolean isPushAllowed() { JTree tree = myPushLog.getTree(); - return !tree.isEditing() && !getSelectedRepoNode().isEmpty() && allNodesAreLoaded(myView2Model.keySet()); + return !tree.isEditing() && + ContainerUtil.exists(myPushSupports, new Condition>() { + @Override + public boolean value(PushSupport support) { + return isPushAllowed(support); + } + }); } + private boolean isPushAllowed(@NotNull PushSupport pushSupport) { + Collection nodes = getNodesForSupport(pushSupport); + if (pushSupport.getRepositoryManager().isSyncEnabled()) { + return hasSomethingToPush(nodes) || (hasCheckedNode(nodes) && allNodesAreLoaded(nodes)); + } + return hasSomethingToPush(nodes) || (hasCheckedNode(nodes)); + } + + private boolean hasSomethingToPush(Collection nodes) { + return ContainerUtil.exists(nodes, new Condition() { + @Override + public boolean value(@NotNull RepositoryNode node) { + PushTarget target = myView2Model.get(node).getTarget(); + //if node is selected target should not be null + return (node.isChecked() || node.isLoading()) && target != null && target.hasSomethingToPush(); + } + }); + } + + private static boolean hasCheckedNode(@NotNull Collection nodes) { + return ContainerUtil.exists(nodes, new Condition() { + @Override + public boolean value(@NotNull RepositoryNode node) { + return node.isChecked(); + } + }); + } + + @NotNull + private Collection getNodesForSupport(final PushSupport support) { + return ContainerUtil.mapNotNull(myView2Model.entrySet(), new Function, RepositoryNode>() { + @Override + public RepositoryNode fun(Map.Entry entry) { + return support.equals(entry.getValue().getSupport()) ? entry.getKey() : null; + } + }); + } + + private static boolean allNodesAreLoaded(@NotNull Collection nodes) { return !ContainerUtil.exists(nodes, new Condition() { @Override @@ -294,8 +350,8 @@ public class PushController implements Disposable { } else { List commits = outgoing.getCommits(); - shouldBeSelected = shouldSelectInitially(target, repository, model.getSupport().getRepositoryManager(), !commits.isEmpty()); model.setLoadedCommits(commits); + shouldBeSelected = shouldSelect(model); myPushLog.setChildren(node, getPresentationForCommits(PushController.this.myProject, model.getLoadedCommits(), model.getNumberOfShownCommits())); @@ -315,19 +371,24 @@ public class PushController implements Disposable { node.startLoading(myPushLog.getTree(), myExecutorService.submit(task, result)); } - private boolean shouldSelectInitially(@NotNull PushTarget target, @NotNull Repository repository, - @NotNull RepositoryManager repositoryManager, boolean hasCommits) { - boolean shouldBeSelected; - if (mySingleRepoProject) { - shouldBeSelected = true; - } - else if (repositoryManager.isSyncEnabled()) { - shouldBeSelected = hasCommits || target.hasSomethingToPush(); - } - else { - shouldBeSelected = (hasCommits || target.hasSomethingToPush()) && myPreselectedRepositories.contains(repository); - } - return shouldBeSelected; + private boolean shouldSelect(@NotNull MyRepoModel model) { + if (mySingleRepoProject) return true; + Repository repository = model.getRepository(); + return hasCommitsToPush(model) && (preselectByUser(repository) || notExcludedByUser(repository)); + } + + private boolean notExcludedByUser(@NotNull Repository repository) { + return !myExcludedRepositoryRoots.contains(repository.getRoot().getPath()); + } + + private boolean preselectByUser(@NotNull Repository repository) { + return myPreselectedRepositories.contains(repository); + } + + private static boolean hasCommitsToPush(@NotNull MyRepoModel model) { + PushTarget target = model.getTarget(); + assert target != null; + return (!model.getLoadedCommits().isEmpty() || target.hasSomethingToPush()); } public PushLog getPushPanelLog() { @@ -338,6 +399,7 @@ public class PushController implements Disposable { Task.Backgroundable task = new Task.Backgroundable(myProject, "Pushing...", false) { @Override public void run(@NotNull ProgressIndicator indicator) { + myExcludedSettings.saveExcludedRepoRoots(myExcludedRepositoryRoots); for (PushSupport support : myPushSupports) { doPush(support, force); } diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/LoadingIcon.java b/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/LoadingIcon.java index 0105032eba97..b983e8f46024 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/LoadingIcon.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/LoadingIcon.java @@ -69,6 +69,7 @@ class LoadingIcon extends JBImageIcon { } public boolean imageUpdate(Image img, int flags, int x, int y, int w, int h) { + if (myNode instanceof RepositoryNode && !((RepositoryNode)myNode).isLoading()) return false; if ((flags & (FRAMEBITS | ALLBITS)) != 0) { TreeNode[] pathToRoot = myModel.getPathToRoot(myNode); if (pathToRoot != null) { diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/RepositoryNode.java b/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/RepositoryNode.java index 12236ed4aefe..6a31c1a123b7 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/RepositoryNode.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/RepositoryNode.java @@ -155,6 +155,7 @@ public class RepositoryNode extends CheckedTreeNode implements EditableTreeNode, public void stopLoading() { myLoading.set(false); + myLoadingIcon.setImageObserver(null); } public boolean isLoading() { diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/VcsPushDialog.java b/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/VcsPushDialog.java index a86d9368e7d2..03fdca55c2c2 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/VcsPushDialog.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/VcsPushDialog.java @@ -25,6 +25,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.ui.OptionAction; +import com.intellij.openapi.ui.ValidationInfo; import net.miginfocom.swing.MigLayout; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -81,6 +82,18 @@ public class VcsPushDialog extends DialogWrapper { return ID; } + @Nullable + @Override + protected ValidationInfo doValidate() { + enableOkActions(myController.isPushAllowed()); + return null; + } + + @Override + protected boolean postponeValidation() { + return false; + } + @Override @NotNull protected Action[] createActions() { diff --git a/platform/editor-ui-api/src/com/intellij/ide/ui/UISettings.java b/platform/editor-ui-api/src/com/intellij/ide/ui/UISettings.java index 998620ffa599..4a390bc1383b 100644 --- a/platform/editor-ui-api/src/com/intellij/ide/ui/UISettings.java +++ b/platform/editor-ui-api/src/com/intellij/ide/ui/UISettings.java @@ -16,6 +16,7 @@ package com.intellij.ide.ui; import com.intellij.ide.IdeBundle; +import com.intellij.ide.WelcomeWizardUtil; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; @@ -128,6 +129,9 @@ public class UISettings extends SimpleModificationTracker implements PersistentS public UISettings() { tweakPlatformDefaults(); setSystemFontFaceAndSize(); + + Boolean scrollToSource = WelcomeWizardUtil.getAutoScrollToSource(); + if (scrollToSource != null) DEFAULT_AUTOSCROLL_TO_SOURCE = scrollToSource; } private void tweakPlatformDefaults() { diff --git a/platform/icons/src/general/searchEverywhereGear.png b/platform/icons/src/general/searchEverywhereGear.png new file mode 100644 index 000000000000..8500a59800e7 Binary files /dev/null and b/platform/icons/src/general/searchEverywhereGear.png differ diff --git a/platform/icons/src/general/searchEverywhereGear@2x.png b/platform/icons/src/general/searchEverywhereGear@2x.png new file mode 100644 index 000000000000..bbc8c4eaf04b Binary files /dev/null and b/platform/icons/src/general/searchEverywhereGear@2x.png differ diff --git a/platform/icons/src/general/searchEverywhereGear@2x_dark.png b/platform/icons/src/general/searchEverywhereGear@2x_dark.png new file mode 100644 index 000000000000..a3658d816448 Binary files /dev/null and b/platform/icons/src/general/searchEverywhereGear@2x_dark.png differ diff --git a/platform/icons/src/general/searchEverywhereGear_dark.png b/platform/icons/src/general/searchEverywhereGear_dark.png new file mode 100644 index 000000000000..d5848c477552 Binary files /dev/null and b/platform/icons/src/general/searchEverywhereGear_dark.png differ diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/std/StdArrangementTokens.java b/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/std/StdArrangementTokens.java index 764f1bcea51f..a5753c8375bb 100644 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/std/StdArrangementTokens.java +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/std/StdArrangementTokens.java @@ -164,6 +164,7 @@ public class StdArrangementTokens { @NotNull public static final ArrangementSettingsToken PROPERTY = invertible("PROPERTY", StdArrangementTokenType.ENTRY_TYPE); @NotNull public static final ArrangementSettingsToken EVENT_HANDLER = invertible("EVENT_HANDLER", StdArrangementTokenType.ENTRY_TYPE); @NotNull public static final ArrangementSettingsToken STATIC_INIT = invertible("STATIC_INIT", StdArrangementTokenType.ENTRY_TYPE); + @NotNull public static final ArrangementSettingsToken INIT_BLOCK = invertible("INITIALIZER BLOCK", StdArrangementTokenType.ENTRY_TYPE); @NotNull public static final ArrangementSettingsToken NAMESPACE = invertible("NAMESPACE", StdArrangementTokenType.ENTRY_TYPE); @NotNull public static final ArrangementSettingsToken TRAIT = invertible("TRAIT", StdArrangementTokenType.ENTRY_TYPE); diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ex/Descriptor.java b/platform/lang-impl/src/com/intellij/codeInspection/ex/Descriptor.java index b27befd36b00..322a273830e5 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ex/Descriptor.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ex/Descriptor.java @@ -45,6 +45,7 @@ public class Descriptor { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.ex.Descriptor"); private final ScopeToolState myState; private final InspectionProfileImpl myInspectionProfile; + private final String myScopeName; public Descriptor(@NotNull ScopeToolState state, @NotNull InspectionProfileImpl inspectionProfile, @NotNull Project project) { myState = state; @@ -54,6 +55,7 @@ public class Descriptor { final String[] groupPath = tool.getGroupPath(); myGroup = groupPath.length == 0 ? new String[]{InspectionProfileEntry.GENERAL_GROUP_NAME} : groupPath; myKey = HighlightDisplayKey.find(tool.getShortName()); + myScopeName = state.getScopeName(); myScope = state.getScope(project); myLevel = inspectionProfile.getErrorLevel(myKey, myScope, project); myEnabled = inspectionProfile.isToolEnabled(myKey, myScope, project); @@ -137,6 +139,11 @@ public class Descriptor { return myGroup; } + @NotNull + public String getScopeName() { + return myScopeName; + } + @Nullable public NamedScope getScope() { return myScope; diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ex/VisibleTreeState.java b/platform/lang-impl/src/com/intellij/codeInspection/ex/VisibleTreeState.java index d81fbf692812..1dc2f40f5432 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ex/VisibleTreeState.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ex/VisibleTreeState.java @@ -176,11 +176,9 @@ public class VisibleTreeState{ final State other = (State)o; if (myKey.equals(other.myKey)) { if (myDescriptor != null && other.myDescriptor != null) { - final NamedScope scope1 = myDescriptor.getScope(); - final NamedScope scope2 = other.myDescriptor.getScope(); - if (scope1 != null && scope2 != null) { - return scope1.getName().compareTo(scope2.getName()); - } + final String scope1 = myDescriptor.getScopeName(); + final String scope2 = other.myDescriptor.getScopeName(); + return scope1.compareTo(scope2); } } return myKey.compareTo(other.myKey); diff --git a/platform/lang-impl/src/com/intellij/execution/ui/RunContentManagerImpl.java b/platform/lang-impl/src/com/intellij/execution/ui/RunContentManagerImpl.java index c9dae095522c..b7f56a69f887 100644 --- a/platform/lang-impl/src/com/intellij/execution/ui/RunContentManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/ui/RunContentManagerImpl.java @@ -38,6 +38,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.project.ProjectManagerListener; import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.IconLoader; import com.intellij.openapi.util.Key; @@ -421,7 +422,11 @@ public class RunContentManagerImpl implements RunContentManager, Disposable { } //Stage two: try to get content from descriptor itself final Content attachedContent = descriptor.getAttachedContent(); - if (attachedContent != null && attachedContent.isValid() && contentManager.getIndexOfContent(attachedContent) != -1) { + + if (attachedContent != null + && attachedContent.isValid() + && contentManager.getIndexOfContent(attachedContent) != -1 + && (Comparing.equal(descriptor.getDisplayName(), attachedContent.getDisplayName()) || !attachedContent.isPinned())) { content = attachedContent; } } 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 93a33c6ce5ee..c5a1db15c2e4 100644 --- a/platform/lang-impl/src/com/intellij/ide/actions/SearchEverywhereAction.java +++ b/platform/lang-impl/src/com/intellij/ide/actions/SearchEverywhereAction.java @@ -716,7 +716,7 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA topPanel.add(title, BorderLayout.WEST); final JPanel controls = new JPanel(new BorderLayout()); controls.setOpaque(false); - final JLabel settings = new JLabel(AllIcons.General.WebSettings); + final JLabel settings = new JLabel(AllIcons.General.SearchEverywhereGear); new ClickListener(){ @Override public boolean onClick(@NotNull MouseEvent event, int clickCount) { diff --git a/platform/lang-impl/src/com/intellij/ide/hierarchy/HierarchyBrowserManager.java b/platform/lang-impl/src/com/intellij/ide/hierarchy/HierarchyBrowserManager.java index c0f156fedd28..595907aa5720 100644 --- a/platform/lang-impl/src/com/intellij/ide/hierarchy/HierarchyBrowserManager.java +++ b/platform/lang-impl/src/com/intellij/ide/hierarchy/HierarchyBrowserManager.java @@ -18,12 +18,15 @@ package com.intellij.ide.hierarchy; import com.intellij.icons.AllIcons; import com.intellij.ide.impl.ContentManagerWatcher; +import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.components.*; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.SimpleToolWindowPanel; import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowAnchor; import com.intellij.openapi.wm.ToolWindowId; import com.intellij.openapi.wm.ToolWindowManager; +import com.intellij.openapi.wm.ex.ToolWindowEx; import com.intellij.ui.content.ContentManager; @State( @@ -46,11 +49,15 @@ public final class HierarchyBrowserManager implements PersistentStateComponent implements Navig @Override public Comparable getTypeSortKey() { VirtualFile file = getVirtualFile(); - if (file == null) return null; - String extension = file.getExtension(); - return extension == null ? null : new PsiFileNode.ExtensionSortKey(extension); + if (file != null) { + String extension = file.getExtension(); + if (extension != null) { + return new PsiFileNode.ExtensionSortKey(extension); + } + } + return null; } @Override diff --git a/platform/lang-impl/src/com/intellij/ide/structureView/impl/StructureViewFactoryImpl.java b/platform/lang-impl/src/com/intellij/ide/structureView/impl/StructureViewFactoryImpl.java index 7d7990a06a41..b350d1f330c9 100644 --- a/platform/lang-impl/src/com/intellij/ide/structureView/impl/StructureViewFactoryImpl.java +++ b/platform/lang-impl/src/com/intellij/ide/structureView/impl/StructureViewFactoryImpl.java @@ -54,7 +54,6 @@ public final class StructureViewFactoryImpl extends StructureViewFactoryEx imple @SuppressWarnings({"WeakerAccess"}) public boolean AUTOSCROLL_MODE = true; @SuppressWarnings({"WeakerAccess"}) public boolean AUTOSCROLL_FROM_SOURCE = false; @SuppressWarnings({"WeakerAccess"}) public String ACTIVE_ACTIONS = ""; - public boolean SHOW_TOOLBAR = false; } private final Project myProject; diff --git a/platform/lang-impl/src/com/intellij/ide/structureView/newStructureView/StructureViewComponent.java b/platform/lang-impl/src/com/intellij/ide/structureView/newStructureView/StructureViewComponent.java index 6363a299539e..3e9eabbfbaa7 100644 --- a/platform/lang-impl/src/com/intellij/ide/structureView/newStructureView/StructureViewComponent.java +++ b/platform/lang-impl/src/com/intellij/ide/structureView/newStructureView/StructureViewComponent.java @@ -154,9 +154,7 @@ public class StructureViewComponent extends SimpleToolWindowPanel implements Tre myAutoScrollToSourceHandler = new MyAutoScrollToSourceHandler(); myAutoScrollFromSourceHandler = new MyAutoScrollFromSourceHandler(myProject, this); - if (getSettings().SHOW_TOOLBAR) { - setToolbar(createToolbar()); - } + setToolbar(createToolbar()); installTree(); @@ -390,25 +388,7 @@ public class StructureViewComponent extends SimpleToolWindowPanel implements Tre } public ActionGroup getGearActions() { - DefaultActionGroup group = createActionGroup(true); - group.addAction(new ToggleAction("Show Toolbar") { - @Override - public boolean isDumbAware() { - return true; - } - - @Override - public boolean isSelected(AnActionEvent e) { - return getSettings().SHOW_TOOLBAR; - } - - @Override - public void setSelected(AnActionEvent e, boolean state) { - setToolbar(state ? createToolbar() : null); - getSettings().SHOW_TOOLBAR = state; - } - }).setAsSecondary(true); - return group; + return createActionGroup(true); } private StructureViewFactoryImpl.State getSettings() { diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/SingleInspectionProfilePanel.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/SingleInspectionProfilePanel.java index 28118dd382d9..5c5f8c745c73 100644 --- a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/SingleInspectionProfilePanel.java +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/SingleInspectionProfilePanel.java @@ -241,9 +241,8 @@ public class SingleInspectionProfilePanel extends JPanel { } else { for (ScopeToolState candidate : mySelectedProfile .getNonDefaultTools(descriptor.getKey().toString(), myProjectProfileManager.getProject())) { - final NamedScope scope = descriptor.getScope(); - LOG.assertTrue(scope != null); - if (Comparing.equal(candidate.getScopeName(), scope.getName())) { + final String scope = descriptor.getScopeName(); + if (Comparing.equal(candidate.getScopeName(), scope)) { state = candidate; break; } diff --git a/platform/lang-impl/src/com/intellij/usageView/impl/UsageViewManagerImpl.java b/platform/lang-impl/src/com/intellij/usageView/impl/UsageViewManagerImpl.java index b2ee89c11e2e..bf76c18e94d5 100644 --- a/platform/lang-impl/src/com/intellij/usageView/impl/UsageViewManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/usageView/impl/UsageViewManagerImpl.java @@ -18,12 +18,15 @@ package com.intellij.usageView.impl; import com.intellij.icons.AllIcons; import com.intellij.ide.impl.ContentManagerWatcher; +import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.SimpleToolWindowPanel; import com.intellij.openapi.util.Key; import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowAnchor; import com.intellij.openapi.wm.ToolWindowId; import com.intellij.openapi.wm.ToolWindowManager; +import com.intellij.openapi.wm.ex.ToolWindowEx; import com.intellij.ui.content.*; import com.intellij.usageView.UsageViewManager; import com.intellij.usages.UsageView; @@ -41,6 +44,10 @@ public class UsageViewManagerImpl extends UsageViewManager { ToolWindow toolWindow = toolWindowManager.registerToolWindow(ToolWindowId.FIND, true, ToolWindowAnchor.BOTTOM, project, true); toolWindow.setToHideOnEmptyContent(true); toolWindow.setIcon(AllIcons.Toolwindows.ToolWindowFind); + DefaultActionGroup gearActions = new DefaultActionGroup(); + gearActions.addAction(SimpleToolWindowPanel.createToggleToolbarAction(project, toolWindow)).setAsSecondary(true); + ((ToolWindowEx)toolWindow).setAdditionalGearActions(gearActions); + myFindContentManager = toolWindow.getContentManager(); myFindContentManager.addContentManagerListener(new ContentManagerAdapter() { @Override diff --git a/platform/platform-api/src/com/intellij/openapi/ui/SimpleToolWindowPanel.java b/platform/platform-api/src/com/intellij/openapi/ui/SimpleToolWindowPanel.java index 751d41eab358..c4523819c0f1 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/SimpleToolWindowPanel.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/SimpleToolWindowPanel.java @@ -15,20 +15,28 @@ */ package com.intellij.openapi.ui; -import com.intellij.openapi.actionSystem.ActionToolbar; -import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.actionSystem.DataProvider; +import com.intellij.ide.util.PropertiesComponent; +import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Ref; +import com.intellij.openapi.wm.ToolWindow; +import com.intellij.ui.content.Content; +import com.intellij.ui.content.ContentManagerAdapter; +import com.intellij.ui.content.ContentManagerEvent; import com.intellij.ui.switcher.QuickActionProvider; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.AwtVisitor; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; import java.awt.event.ContainerAdapter; import java.awt.event.ContainerEvent; +import java.util.LinkedList; import java.util.List; public class SimpleToolWindowPanel extends JPanel implements QuickActionProvider, DataProvider { @@ -74,6 +82,10 @@ public class SimpleToolWindowPanel extends JPanel implements QuickActionProvider }); } + public boolean isToolbarVisible() { + return myToolbar != null && myToolbar.isVisible(); + } + public void setToolbar(@Nullable JComponent c) { if (c == null) { remove(myToolbar); @@ -159,4 +171,67 @@ public class SimpleToolWindowPanel extends JPanel implements QuickActionProvider } } } + + @NotNull + public static AnAction createToggleToolbarAction(Project project, @NotNull ToolWindow toolWindow) { + return new ToggleToolbarAction(toolWindow, PropertiesComponent.getInstance(project)); + } + + private static class ToggleToolbarAction extends ToggleAction implements DumbAware { + + private final PropertiesComponent myPropertiesComponent; + private final ToolWindow myToolWindow; + + private ToggleToolbarAction(@NotNull ToolWindow toolWindow, @NotNull PropertiesComponent propertiesComponent) { + super("Show Toolbar"); + myPropertiesComponent = propertiesComponent; + myToolWindow = toolWindow; + myToolWindow.getContentManager().addContentManagerListener(new ContentManagerAdapter() { + @Override + public void contentAdded(ContentManagerEvent event) { + setContentToolbarVisible(event.getContent(), getVisibilityValue()); + } + }); + } + + @Override + public boolean isSelected(AnActionEvent e) { + return getVisibilityValue(); + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + myPropertiesComponent.setValue(getProperty(), String.valueOf(state), String.valueOf(true)); + for (Content content : myToolWindow.getContentManager().getContents()) { + setContentToolbarVisible(content, state); + } + } + + @NotNull + private String getProperty() { + return myToolWindow.getStripeTitle() + ".ShowToolbar"; + } + + private boolean getVisibilityValue() { + return myPropertiesComponent.getBoolean(getProperty(), true); + } + + private static void setContentToolbarVisible(Content content, boolean state) { + LinkedList deque = ContainerUtil.newLinkedList(content.getComponent()); + while(!deque.isEmpty()) { + JComponent component = deque.pollFirst(); + if (!(component instanceof JPanel)) continue; + + for (int i = 0, count = component.getComponentCount(); i < count; i++) { + Component c = component.getComponent(i); + if (c instanceof ActionToolbar) { + c.setVisible(state); + } + else if (c instanceof JPanel) { + deque.addLast((JPanel)c); + } + } + } + } + } } \ No newline at end of file diff --git a/platform/platform-api/src/com/intellij/util/net/HttpProxySettingsUi.form b/platform/platform-api/src/com/intellij/util/net/HttpProxySettingsUi.form index 3d2ae0575e1f..70561034a3ea 100644 --- a/platform/platform-api/src/com/intellij/util/net/HttpProxySettingsUi.form +++ b/platform/platform-api/src/com/intellij/util/net/HttpProxySettingsUi.form @@ -1,58 +1,30 @@
- + - + - - + + - + - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - + @@ -62,15 +34,67 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -79,67 +103,39 @@ - + - + - + - - - - - - - - - - - - - + - + - - - - - - - - - - - - - - - - - + - + @@ -148,23 +144,25 @@ - + - + - + + + - + @@ -173,14 +171,14 @@ - + - + @@ -188,7 +186,7 @@ - + @@ -198,7 +196,7 @@ - + @@ -206,22 +204,24 @@ - + - + - + - + + + - + - + @@ -234,14 +234,6 @@ - - - - - - - - @@ -252,6 +244,14 @@ + + + + + + + +
diff --git a/platform/platform-api/src/com/intellij/util/net/HttpProxySettingsUi.java b/platform/platform-api/src/com/intellij/util/net/HttpProxySettingsUi.java index e71d8e5af4ec..f4c849f015f7 100644 --- a/platform/platform-api/src/com/intellij/util/net/HttpProxySettingsUi.java +++ b/platform/platform-api/src/com/intellij/util/net/HttpProxySettingsUi.java @@ -21,17 +21,16 @@ import com.google.common.net.InternetDomainName; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.options.ConfigurableUi; import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.ui.MultiLineLabelUI; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.openapi.wm.IdeFrame; import com.intellij.ui.PortField; -import com.intellij.ui.components.JBLabel; +import com.intellij.ui.RawCommandLineEditor; +import com.intellij.ui.RelativeFont; import com.intellij.ui.components.JBRadioButton; import com.intellij.util.proxy.CommonProxy; import com.intellij.util.proxy.JavaProxyProperty; -import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -59,16 +58,16 @@ class HttpProxySettingsUi implements ConfigurableUi { private JLabel myPortNumberLabel; private JBRadioButton myAutoDetectProxyRb; private JBRadioButton myUseHTTPProxyRb; - private JBLabel mySystemProxyDefined; + private JLabel mySystemProxyDefined; private JBRadioButton myNoProxyRb; private JBRadioButton myHTTP; private JBRadioButton mySocks; private JButton myClearPasswordsButton; private JLabel myErrorLabel; private JButton myCheckButton; - private JBLabel myOtherWarning; + private JLabel myOtherWarning; private JLabel myProxyExceptionsLabel; - private JTextArea myProxyExceptions; + private RawCommandLineEditor myProxyExceptions; private JLabel myNoProxyForLabel; private JCheckBox myPacUrlCheckBox; private JTextField myPacUrlTextField; @@ -106,14 +105,11 @@ class HttpProxySettingsUi implements ConfigurableUi { proxyTypeGroup.add(mySocks); myHTTP.setSelected(true); - myProxyExceptions.setBorder(UIUtil.getTextFieldBorder()); - Boolean property = Boolean.getBoolean(JavaProxyProperty.USE_SYSTEM_PROXY); mySystemProxyDefined.setVisible(Boolean.TRUE.equals(property)); if (Boolean.TRUE.equals(property)) { mySystemProxyDefined.setIcon(Messages.getWarningIcon()); - mySystemProxyDefined.setFont(mySystemProxyDefined.getFont().deriveFont(Font.BOLD)); - mySystemProxyDefined.setUI(new MultiLineLabelUI()); + RelativeFont.BOLD.install(mySystemProxyDefined); } myProxyAuthCheckBox.addActionListener(new ActionListener() { @@ -270,7 +266,6 @@ class HttpProxySettingsUi implements ConfigurableUi { myOtherWarning.setVisible(oldStyleText != null); if (oldStyleText != null) { myOtherWarning.setText(oldStyleText); - myOtherWarning.setUI(new MultiLineLabelUI()); myOtherWarning.setIcon(Messages.getWarningIcon()); } } @@ -354,7 +349,6 @@ class HttpProxySettingsUi implements ConfigurableUi { mySocks.setEnabled(enabled); myHTTP.setEnabled(enabled); myProxyExceptions.setEnabled(enabled); - myProxyExceptions.setBackground(myProxyPortTextField.getBackground()); myProxyExceptionsLabel.setEnabled(enabled); myNoProxyForLabel.setEnabled(enabled); diff --git a/platform/platform-impl/src/com/intellij/ide/actions/ToggleDistractionFreeModeAction.java b/platform/platform-impl/src/com/intellij/ide/actions/ToggleDistractionFreeModeAction.java index 4afd28d9f893..196d36ea5a6b 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/ToggleDistractionFreeModeAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/ToggleDistractionFreeModeAction.java @@ -15,18 +15,33 @@ */ package com.intellij.ide.actions; +import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; +import com.intellij.codeInsight.daemon.DaemonCodeAnalyzerSettings; +import com.intellij.ide.ui.LafManager; +import com.intellij.ide.ui.UISettings; +import com.intellij.ide.util.PropertiesComponent; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.editor.EditorFactory; +import com.intellij.openapi.editor.ex.EditorSettingsExternalizable; +import com.intellij.openapi.editor.ex.util.EditorUtil; import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.project.Project; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.registry.RegistryValue; import org.jetbrains.annotations.NotNull; +import javax.swing.*; + public class ToggleDistractionFreeModeAction extends AnAction implements DumbAware { private static final String key = "editor.distraction.free.mode"; + @Override public void update(@NotNull AnActionEvent e) { + if (e.getProject() == null) { + e.getPresentation().setEnabled(false); + return; + } RegistryValue value = Registry.get(key); boolean selected = value.asBoolean(); e.getPresentation().setText((selected ? "Exit" : "Enter") + " Distraction Free Mode"); @@ -34,8 +49,58 @@ public class ToggleDistractionFreeModeAction extends AnAction implements DumbAwa @Override public void actionPerformed(@NotNull AnActionEvent e) { + Project project = e.getProject(); RegistryValue value = Registry.get(key); - value.setValue(!value.asBoolean()); + boolean v = !value.asBoolean(); + value.setValue(v); + + if (project == null) return; + + PropertiesComponent p = PropertiesComponent.getInstance(project); + UISettings ui = UISettings.getInstance(); + EditorSettingsExternalizable.OptionSet eo = EditorSettingsExternalizable.getInstance().getOptions(); + DaemonCodeAnalyzerSettings ds = DaemonCodeAnalyzerSettings.getInstance(); + + String before = "BEFORE.DISTRACTION.MODE."; + if (v) { + p.setValue(before + "SHOW_STATUS_BAR", String.valueOf(ui.SHOW_STATUS_BAR)); ui.SHOW_STATUS_BAR = false; + p.setValue(before + "SHOW_MAIN_TOOLBAR", String.valueOf(ui.SHOW_MAIN_TOOLBAR)); ui.SHOW_MAIN_TOOLBAR = false; + p.setValue(before + "SHOW_NAVIGATION_BAR", String.valueOf(ui.SHOW_NAVIGATION_BAR)); ui.SHOW_NAVIGATION_BAR = false; + p.setValue(before + "HIDE_TOOL_STRIPES", String.valueOf(ui.HIDE_TOOL_STRIPES)); ui.HIDE_TOOL_STRIPES = true; + p.setValue(before + "EDITOR_TAB_PLACEMENT", String.valueOf(ui.EDITOR_TAB_PLACEMENT)); ui.EDITOR_TAB_PLACEMENT = UISettings.TABS_NONE; + + p.setValue(before + "IS_FOLDING_OUTLINE_SHOWN", String.valueOf(eo.IS_FOLDING_OUTLINE_SHOWN)); eo.IS_FOLDING_OUTLINE_SHOWN = false; + p.setValue(before + "IS_WHITESPACES_SHOWN", String.valueOf(eo.IS_WHITESPACES_SHOWN)); eo.IS_WHITESPACES_SHOWN = false; + p.setValue(before + "ARE_LINE_NUMBERS_SHOWN", String.valueOf(eo.ARE_LINE_NUMBERS_SHOWN)); eo.ARE_LINE_NUMBERS_SHOWN = false; + //p.setValue(before + "IS_RIGHT_MARGIN_SHOWN", String.valueOf(eo.IS_RIGHT_MARGIN_SHOWN)); eo.IS_RIGHT_MARGIN_SHOWN = false; + p.setValue(before + "IS_INDENT_GUIDES_SHOWN", String.valueOf(eo.IS_INDENT_GUIDES_SHOWN)); eo.IS_INDENT_GUIDES_SHOWN = false; + + p.setValue(before + "SHOW_METHOD_SEPARATORS", String.valueOf(ds.SHOW_METHOD_SEPARATORS)); ds.SHOW_METHOD_SEPARATORS = false; + + TogglePresentationModeAction.storeToolWindows(project); + } + else { + ui.SHOW_STATUS_BAR = p.getBoolean(before + "SHOW_STATUS_BAR", true); + ui.SHOW_MAIN_TOOLBAR = p.getBoolean(before + "SHOW_MAIN_TOOLBAR", true); + ui.SHOW_NAVIGATION_BAR = p.getBoolean(before + "SHOW_NAVIGATION_BAR", true); + ui.HIDE_TOOL_STRIPES = p.getBoolean(before + "HIDE_TOOL_STRIPES", true); + ui.EDITOR_TAB_PLACEMENT = p.getOrInitInt(before + "EDITOR_TAB_PLACEMENT", SwingConstants.TOP); + + eo.IS_FOLDING_OUTLINE_SHOWN = p.getBoolean(before + "IS_FOLDING_OUTLINE_SHOWN", true); + eo.IS_WHITESPACES_SHOWN = p.getBoolean(before + "IS_WHITESPACES_SHOWN", false); + eo.ARE_LINE_NUMBERS_SHOWN = p.getBoolean(before + "ARE_LINE_NUMBERS_SHOWN", false); + //eo.IS_RIGHT_MARGIN_SHOWN = p.getBoolean(before + "IS_RIGHT_MARGIN_SHOWN", true); + eo.IS_INDENT_GUIDES_SHOWN = p.getBoolean(before + "IS_INDENT_GUIDES_SHOWN", false); + + ds.SHOW_METHOD_SEPARATORS = p.getBoolean(before + "SHOW_METHOD_SEPARATORS", false); + + TogglePresentationModeAction.restoreToolWindows(project, true, false); + } + + UISettings.getInstance().fireUISettingsChanged(); + LafManager.getInstance().updateUI(); + EditorUtil.reinitSettings(); + DaemonCodeAnalyzer.getInstance(project).settingsChanged(); EditorFactory.getInstance().refreshAllEditors(); } } diff --git a/platform/platform-impl/src/com/intellij/ide/actions/TogglePresentationModeAction.java b/platform/platform-impl/src/com/intellij/ide/actions/TogglePresentationModeAction.java index 78b84652563c..16dae889b101 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/TogglePresentationModeAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/TogglePresentationModeAction.java @@ -180,7 +180,7 @@ public class TogglePresentationModeAction extends AnAction implements DumbAware return hasVisible; } - private static boolean storeToolWindows(@Nullable Project project) { + static boolean storeToolWindows(@Nullable Project project) { if (project == null) return false; ToolWindowManagerEx manager = ToolWindowManagerEx.getInstanceEx(project); @@ -195,7 +195,7 @@ public class TogglePresentationModeAction extends AnAction implements DumbAware return hasVisible; } - private static void restoreToolWindows(Project project, boolean needsRestore, boolean inPresentation) { + static void restoreToolWindows(Project project, boolean needsRestore, boolean inPresentation) { if (project == null || !needsRestore) return; ToolWindowManagerEx manager = ToolWindowManagerEx.getInstanceEx(project); DesktopLayout restoreLayout = manager.getLayoutToRestoreLater(); diff --git a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeFeaturedPluginsStepPanel.java b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeFeaturedPluginsStepPanel.java index 4371c3c991cf..61b3ac4ab20c 100644 --- a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeFeaturedPluginsStepPanel.java +++ b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeFeaturedPluginsStepPanel.java @@ -56,10 +56,7 @@ public class CustomizeFeaturedPluginsStepPanel extends AbstractCustomizeWizardSt public CustomizeFeaturedPluginsStepPanel(PluginGroups pluginGroups) throws OfflineException { setLayout(new GridLayout(1, 1)); JPanel gridPanel = new JPanel(new GridLayout(0, 3)); - JBScrollPane scrollPane = - new JBScrollPane(gridPanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); - scrollPane.getVerticalScrollBar().setUnitIncrement(10); - scrollPane.setBorder(null); + JBScrollPane scrollPane = CustomizePluginsStepPanel.createScrollPane(gridPanel); Map config = pluginGroups.getFeaturedPlugins(); boolean isEmptyOrOffline = true; diff --git a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeKeyboardSchemeStepPanel.java b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeKeyboardSchemeStepPanel.java index 53b215516c09..1b1779141ab6 100644 --- a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeKeyboardSchemeStepPanel.java +++ b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeKeyboardSchemeStepPanel.java @@ -16,7 +16,7 @@ package com.intellij.ide.customize; import com.intellij.CommonBundle; -import com.intellij.idea.StartupUtil; +import com.intellij.ide.WelcomeWizardUtil; import com.intellij.openapi.application.ApplicationNamesInfo; import com.intellij.openapi.keymap.KeymapManager; import com.intellij.openapi.ui.VerticalFlowLayout; @@ -37,7 +37,7 @@ public class CustomizeKeyboardSchemeStepPanel extends AbstractCustomizeWizardSte myMacPanel = createBigButtonPanel(new VerticalFlowLayout(), macRadioButton, new Runnable() { @Override public void run() { - StartupUtil.setMyWizardMacKeymap(KeymapManager.MAC_OS_X_10_5_PLUS_KEYMAP); + WelcomeWizardUtil.setWizardKeymap(KeymapManager.MAC_OS_X_10_5_PLUS_KEYMAP); } }); String style = "" + + "bar
public interface MyExtensionPoint
\n" + + " MyExtensionPoint JavaDoc.", + provider.generateDoc(docElement, null)); + } +} diff --git a/plugins/git4idea/src/git4idea/actions/GitPushAction.java b/plugins/git4idea/src/git4idea/actions/GitPushAction.java index 04e139fa8934..8340de21c560 100644 --- a/plugins/git4idea/src/git4idea/actions/GitPushAction.java +++ b/plugins/git4idea/src/git4idea/actions/GitPushAction.java @@ -24,7 +24,6 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.containers.ContainerUtil; import git4idea.GitUtil; -import git4idea.branch.GitBranchUtil; import git4idea.repo.GitRepository; import git4idea.repo.GitRepositoryManager; import org.jetbrains.annotations.NotNull; @@ -38,15 +37,15 @@ public class GitPushAction extends DumbAwareAction { @Override public void actionPerformed(@NotNull AnActionEvent e) { Project project = e.getRequiredData(CommonDataKeys.PROJECT); - Collection repositories = collectRepositories(project, e.getData(CommonDataKeys.VIRTUAL_FILE_ARRAY)); + Collection repositories = e.getData(CommonDataKeys.EDITOR) != null + ? ContainerUtil.emptyList() + : collectRepositories(project, e.getData(CommonDataKeys.VIRTUAL_FILE_ARRAY)); new VcsPushDialog(project, RepositoryUtil.sortRepositories(repositories)).show(); } @NotNull private static Collection collectRepositories(@NotNull Project project, @Nullable VirtualFile[] files) { - if (files == null) { - return Collections.singletonList(GitBranchUtil.getCurrentRepository(project)); - } + if (files == null) return Collections.emptyList(); GitRepositoryManager manager = GitUtil.getRepositoryManager(project); Collection repositories = ContainerUtil.newHashSet(); for (VirtualFile file : files) { diff --git a/plugins/git4idea/src/git4idea/push/GitPushTagPanel.java b/plugins/git4idea/src/git4idea/push/GitPushTagPanel.java index 21a86a63ac41..300d0b9d1a1a 100644 --- a/plugins/git4idea/src/git4idea/push/GitPushTagPanel.java +++ b/plugins/git4idea/src/git4idea/push/GitPushTagPanel.java @@ -28,12 +28,12 @@ import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -class GitPushTagPanel extends VcsPushOptionsPanel { +public class GitPushTagPanel extends VcsPushOptionsPanel { private final ComboBox myCombobox; private final JBCheckBox myCheckBox; - GitPushTagPanel(@Nullable GitPushTagMode defaultMode, boolean followTagsSupported) { + public GitPushTagPanel(@Nullable GitPushTagMode defaultMode, boolean followTagsSupported) { String checkboxText = "Push Tags"; if (followTagsSupported) { checkboxText += ": "; diff --git a/plugins/git4idea/src/git4idea/push/GitPushTarget.java b/plugins/git4idea/src/git4idea/push/GitPushTarget.java index 467c4e7448e4..4536973b9281 100644 --- a/plugins/git4idea/src/git4idea/push/GitPushTarget.java +++ b/plugins/git4idea/src/git4idea/push/GitPushTarget.java @@ -33,20 +33,20 @@ import java.util.Collection; import static git4idea.GitUtil.findRemoteBranch; -class GitPushTarget implements PushTarget { +public class GitPushTarget implements PushTarget { private static final Logger LOG = Logger.getInstance(GitPushTarget.class); @NotNull private final GitRemoteBranch myRemoteBranch; private final boolean myIsNewBranchCreated; - GitPushTarget(@NotNull GitRemoteBranch remoteBranch, boolean isNewBranchCreated) { + public GitPushTarget(@NotNull GitRemoteBranch remoteBranch, boolean isNewBranchCreated) { myRemoteBranch = remoteBranch; myIsNewBranchCreated = isNewBranchCreated; } @NotNull - GitRemoteBranch getBranch() { + public GitRemoteBranch getBranch() { return myRemoteBranch; } @@ -55,12 +55,12 @@ class GitPushTarget implements PushTarget { return isNewBranchCreated(); } - boolean isNewBranchCreated() { + public boolean isNewBranchCreated() { return myIsNewBranchCreated; } @NotNull - static GitPushTarget parse(@NotNull GitRepository repository, @Nullable String remoteName, @NotNull String branchName) throws + public static GitPushTarget parse(@NotNull GitRepository repository, @Nullable String remoteName, @NotNull String branchName) throws ParseException { if (remoteName == null) { throw new ParseException("No remotes defined", -1); diff --git a/plugins/git4idea/src/git4idea/push/GitPushTargetPanel.java b/plugins/git4idea/src/git4idea/push/GitPushTargetPanel.java index aa03697963c8..62ecbd670d4c 100644 --- a/plugins/git4idea/src/git4idea/push/GitPushTargetPanel.java +++ b/plugins/git4idea/src/git4idea/push/GitPushTargetPanel.java @@ -44,7 +44,7 @@ import java.text.ParseException; import java.util.Comparator; import java.util.List; -class GitPushTargetPanel extends PushTargetPanel { +public class GitPushTargetPanel extends PushTargetPanel { private static final Logger LOG = Logger.getInstance(GitPushTargetPanel.class); diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/provider/update/HgUpdateEnvironment.java b/plugins/hg4idea/src/org/zmlx/hg4idea/provider/update/HgUpdateEnvironment.java index 038c1c24121f..d4d40d791bed 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/provider/update/HgUpdateEnvironment.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/provider/update/HgUpdateEnvironment.java @@ -35,7 +35,7 @@ import java.util.List; public class HgUpdateEnvironment implements UpdateEnvironment { private final Project project; - private final HgUpdateConfigurationSettings updateConfiguration; + @NotNull private final HgUpdateConfigurationSettings updateConfiguration; public HgUpdateEnvironment(Project project) { this.project = project; @@ -89,7 +89,7 @@ public class HgUpdateEnvironment implements UpdateEnvironment { private final HgUpdateConfigurationSettings updateConfiguration; protected HgUpdateDialog updateDialog; - public UpdateConfigurable(HgUpdateConfigurationSettings updateConfiguration) { + public UpdateConfigurable(@NotNull HgUpdateConfigurationSettings updateConfiguration) { this.updateConfiguration = updateConfiguration; } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/push/HgPushSupport.java b/plugins/hg4idea/src/org/zmlx/hg4idea/push/HgPushSupport.java index b05cde7fbd9f..e5539ef7c57e 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/push/HgPushSupport.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/push/HgPushSupport.java @@ -15,14 +15,15 @@ */ package org.zmlx.hg4idea.push; +import com.intellij.dvcs.branch.DvcsSyncSettings; import com.intellij.dvcs.push.*; -import com.intellij.dvcs.repo.Repository; import com.intellij.dvcs.repo.RepositoryManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.AbstractVcs; import com.intellij.util.ObjectUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.zmlx.hg4idea.HgProjectSettings; import org.zmlx.hg4idea.HgVcs; import org.zmlx.hg4idea.repo.HgRepository; import org.zmlx.hg4idea.util.HgUtil; @@ -31,10 +32,12 @@ public class HgPushSupport extends PushSupport= FunctionExprent.getPrecedence(FunctionExprent.FUNCTION_CAST)) { @@ -890,12 +888,11 @@ public class ExprProcessor implements CodeConstants { } res.prepend("(" + getCastTypeName(leftType) + ")"); - ret = true; } buffer.append(res); - return ret; + return cast; } private static boolean isIntConstant(Exprent exprent) { diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java index 57711eee2bd0..b80b745c04e2 100644 --- a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java +++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java @@ -357,21 +357,23 @@ public class InvocationExprent extends Exprent { Set setAmbiguousParameters = getAmbiguousParameters(); - boolean firstpar = true; + boolean firstParameter = true; int start = isEnum ? 2 : 0; for (int i = start; i < lstParameters.size(); i++) { if (sigFields == null || sigFields.get(i) == null) { - if (!firstpar) { + if (!firstParameter) { buf.append(", "); } TextBuffer buff = new TextBuffer(); - ExprProcessor.getCastedExprent(lstParameters.get(i), descriptor.params[i], buff, indent, true, setAmbiguousParameters.contains(i), tracer); - + boolean ambiguous = setAmbiguousParameters.contains(i); + ExprProcessor.getCastedExprent(lstParameters.get(i), descriptor.params[i], buff, indent, true, ambiguous, tracer); buf.append(buff); - firstpar = false; + + firstParameter = false; } } + buf.append(")"); return buf; diff --git a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFXQuickfixTest.java b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFXQuickfixTest.java index b95b12497e7b..a99c0e4da48e 100644 --- a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFXQuickfixTest.java +++ b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFXQuickfixTest.java @@ -20,12 +20,16 @@ import com.intellij.openapi.application.PluginPathManager; import com.intellij.openapi.module.Module; import com.intellij.openapi.roots.ContentEntry; import com.intellij.openapi.roots.ModifiableRootModel; +import com.intellij.psi.xml.XmlFile; import com.intellij.testFramework.LightProjectDescriptor; import com.intellij.testFramework.PsiTestUtil; import com.intellij.testFramework.fixtures.DefaultLightProjectDescriptor; import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.javaFX.fxml.codeInsight.inspections.JavaFxUnresolvedFxIdReferenceInspection; +import org.jetbrains.plugins.javaFX.fxml.codeInsight.intentions.JavaFxInjectPageLanguageIntention; + +import java.util.Set; public class JavaFXQuickfixTest extends LightCodeInsightFixtureTestCase { public static final DefaultLightProjectDescriptor JAVA_FX_WITH_GROOVY_DESCRIPTOR = new DefaultLightProjectDescriptor() { @@ -66,7 +70,10 @@ public class JavaFXQuickfixTest extends LightCodeInsightFixtureTestCase { myFixture.configureByFile(getTestName(true) + ".fxml"); final IntentionAction intention = myFixture.findSingleIntention("Specify page language"); assertNotNull(intention); - myFixture.launchAction(intention); + Set languages = JavaFxInjectPageLanguageIntention.getAvailableLanguages(); + assertContainsElements(languages, "groovy"); + JavaFxInjectPageLanguageIntention languageIntention = (JavaFxInjectPageLanguageIntention)intention; + languageIntention.registerPageLanguage(getProject(), (XmlFile)myFixture.getFile(), "groovy"); myFixture.checkResultByFile(getTestName(true) + ".fxml", getTestName(true) + "_after.fxml", true); } diff --git a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/codeInsight/intentions/JavaFxInjectPageLanguageIntention.java b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/codeInsight/intentions/JavaFxInjectPageLanguageIntention.java index 0cbd61f765a9..dc62783d23fb 100644 --- a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/codeInsight/intentions/JavaFxInjectPageLanguageIntention.java +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/codeInsight/intentions/JavaFxInjectPageLanguageIntention.java @@ -52,7 +52,7 @@ import java.util.TreeSet; public class JavaFxInjectPageLanguageIntention extends PsiElementBaseIntentionAction { public static final Logger LOG = Logger.getInstance("#" + JavaFxInjectPageLanguageIntention.class.getName()); - private static Set getAvailableLanguages(Project project) { + public static Set getAvailableLanguages() { final List engineFactories = new ScriptEngineManager().getEngineFactories(); final Set availableNames = new TreeSet(); @@ -68,8 +68,8 @@ public class JavaFxInjectPageLanguageIntention extends PsiElementBaseIntentionAc if (!FileModificationService.getInstance().preparePsiElementsForWrite(element)) return; final XmlFile containingFile = (XmlFile)element.getContainingFile(); - final Set availableLanguages = getAvailableLanguages(project); - if (availableLanguages.size() == 1 || ApplicationManager.getApplication().isUnitTestMode()) { + final Set availableLanguages = getAvailableLanguages(); + if (availableLanguages.size() == 1) { registerPageLanguage(project, containingFile, availableLanguages.iterator().next()); } else { final JBList list = new JBList(availableLanguages); @@ -83,7 +83,7 @@ public class JavaFxInjectPageLanguageIntention extends PsiElementBaseIntentionAc } } - private void registerPageLanguage(final Project project, final XmlFile containingFile, final String languageName) { + public void registerPageLanguage(final Project project, final XmlFile containingFile, final String languageName) { new WriteCommandAction.Simple(project, getFamilyName()) { @Override protected void run() { @@ -110,7 +110,7 @@ public class JavaFxInjectPageLanguageIntention extends PsiElementBaseIntentionAc @Override public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) { - if (ContainerUtil.isEmpty(getAvailableLanguages(project))) { + if (ContainerUtil.isEmpty(getAvailableLanguages())) { return false; } setText(getFamilyName()); diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/trello/TrelloRepository.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/trello/TrelloRepository.java index eafe341d3989..8ea6b8618d71 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/trello/TrelloRepository.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/trello/TrelloRepository.java @@ -26,7 +26,6 @@ import com.intellij.tasks.TaskBundle; import com.intellij.tasks.TaskRepositoryType; import com.intellij.tasks.impl.BaseRepository; import com.intellij.tasks.impl.BaseRepositoryImpl; -import com.intellij.tasks.impl.TaskUtil; import com.intellij.tasks.impl.httpclient.ResponseUtil; import com.intellij.tasks.trello.model.TrelloBoard; import com.intellij.tasks.trello.model.TrelloCard; @@ -320,8 +319,6 @@ public final class TrelloRepository extends BaseRepositoryImpl { HttpClient client = getHttpClient(); client.executeMethod(method); String entityContent = ResponseUtil.getResponseContentAsString(method); - TaskUtil.prettyFormatJsonToLog(LOG, entityContent); - // LOG.debug("Response size: " + method.getResponseHeader("Content-Length").getValue() + " bytes"); if (method.getStatusCode() != HttpStatus.SC_OK) { Header header = method.getResponseHeader("Content-Type"); if (header != null && header.getValue().startsWith("text/plain")) { diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/trello/TrelloRepositoryEditor.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/trello/TrelloRepositoryEditor.java index 31584497f43a..3ad4b12de380 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/trello/TrelloRepositoryEditor.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/trello/TrelloRepositoryEditor.java @@ -43,7 +43,7 @@ import java.util.List; * @author Mikhail Golubev */ public class TrelloRepositoryEditor extends BaseRepositoryEditor { - private static final Logger LOG = Logger.getInstance("#com.intellij.tasks.trello.TrelloRepositoryEditor"); + private static final Logger LOG = Logger.getInstance(TrelloRepositoryEditor.class); private static final TrelloBoard UNSPECIFIED_BOARD = new TrelloBoard() { @NotNull @@ -83,9 +83,10 @@ public class TrelloRepositoryEditor extends BaseRepositoryEditor download() throws Exception { + LOG.debug("Updating information about boards available to user when repository setting are shown"); List boards = super.download(); if (myBoard == null) { return boards; @@ -157,6 +159,7 @@ public class TrelloRepositoryEditor extends BaseRepositoryEditor download() throws Exception { + LOG.debug("Updating information about lists of board " + myRepository.getCurrentBoard() + " when repository setting are shown"); List lists = super.download(); if (myList == null) { return lists; @@ -266,6 +269,7 @@ public class TrelloRepositoryEditor extends BaseRepositoryEditor boards) { myBoardComboBox.setModel(new DefaultComboBoxModel(boards.toArray())); @@ -296,6 +300,7 @@ public class TrelloRepositoryEditor extends BaseRepositoryEditor lists) { myListComboBox.setModel(new DefaultComboBoxModel(lists.toArray())); diff --git a/plugins/tasks/tasks-core/src/icons/TasksIcons.java b/plugins/tasks/tasks-core/src/icons/TasksIcons.java index 68a2e24608ea..0beca234c217 100644 --- a/plugins/tasks/tasks-core/src/icons/TasksIcons.java +++ b/plugins/tasks/tasks-core/src/icons/TasksIcons.java @@ -40,15 +40,15 @@ public class TasksIcons { public static final Icon Github = load("/icons/github.png"); // 16x16 public static final Icon Gitlab = load("/icons/gitlab.png"); // 16x16 public static final Icon Jira = load("/icons/jira.png"); // 16x16 - public static final Icon Lighthouse = load("/icons/lighthouse.gif"); // 16x16 + public static final Icon Lighthouse = load("/icons/lighthouse.png"); // 16x16 public static final Icon Mantis = load("/icons/mantis.png"); // 16x16 public static final Icon Other = load("/icons/other.png"); // 16x16 public static class Pivotal { - public static final Icon Bug = load("/icons/pivotal/bug.png"); // 14x14 - public static final Icon Chore = load("/icons/pivotal/chore.png"); // 14x14 - public static final Icon Feature = load("/icons/pivotal/feature.png"); // 14x14 - public static final Icon Release = load("/icons/pivotal/release.png"); // 14x14 + public static final Icon Bug = load("/icons/pivotal/bug.png"); // 16x16 + public static final Icon Chore = load("/icons/pivotal/chore.png"); // 16x16 + public static final Icon Feature = load("/icons/pivotal/feature.png"); // 16x16 + public static final Icon Release = load("/icons/pivotal/release.png"); // 16x16 } public static final Icon Pivotal = load("/icons/pivotal.png"); // 16x16 diff --git a/plugins/tasks/tasks-core/src/icons/asana.png b/plugins/tasks/tasks-core/src/icons/asana.png index 25cea9634766..2d2958fc7f73 100644 Binary files a/plugins/tasks/tasks-core/src/icons/asana.png and b/plugins/tasks/tasks-core/src/icons/asana.png differ diff --git a/plugins/tasks/tasks-core/src/icons/asana@2x.png b/plugins/tasks/tasks-core/src/icons/asana@2x.png new file mode 100644 index 000000000000..eafd61a24989 Binary files /dev/null and b/plugins/tasks/tasks-core/src/icons/asana@2x.png differ diff --git a/plugins/tasks/tasks-core/src/icons/assembla.png b/plugins/tasks/tasks-core/src/icons/assembla.png index 5198524088cb..3e570c9880aa 100644 Binary files a/plugins/tasks/tasks-core/src/icons/assembla.png and b/plugins/tasks/tasks-core/src/icons/assembla.png differ diff --git a/plugins/tasks/tasks-core/src/icons/assembla@2x.png b/plugins/tasks/tasks-core/src/icons/assembla@2x.png new file mode 100644 index 000000000000..9b2e1daa0276 Binary files /dev/null and b/plugins/tasks/tasks-core/src/icons/assembla@2x.png differ diff --git a/plugins/tasks/tasks-core/src/icons/autoMode.png b/plugins/tasks/tasks-core/src/icons/autoMode.png index 9355db4b2de6..8b4cf8c4b6cf 100644 Binary files a/plugins/tasks/tasks-core/src/icons/autoMode.png and b/plugins/tasks/tasks-core/src/icons/autoMode.png differ diff --git a/plugins/tasks/tasks-core/src/icons/autoMode@2x.png b/plugins/tasks/tasks-core/src/icons/autoMode@2x.png new file mode 100644 index 000000000000..572d3356fb60 Binary files /dev/null and b/plugins/tasks/tasks-core/src/icons/autoMode@2x.png differ diff --git a/plugins/tasks/tasks-core/src/icons/bug.png b/plugins/tasks/tasks-core/src/icons/bug.png index 9303d5a019dd..412b67006dc2 100644 Binary files a/plugins/tasks/tasks-core/src/icons/bug.png and b/plugins/tasks/tasks-core/src/icons/bug.png differ diff --git a/plugins/tasks/tasks-core/src/icons/bug@2x.png b/plugins/tasks/tasks-core/src/icons/bug@2x.png new file mode 100644 index 000000000000..2fc69150f98f Binary files /dev/null and b/plugins/tasks/tasks-core/src/icons/bug@2x.png differ diff --git a/plugins/tasks/tasks-core/src/icons/bug@2x_dark.png b/plugins/tasks/tasks-core/src/icons/bug@2x_dark.png new file mode 100644 index 000000000000..6d02836719e9 Binary files /dev/null and b/plugins/tasks/tasks-core/src/icons/bug@2x_dark.png differ diff --git a/plugins/tasks/tasks-core/src/icons/bug_dark.png b/plugins/tasks/tasks-core/src/icons/bug_dark.png new file mode 100644 index 000000000000..5926f1dd2b1b Binary files /dev/null and b/plugins/tasks/tasks-core/src/icons/bug_dark.png differ diff --git a/plugins/tasks/tasks-core/src/icons/bugzilla.png b/plugins/tasks/tasks-core/src/icons/bugzilla.png index b06da57f512e..7dc4666337d2 100644 Binary files a/plugins/tasks/tasks-core/src/icons/bugzilla.png and b/plugins/tasks/tasks-core/src/icons/bugzilla.png differ diff --git a/plugins/tasks/tasks-core/src/icons/bugzilla@2x.png b/plugins/tasks/tasks-core/src/icons/bugzilla@2x.png new file mode 100644 index 000000000000..d010d4fe95fa Binary files /dev/null and b/plugins/tasks/tasks-core/src/icons/bugzilla@2x.png differ diff --git a/plugins/tasks/tasks-core/src/icons/clock.png b/plugins/tasks/tasks-core/src/icons/clock.png index d9cbbfc508a1..701863c24cd4 100644 Binary files a/plugins/tasks/tasks-core/src/icons/clock.png and b/plugins/tasks/tasks-core/src/icons/clock.png differ diff --git a/plugins/tasks/tasks-core/src/icons/clock@2x.png b/plugins/tasks/tasks-core/src/icons/clock@2x.png new file mode 100644 index 000000000000..c2e69d55385e Binary files /dev/null and b/plugins/tasks/tasks-core/src/icons/clock@2x.png differ diff --git a/plugins/tasks/tasks-core/src/icons/exception.png b/plugins/tasks/tasks-core/src/icons/exception.png index 3edaf7ab9785..02ce6b16d348 100644 Binary files a/plugins/tasks/tasks-core/src/icons/exception.png and b/plugins/tasks/tasks-core/src/icons/exception.png differ diff --git a/plugins/tasks/tasks-core/src/icons/exception@2x.png b/plugins/tasks/tasks-core/src/icons/exception@2x.png new file mode 100644 index 000000000000..1d1607adb7f1 Binary files /dev/null and b/plugins/tasks/tasks-core/src/icons/exception@2x.png differ diff --git a/plugins/tasks/tasks-core/src/icons/feature.png b/plugins/tasks/tasks-core/src/icons/feature.png index 9051b6eadf38..26793add948f 100644 Binary files a/plugins/tasks/tasks-core/src/icons/feature.png and b/plugins/tasks/tasks-core/src/icons/feature.png differ diff --git a/plugins/tasks/tasks-core/src/icons/feature@2x.png b/plugins/tasks/tasks-core/src/icons/feature@2x.png new file mode 100644 index 000000000000..66f44bd2ad51 Binary files /dev/null and b/plugins/tasks/tasks-core/src/icons/feature@2x.png differ diff --git a/plugins/tasks/tasks-core/src/icons/fogbugz.png b/plugins/tasks/tasks-core/src/icons/fogbugz.png index 70c2d7ffef58..1b1f086720ae 100644 Binary files a/plugins/tasks/tasks-core/src/icons/fogbugz.png and b/plugins/tasks/tasks-core/src/icons/fogbugz.png differ diff --git a/plugins/tasks/tasks-core/src/icons/fogbugz@2x.png b/plugins/tasks/tasks-core/src/icons/fogbugz@2x.png new file mode 100644 index 000000000000..58ba46497423 Binary files /dev/null and b/plugins/tasks/tasks-core/src/icons/fogbugz@2x.png differ diff --git a/plugins/tasks/tasks-core/src/icons/github.png b/plugins/tasks/tasks-core/src/icons/github.png index b2f329a2756c..1cd6d256b56c 100644 Binary files a/plugins/tasks/tasks-core/src/icons/github.png and b/plugins/tasks/tasks-core/src/icons/github.png differ diff --git a/plugins/tasks/tasks-core/src/icons/github@2x.png b/plugins/tasks/tasks-core/src/icons/github@2x.png new file mode 100644 index 000000000000..1f02f43021ee Binary files /dev/null and b/plugins/tasks/tasks-core/src/icons/github@2x.png differ diff --git a/plugins/tasks/tasks-core/src/icons/github@2x_dark.png b/plugins/tasks/tasks-core/src/icons/github@2x_dark.png new file mode 100644 index 000000000000..6bc758ad325b Binary files /dev/null and b/plugins/tasks/tasks-core/src/icons/github@2x_dark.png differ diff --git a/plugins/tasks/tasks-core/src/icons/github_dark.png b/plugins/tasks/tasks-core/src/icons/github_dark.png new file mode 100644 index 000000000000..97224b46d1a7 Binary files /dev/null and b/plugins/tasks/tasks-core/src/icons/github_dark.png differ diff --git a/plugins/tasks/tasks-core/src/icons/gitlab.png b/plugins/tasks/tasks-core/src/icons/gitlab.png index 12a06e757436..b581196c21f8 100644 Binary files a/plugins/tasks/tasks-core/src/icons/gitlab.png and b/plugins/tasks/tasks-core/src/icons/gitlab.png differ diff --git a/plugins/tasks/tasks-core/src/icons/gitlab@2x.png b/plugins/tasks/tasks-core/src/icons/gitlab@2x.png new file mode 100644 index 000000000000..011d5d6bcc61 Binary files /dev/null and b/plugins/tasks/tasks-core/src/icons/gitlab@2x.png differ diff --git a/plugins/tasks/tasks-core/src/icons/jira.png b/plugins/tasks/tasks-core/src/icons/jira.png index 5375c427edc0..f6f1f416b09f 100644 Binary files a/plugins/tasks/tasks-core/src/icons/jira.png and b/plugins/tasks/tasks-core/src/icons/jira.png differ diff --git a/plugins/tasks/tasks-core/src/icons/jira@2x.png b/plugins/tasks/tasks-core/src/icons/jira@2x.png new file mode 100644 index 000000000000..3825b73930ff Binary files /dev/null and b/plugins/tasks/tasks-core/src/icons/jira@2x.png differ diff --git a/plugins/tasks/tasks-core/src/icons/jira@2x_dark.png b/plugins/tasks/tasks-core/src/icons/jira@2x_dark.png new file mode 100644 index 000000000000..33cda03b2b27 Binary files /dev/null and b/plugins/tasks/tasks-core/src/icons/jira@2x_dark.png differ diff --git a/plugins/tasks/tasks-core/src/icons/jira_dark.png b/plugins/tasks/tasks-core/src/icons/jira_dark.png new file mode 100644 index 000000000000..876771437d14 Binary files /dev/null and b/plugins/tasks/tasks-core/src/icons/jira_dark.png differ diff --git a/plugins/tasks/tasks-core/src/icons/lighthouse.gif b/plugins/tasks/tasks-core/src/icons/lighthouse.gif deleted file mode 100644 index e12b528031e6..000000000000 Binary files a/plugins/tasks/tasks-core/src/icons/lighthouse.gif and /dev/null differ diff --git a/plugins/tasks/tasks-core/src/icons/lighthouse.png b/plugins/tasks/tasks-core/src/icons/lighthouse.png new file mode 100644 index 000000000000..bb6dc364a52e Binary files /dev/null and b/plugins/tasks/tasks-core/src/icons/lighthouse.png differ diff --git a/plugins/tasks/tasks-core/src/icons/lighthouse@2x.png b/plugins/tasks/tasks-core/src/icons/lighthouse@2x.png new file mode 100644 index 000000000000..99a00441fa3a Binary files /dev/null and b/plugins/tasks/tasks-core/src/icons/lighthouse@2x.png differ diff --git a/plugins/tasks/tasks-core/src/icons/mantis.png b/plugins/tasks/tasks-core/src/icons/mantis.png index d180cfa545c7..7e783936ba3e 100644 Binary files a/plugins/tasks/tasks-core/src/icons/mantis.png and b/plugins/tasks/tasks-core/src/icons/mantis.png differ diff --git a/plugins/tasks/tasks-core/src/icons/mantis@2x.png b/plugins/tasks/tasks-core/src/icons/mantis@2x.png new file mode 100644 index 000000000000..211bd8bbcf83 Binary files /dev/null and b/plugins/tasks/tasks-core/src/icons/mantis@2x.png differ diff --git a/plugins/tasks/tasks-core/src/icons/other.png b/plugins/tasks/tasks-core/src/icons/other.png index fb5256180562..17988437e520 100644 Binary files a/plugins/tasks/tasks-core/src/icons/other.png and b/plugins/tasks/tasks-core/src/icons/other.png differ diff --git a/plugins/tasks/tasks-core/src/icons/other@2x.png b/plugins/tasks/tasks-core/src/icons/other@2x.png new file mode 100644 index 000000000000..8950bac515e8 Binary files /dev/null and b/plugins/tasks/tasks-core/src/icons/other@2x.png differ diff --git a/plugins/tasks/tasks-core/src/icons/pivotal.png b/plugins/tasks/tasks-core/src/icons/pivotal.png index 1ae1042485d4..7dcd1252a30a 100644 Binary files a/plugins/tasks/tasks-core/src/icons/pivotal.png and b/plugins/tasks/tasks-core/src/icons/pivotal.png differ diff --git a/plugins/tasks/tasks-core/src/icons/pivotal/bug.png b/plugins/tasks/tasks-core/src/icons/pivotal/bug.png index fb5bdeb8edcc..412b67006dc2 100644 Binary files a/plugins/tasks/tasks-core/src/icons/pivotal/bug.png and b/plugins/tasks/tasks-core/src/icons/pivotal/bug.png differ diff --git a/plugins/tasks/tasks-core/src/icons/pivotal/bug@2x.png b/plugins/tasks/tasks-core/src/icons/pivotal/bug@2x.png new file mode 100644 index 000000000000..2fc69150f98f Binary files /dev/null and b/plugins/tasks/tasks-core/src/icons/pivotal/bug@2x.png differ diff --git a/plugins/tasks/tasks-core/src/icons/pivotal/bug@2x_dark.png b/plugins/tasks/tasks-core/src/icons/pivotal/bug@2x_dark.png new file mode 100644 index 000000000000..6d02836719e9 Binary files /dev/null and b/plugins/tasks/tasks-core/src/icons/pivotal/bug@2x_dark.png differ diff --git a/plugins/tasks/tasks-core/src/icons/pivotal/bug_dark.png b/plugins/tasks/tasks-core/src/icons/pivotal/bug_dark.png new file mode 100644 index 000000000000..5926f1dd2b1b Binary files /dev/null and b/plugins/tasks/tasks-core/src/icons/pivotal/bug_dark.png differ diff --git a/plugins/tasks/tasks-core/src/icons/pivotal/chore.png b/plugins/tasks/tasks-core/src/icons/pivotal/chore.png index bfe2e63909e1..7479b84eb0df 100644 Binary files a/plugins/tasks/tasks-core/src/icons/pivotal/chore.png and b/plugins/tasks/tasks-core/src/icons/pivotal/chore.png differ diff --git a/plugins/tasks/tasks-core/src/icons/pivotal/chore@2x.png b/plugins/tasks/tasks-core/src/icons/pivotal/chore@2x.png new file mode 100644 index 000000000000..f4406c39f0a6 Binary files /dev/null and b/plugins/tasks/tasks-core/src/icons/pivotal/chore@2x.png differ diff --git a/plugins/tasks/tasks-core/src/icons/pivotal/feature.png b/plugins/tasks/tasks-core/src/icons/pivotal/feature.png index a68335907b15..26793add948f 100644 Binary files a/plugins/tasks/tasks-core/src/icons/pivotal/feature.png and b/plugins/tasks/tasks-core/src/icons/pivotal/feature.png differ diff --git a/plugins/tasks/tasks-core/src/icons/pivotal/feature@2x.png b/plugins/tasks/tasks-core/src/icons/pivotal/feature@2x.png new file mode 100644 index 000000000000..66f44bd2ad51 Binary files /dev/null and b/plugins/tasks/tasks-core/src/icons/pivotal/feature@2x.png differ diff --git a/plugins/tasks/tasks-core/src/icons/pivotal/release.png b/plugins/tasks/tasks-core/src/icons/pivotal/release.png index 10e71b0a54a3..fd41786c4d44 100644 Binary files a/plugins/tasks/tasks-core/src/icons/pivotal/release.png and b/plugins/tasks/tasks-core/src/icons/pivotal/release.png differ diff --git a/plugins/tasks/tasks-core/src/icons/pivotal/release@2x.png b/plugins/tasks/tasks-core/src/icons/pivotal/release@2x.png new file mode 100644 index 000000000000..2365649df2a4 Binary files /dev/null and b/plugins/tasks/tasks-core/src/icons/pivotal/release@2x.png differ diff --git a/plugins/tasks/tasks-core/src/icons/pivotal/release@2x_dark.png b/plugins/tasks/tasks-core/src/icons/pivotal/release@2x_dark.png new file mode 100644 index 000000000000..f14f527c048b Binary files /dev/null and b/plugins/tasks/tasks-core/src/icons/pivotal/release@2x_dark.png differ diff --git a/plugins/tasks/tasks-core/src/icons/pivotal/release_dark.png b/plugins/tasks/tasks-core/src/icons/pivotal/release_dark.png new file mode 100644 index 000000000000..d9e040fafe8b Binary files /dev/null and b/plugins/tasks/tasks-core/src/icons/pivotal/release_dark.png differ diff --git a/plugins/tasks/tasks-core/src/icons/pivotal@2x.png b/plugins/tasks/tasks-core/src/icons/pivotal@2x.png new file mode 100644 index 000000000000..f1afcaa3bcdb Binary files /dev/null and b/plugins/tasks/tasks-core/src/icons/pivotal@2x.png differ diff --git a/plugins/tasks/tasks-core/src/icons/redmine.png b/plugins/tasks/tasks-core/src/icons/redmine.png index 391f369cba6e..d07109f4cb53 100644 Binary files a/plugins/tasks/tasks-core/src/icons/redmine.png and b/plugins/tasks/tasks-core/src/icons/redmine.png differ diff --git a/plugins/tasks/tasks-core/src/icons/redmine@2x.png b/plugins/tasks/tasks-core/src/icons/redmine@2x.png new file mode 100644 index 000000000000..55ae4db6476c Binary files /dev/null and b/plugins/tasks/tasks-core/src/icons/redmine@2x.png differ diff --git a/plugins/tasks/tasks-core/src/icons/savedContext.png b/plugins/tasks/tasks-core/src/icons/savedContext.png index 84b5f8910735..4eecb92bb136 100644 Binary files a/plugins/tasks/tasks-core/src/icons/savedContext.png and b/plugins/tasks/tasks-core/src/icons/savedContext.png differ diff --git a/plugins/tasks/tasks-core/src/icons/savedContext@2x.png b/plugins/tasks/tasks-core/src/icons/savedContext@2x.png new file mode 100644 index 000000000000..1c03eaad1a36 Binary files /dev/null and b/plugins/tasks/tasks-core/src/icons/savedContext@2x.png differ diff --git a/plugins/tasks/tasks-core/src/icons/sprintly.png b/plugins/tasks/tasks-core/src/icons/sprintly.png index 4e1fd1d746b2..c95cc07e904c 100644 Binary files a/plugins/tasks/tasks-core/src/icons/sprintly.png and b/plugins/tasks/tasks-core/src/icons/sprintly.png differ diff --git a/plugins/tasks/tasks-core/src/icons/sprintly@2x.png b/plugins/tasks/tasks-core/src/icons/sprintly@2x.png new file mode 100644 index 000000000000..abfbc600a474 Binary files /dev/null and b/plugins/tasks/tasks-core/src/icons/sprintly@2x.png differ diff --git a/plugins/tasks/tasks-core/src/icons/startTimer.png b/plugins/tasks/tasks-core/src/icons/startTimer.png index d600e7979be6..2d68e012c82e 100644 Binary files a/plugins/tasks/tasks-core/src/icons/startTimer.png and b/plugins/tasks/tasks-core/src/icons/startTimer.png differ diff --git a/plugins/tasks/tasks-core/src/icons/startTimer@2x.png b/plugins/tasks/tasks-core/src/icons/startTimer@2x.png new file mode 100644 index 000000000000..c3bf2479288f Binary files /dev/null and b/plugins/tasks/tasks-core/src/icons/startTimer@2x.png differ diff --git a/plugins/tasks/tasks-core/src/icons/stopTimer.png b/plugins/tasks/tasks-core/src/icons/stopTimer.png index dfeccbaf4b82..54502704d26b 100644 Binary files a/plugins/tasks/tasks-core/src/icons/stopTimer.png and b/plugins/tasks/tasks-core/src/icons/stopTimer.png differ diff --git a/plugins/tasks/tasks-core/src/icons/stopTimer@2x.png b/plugins/tasks/tasks-core/src/icons/stopTimer@2x.png new file mode 100644 index 000000000000..aa3486c09722 Binary files /dev/null and b/plugins/tasks/tasks-core/src/icons/stopTimer@2x.png differ diff --git a/plugins/tasks/tasks-core/src/icons/trac.png b/plugins/tasks/tasks-core/src/icons/trac.png index 2275f30a11d0..dc10451e2dbc 100644 Binary files a/plugins/tasks/tasks-core/src/icons/trac.png and b/plugins/tasks/tasks-core/src/icons/trac.png differ diff --git a/plugins/tasks/tasks-core/src/icons/trac@2x.png b/plugins/tasks/tasks-core/src/icons/trac@2x.png new file mode 100644 index 000000000000..c5c5532a7e93 Binary files /dev/null and b/plugins/tasks/tasks-core/src/icons/trac@2x.png differ diff --git a/plugins/tasks/tasks-core/src/icons/trello.png b/plugins/tasks/tasks-core/src/icons/trello.png index 86d6c6ea5e45..95806c6527fe 100644 Binary files a/plugins/tasks/tasks-core/src/icons/trello.png and b/plugins/tasks/tasks-core/src/icons/trello.png differ diff --git a/plugins/tasks/tasks-core/src/icons/trello@2x.png b/plugins/tasks/tasks-core/src/icons/trello@2x.png new file mode 100644 index 000000000000..e27ba0af71fd Binary files /dev/null and b/plugins/tasks/tasks-core/src/icons/trello@2x.png differ diff --git a/plugins/tasks/tasks-core/src/icons/unknown.png b/plugins/tasks/tasks-core/src/icons/unknown.png index 609fd60936e6..04c30c9facae 100644 Binary files a/plugins/tasks/tasks-core/src/icons/unknown.png and b/plugins/tasks/tasks-core/src/icons/unknown.png differ diff --git a/plugins/tasks/tasks-core/src/icons/unknown@2x.png b/plugins/tasks/tasks-core/src/icons/unknown@2x.png new file mode 100644 index 000000000000..8d1e84ca4569 Binary files /dev/null and b/plugins/tasks/tasks-core/src/icons/unknown@2x.png differ diff --git a/plugins/tasks/tasks-core/src/icons/youtrack.png b/plugins/tasks/tasks-core/src/icons/youtrack.png index de7b35dce28e..437f9eba6993 100644 Binary files a/plugins/tasks/tasks-core/src/icons/youtrack.png and b/plugins/tasks/tasks-core/src/icons/youtrack.png differ diff --git a/plugins/tasks/tasks-core/src/icons/youtrack@2x.png b/plugins/tasks/tasks-core/src/icons/youtrack@2x.png new file mode 100644 index 000000000000..6ba3829c6ff6 Binary files /dev/null and b/plugins/tasks/tasks-core/src/icons/youtrack@2x.png differ diff --git a/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/XsltSupport.java b/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/XsltSupport.java index 3a412bf3ca8c..68c031cea400 100644 --- a/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/XsltSupport.java +++ b/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/XsltSupport.java @@ -15,6 +15,8 @@ */ package org.intellij.lang.xpath.xslt; +import com.intellij.openapi.editor.DefaultLanguageHighlighterColors; +import com.intellij.openapi.editor.colors.TextAttributesKey; import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.util.Key; import com.intellij.psi.PsiElement; @@ -47,6 +49,8 @@ public class XsltSupport { public static final String XSLT_NS = "http://www.w3.org/1999/XSL/Transform"; public static final String PLUGIN_EXTENSIONS_NS = "urn:idea:xslt-plugin#extensions"; public static final Key> FORCE_XSLT_KEY = Key.create("FORCE_XSLT"); + public static final TextAttributesKey XSLT_DIRECTIVE = + TextAttributesKey.createTextAttributesKey("XSLT_DIRECTIVE", DefaultLanguageHighlighterColors.TEMPLATE_LANGUAGE_COLOR); private static final Map XPATH_ATTR_MAP = new THashMap(10); private static final Map> XPATH_AVT_MAP = new THashMap>(10); diff --git a/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/validation/XsltColorPage.java b/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/validation/XsltColorPage.java new file mode 100644 index 000000000000..e4f7f43057f0 --- /dev/null +++ b/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/validation/XsltColorPage.java @@ -0,0 +1,61 @@ +/* + * 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.intellij.lang.xpath.xslt.validation; + +import com.intellij.openapi.editor.colors.TextAttributesKey; +import com.intellij.openapi.options.colors.AttributesDescriptor; +import com.intellij.openapi.options.colors.pages.XMLColorsPage; +import org.intellij.lang.xpath.xslt.XsltSupport; +import org.jetbrains.annotations.NotNull; + +import java.util.Collections; +import java.util.Map; + +/** + * @author Dmitry Avdeev + */ +public class XsltColorPage extends XMLColorsPage { + @NotNull + @Override + public String getDisplayName() { + return "XSLT"; + } + + @NotNull + @Override + public AttributesDescriptor[] getAttributeDescriptors() { + return new AttributesDescriptor[] { new AttributesDescriptor("XSLT directive", XsltSupport.XSLT_DIRECTIVE)}; + } + + @NotNull + @Override + public String getDemoText() { + return "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; + } + + @Override + public Map getAdditionalHighlightingTagToDescriptorMap() { + return Collections.singletonMap("xsl", XsltSupport.XSLT_DIRECTIVE); + } +} diff --git a/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/validation/XsltNSColorProvider.java b/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/validation/XsltNSColorProvider.java new file mode 100644 index 000000000000..6687c71e70c2 --- /dev/null +++ b/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/validation/XsltNSColorProvider.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 org.intellij.lang.xpath.xslt.validation; + +import com.intellij.codeInsight.daemon.impl.analysis.XmlNSColorProvider; +import com.intellij.openapi.editor.colors.TextAttributesKey; +import com.intellij.psi.xml.XmlElement; +import com.intellij.psi.xml.XmlTag; +import org.intellij.lang.xpath.xslt.XsltSupport; +import org.jetbrains.annotations.Nullable; + +/** + * @author Dmitry Avdeev + */ +public class XsltNSColorProvider implements XmlNSColorProvider { + + @Nullable + @Override + public TextAttributesKey getKeyForNamespace(String namespace, XmlElement context) { + if (!(context instanceof XmlTag)) return null; + if (XsltSupport.XSLT_NS.equals(((XmlTag)context).getNamespace())) return XsltSupport.XSLT_DIRECTIVE; + return null; + } +} diff --git a/plugins/xpath/xpath-view/src/META-INF/plugin.xml b/plugins/xpath/xpath-view/src/META-INF/plugin.xml index 840cccb76aca..0be80ec48645 100644 --- a/plugins/xpath/xpath-view/src/META-INF/plugin.xml +++ b/plugins/xpath/xpath-view/src/META-INF/plugin.xml @@ -145,6 +145,9 @@ + + + diff --git a/python/edu/course-creator/resources/META-INF/plugin.xml b/python/edu/course-creator/resources/META-INF/plugin.xml index 7173f9d1c15b..f4e0d510a8d8 100644 --- a/python/edu/course-creator/resources/META-INF/plugin.xml +++ b/python/edu/course-creator/resources/META-INF/plugin.xml @@ -48,6 +48,9 @@ + + + diff --git a/python/edu/course-creator/resources/fileTemplates/internal/test_helper.py.ft b/python/edu/course-creator/resources/fileTemplates/internal/test_helper.py.ft index 9d1842742845..313b9150c467 100644 --- a/python/edu/course-creator/resources/fileTemplates/internal/test_helper.py.ft +++ b/python/edu/course-creator/resources/fileTemplates/internal/test_helper.py.ft @@ -46,9 +46,9 @@ def check_importable_path(path): try: import_file(path) except: - failed("File contains syntax errors", test_file_importable.func_name) + failed("File contains syntax errors", test_file_importable.__name__) return - passed(test_file_importable.func_name) + passed(test_file_importable.__name__) def import_file(path): diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCDocumentListener.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCDocumentListener.java index 3573216e9efa..b1917292df32 100644 --- a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCDocumentListener.java +++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCDocumentListener.java @@ -1,24 +1,23 @@ package org.jetbrains.plugins.coursecreator; import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.LogicalPosition; import com.intellij.openapi.editor.event.DocumentAdapter; import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.editor.impl.event.DocumentEventImpl; import org.jetbrains.plugins.coursecreator.format.TaskFile; import org.jetbrains.plugins.coursecreator.format.TaskWindow; +import java.util.ArrayList; +import java.util.List; + /** - * author: liana - * data: 7/16/14. * Listens changes in study files and updates * coordinates of all the windows in current task file */ public abstract class CCDocumentListener extends DocumentAdapter { private final TaskFile myTaskFile; - private int myOldLine; - private int myOldLineStartOffset; - private TaskWindow myTaskWindow; + private List myTaskWindows = new ArrayList(); + public CCDocumentListener(TaskFile taskFile) { myTaskFile = taskFile; @@ -29,39 +28,71 @@ public abstract class CCDocumentListener extends DocumentAdapter { // with fragments containing "\n" @Override public void beforeDocumentChange(DocumentEvent e) { - int offset = e.getOffset(); - int oldEnd = offset + e.getOldLength(); Document document = e.getDocument(); - myOldLine = document.getLineNumber(oldEnd); - myOldLineStartOffset = document.getLineStartOffset(myOldLine); - int line = document.getLineNumber(offset); - int offsetInLine = offset - document.getLineStartOffset(line); - LogicalPosition pos = new LogicalPosition(line, offsetInLine); - myTaskWindow = myTaskFile.getTaskWindow(document, pos); + myTaskWindows.clear(); + for (TaskWindow taskWindow : myTaskFile.getTaskWindows()) { + int twStart = taskWindow.getRealStartOffset(document); + int length = useLength() ? taskWindow.getLength() : taskWindow.getReplacementLength(); + int twEnd = twStart + length; + myTaskWindows.add(new TaskWindowWrapper(taskWindow, twStart, twEnd)); + } } @Override public void documentChanged(DocumentEvent e) { if (e instanceof DocumentEventImpl) { - DocumentEventImpl event = (DocumentEventImpl)e; - Document document = e.getDocument(); - int offset = e.getOffset(); - int change = event.getNewLength() - event.getOldLength(); - if (myTaskWindow != null) { - updateTaskWindowLength(e.getNewFragment(), myTaskWindow, change); + DocumentEventImpl event = (DocumentEventImpl)e; + Document document = e.getDocument(); + int offset = e.getOffset(); + int change = event.getNewLength() - event.getOldLength(); + for (TaskWindowWrapper taskWindowWrapper : myTaskWindows) { + int twStart = taskWindowWrapper.getTwStart(); + if (twStart > offset) { + twStart += change; + } + int twEnd = taskWindowWrapper.getTwEnd(); + if (twEnd >= offset) { + twEnd += change; + } + TaskWindow taskWindow = taskWindowWrapper.getTaskWindow(); + int line = document.getLineNumber(twStart); + int start = twStart - document.getLineStartOffset(line); + int length = twEnd - twStart; + taskWindow.setLine(line); + taskWindow.setStart(start); + if (useLength()) { + taskWindow.setLength(length); + } else { + taskWindow.setReplacementLength(length); } - int newEnd = offset + event.getNewLength(); - int newLine = document.getLineNumber(newEnd); - int lineChange = newLine - myOldLine; - myTaskFile.incrementLines(myOldLine + 1, lineChange); - int newEndOffsetInLine = offset + e.getNewLength() - document.getLineStartOffset(newLine); - int oldEndOffsetInLine = offset + e.getOldLength() - myOldLineStartOffset; - myTaskFile.updateLine(lineChange, myOldLine, newEndOffsetInLine, oldEndOffsetInLine, useLength()); } + } } - protected abstract void updateTaskWindowLength(CharSequence fragment, TaskWindow taskWindow, int change); - protected abstract boolean useLength(); + + private static class TaskWindowWrapper { + public TaskWindow myTaskWindow; + public int myTwStart; + public int myTwEnd; + + public TaskWindowWrapper(TaskWindow taskWindow, int twStart, int twEnd) { + myTaskWindow = taskWindow; + myTwStart = twStart; + myTwEnd = twEnd; + } + + public int getTwStart() { + return myTwStart; + } + + public int getTwEnd() { + return myTwEnd; + } + + public TaskWindow getTaskWindow() { + return myTaskWindow; + } + } } diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCEditorFactoryListener.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCEditorFactoryListener.java index 74afc795b60a..24688c4b2709 100644 --- a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCEditorFactoryListener.java +++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCEditorFactoryListener.java @@ -72,12 +72,6 @@ public class CCEditorFactoryListener implements EditorFactoryListener { super(taskFile); } - @Override - protected void updateTaskWindowLength(CharSequence fragment, TaskWindow taskWindow, int change) { - int newLength = taskWindow.getReplacementLength() + change; - taskWindow.setReplacementLength(newLength <= 0 ? 0 : newLength); - } - @Override protected boolean useLength() { return false; diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCProjectComponent.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCProjectComponent.java index 8b524dbffd4b..88a242492581 100644 --- a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCProjectComponent.java +++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCProjectComponent.java @@ -17,6 +17,7 @@ import com.intellij.openapi.vfs.VirtualFileAdapter; import com.intellij.openapi.vfs.VirtualFileEvent; import com.intellij.openapi.vfs.VirtualFileManager; import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.coursecreator.actions.CCRunTestsAction; import org.jetbrains.plugins.coursecreator.format.Course; import org.jetbrains.plugins.coursecreator.format.Lesson; import org.jetbrains.plugins.coursecreator.format.Task; @@ -158,7 +159,8 @@ public class CCProjectComponent implements ProjectComponent { } private void deleteTaskFile(Course course, VirtualFile removedFile) { - VirtualFile taskDir = removedFile.getParent(); + + final VirtualFile taskDir = removedFile.getParent(); if (taskDir == null || !taskDir.getName().contains("task")) { return; } @@ -182,6 +184,12 @@ public class CCProjectComponent implements ProjectComponent { if (taskFile == null) { return; } + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + CCRunTestsAction.clearTestEnvironment(taskDir, myProject); + } + }); String name = CCProjectService.getRealTaskFileName(removedFile.getName()); task.getTaskFiles().remove(name); } diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCProjectGenerator.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCProjectGenerator.java index 73e5bc7edf93..b40c6c5f913d 100644 --- a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCProjectGenerator.java +++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCProjectGenerator.java @@ -20,6 +20,8 @@ import icons.CourseCreatorIcons; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.coursecreator.actions.CCCreateLesson; +import org.jetbrains.plugins.coursecreator.actions.CCCreateTask; import org.jetbrains.plugins.coursecreator.format.Course; import org.jetbrains.plugins.coursecreator.ui.CCNewProjectPanel; @@ -69,9 +71,10 @@ public class CCProjectGenerator extends PythonProjectGenerator implements Direct catch (Exception ignored) { } DirectoryUtil.createSubdirectories("hints", projectDir, "\\/"); + final PsiDirectory lessonDir = CCCreateLesson.createLesson(projectDir, 1, null, null, course); + CCCreateTask.createTask(null, project, lessonDir, false); } }.execute(); - } @NotNull @@ -81,7 +84,7 @@ public class CCProjectGenerator extends PythonProjectGenerator implements Direct message = mySettingsPanel.getDescription().equals("") ? "Enter description" : message; message = mySettingsPanel.getAuthor().equals("") ? "Enter author name" : message; message = mySettingsPanel.getName().equals("") ? "Enter course name" : message; - return message.equals("")? ValidationResult.OK : new ValidationResult(message) ; + return message.equals("") ? ValidationResult.OK : new ValidationResult(message); } @Nullable diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCAddTaskWindow.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCAddTaskWindow.java index 6c2635fc2273..e423da553d1b 100644 --- a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCAddTaskWindow.java +++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCAddTaskWindow.java @@ -18,11 +18,27 @@ import org.jetbrains.plugins.coursecreator.CCProjectService; import org.jetbrains.plugins.coursecreator.format.*; import org.jetbrains.plugins.coursecreator.ui.CreateTaskWindowDialog; +import java.util.List; + public class CCAddTaskWindow extends DumbAwareAction { private static final Logger LOG = Logger.getInstance(CCAddTaskWindow.class); public CCAddTaskWindow() { - super("Add problem to solve","Add problem to solve", null); + super("Add problem to solve", "Add problem to solve", null); + } + + + private static boolean areTaskWindowsIntersect(@NotNull final TaskFile taskFile, @NotNull final Document document, int start, int end) { + List taskWindows = taskFile.getTaskWindows(); + for (TaskWindow existingTaskWindow : taskWindows) { + int twStart = existingTaskWindow.getRealStartOffset(document); + int twEnd = existingTaskWindow.getReplacementLength() + twStart; + if ((start >= twStart && start < twEnd) || (end > twStart && end <= twEnd) || + (twStart >= start && twStart < end) || (twEnd > start && twEnd <= end)) { + return true; + } + } + return false; } @Override @@ -35,7 +51,6 @@ public class CCAddTaskWindow extends DumbAwareAction { if (file == null) return; final Editor editor = CommonDataKeys.EDITOR.getData(e.getDataContext()); if (editor == null) return; - final SelectionModel model = editor.getSelectionModel(); final Document document = PsiDocumentManager.getInstance(project).getDocument(file); if (document == null) return; @@ -54,14 +69,23 @@ public class CCAddTaskWindow extends DumbAwareAction { final Lesson lesson = course.getLesson(lessonDir.getName()); final Task task = lesson.getTask(taskDir.getName()); final TaskFile taskFile = task.getTaskFile(file.getName()); + if (taskFile == null) { + return; + } + if (areTaskWindowsIntersect(taskFile, document, start, end)) { + return; + } final TaskWindow taskWindow = new TaskWindow(lineNumber, realStart, length, model.getSelectedText()); - CreateTaskWindowDialog dlg = new CreateTaskWindowDialog(project, taskWindow, lesson.getIndex(), task.getIndex(), file.getVirtualFile().getNameWithoutExtension(), taskFile.getTaskWindows().size() + 1); + CreateTaskWindowDialog dlg = new CreateTaskWindowDialog(project, taskWindow, lesson.getIndex(), + task.getIndex(), file.getVirtualFile().getNameWithoutExtension(), + taskFile.getTaskWindows().size() + 1); dlg.show(); if (dlg.getExitCode() != DialogWrapper.OK_EXIT_CODE) { return; } int index = taskFile.getTaskWindows().size() + 1; taskFile.addTaskWindow(taskWindow, index); + taskFile.sortTaskWindows(); taskWindow.drawHighlighter(editor, false); taskWindow.createGuardedBlocks(editor); } @@ -85,11 +109,14 @@ public class CCAddTaskWindow extends DumbAwareAction { presentation.setEnabled(false); return; } - if (!editor.getSelectionModel().hasSelection()) { + SelectionModel selectionModel = editor.getSelectionModel(); + if (!selectionModel.hasSelection()) { presentation.setVisible(false); presentation.setEnabled(false); return; } + int start = selectionModel.getSelectionStart(); + int end = selectionModel.getSelectionEnd(); final CCProjectService service = CCProjectService.getInstance(project); final Course course = service.getCourse(); @@ -116,6 +143,11 @@ public class CCAddTaskWindow extends DumbAwareAction { presentation.setEnabled(false); return; } + if (areTaskWindowsIntersect(taskFile, editor.getDocument(), start, end)) { + presentation.setVisible(false); + presentation.setEnabled(false); + return; + } presentation.setVisible(true); presentation.setEnabled(true); } diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCChangeCourseInfo.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCChangeCourseInfo.java new file mode 100644 index 000000000000..a556cd1113f1 --- /dev/null +++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCChangeCourseInfo.java @@ -0,0 +1,109 @@ +package org.jetbrains.plugins.coursecreator.actions; + +import com.intellij.ide.IdeView; +import com.intellij.ide.projectView.ProjectView; +import com.intellij.ide.util.DirectoryChooserUtil; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.actionSystem.LangDataKeys; +import com.intellij.openapi.actionSystem.Presentation; +import com.intellij.openapi.project.DumbAwareAction; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.psi.PsiDirectory; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.coursecreator.CCProjectService; +import org.jetbrains.plugins.coursecreator.format.Course; +import org.jetbrains.plugins.coursecreator.ui.CCNewProjectPanel; + +import javax.swing.*; + +public class CCChangeCourseInfo extends DumbAwareAction { + public CCChangeCourseInfo() { + super("Change Course information", "Change Course Information", null); + } + + @Override + public void update(@NotNull AnActionEvent event) { + if (!CCProjectService.setCCActionAvailable(event)) { + return; + } + final Presentation presentation = event.getPresentation(); + presentation.setVisible(false); + presentation.setEnabled(false); + final Project project = event.getData(CommonDataKeys.PROJECT); + if (project == null) { + return; + } + final IdeView view = event.getData(LangDataKeys.IDE_VIEW); + if (view == null) { + return; + } + final PsiDirectory[] directories = view.getDirectories(); + if (directories.length == 0) { + return; + } + final PsiDirectory directory = DirectoryChooserUtil.getOrChooseDirectory(view); + if (directory != null && !project.getBaseDir().equals(directory.getVirtualFile())) { + return; + } + presentation.setVisible(true); + presentation.setEnabled(true); + + } + + @Override + public void actionPerformed(@NotNull AnActionEvent e) { + if (!CCProjectService.setCCActionAvailable(e)) { + return; + } + final Project project = e.getProject(); + if (project == null) { + return; + } + Course course = CCProjectService.getInstance(project).getCourse(); + if (course == null) { + return; + } + final IdeView view = e.getData(LangDataKeys.IDE_VIEW); + if (view == null) { + return; + } + final PsiDirectory[] directories = view.getDirectories(); + if (directories.length == 0) { + return; + } + final PsiDirectory directory = DirectoryChooserUtil.getOrChooseDirectory(view); + if (directory != null && !project.getBaseDir().equals(directory.getVirtualFile())) { + return; + } + CCNewProjectPanel panel = new CCNewProjectPanel(course.getName(), course.getAuthor(), course.getDescription()); + ChangeCourseInfoDialog changeCourseInfoDialog = + new ChangeCourseInfoDialog(project, panel); + changeCourseInfoDialog.show(); + if (changeCourseInfoDialog.getExitCode() == DialogWrapper.OK_EXIT_CODE) { + course.setAuthor(panel.getAuthor()); + course.setName(panel.getName()); + course.setDescription(panel.getDescription()); + ProjectView.getInstance(project).refresh(); + } + } + + static class ChangeCourseInfoDialog extends DialogWrapper { + + CCNewProjectPanel myNewProjectPanel; + public ChangeCourseInfoDialog(@Nullable Project project, CCNewProjectPanel panel) { + super(project); + setTitle("Change Course Information"); + myNewProjectPanel = panel; + init(); + } + + @Nullable + @Override + protected JComponent createCenterPanel() { + return myNewProjectPanel.getMainPanel(); + } + } +} diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCCreateCourseArchive.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCCreateCourseArchive.java index c201e4ddaee4..b5a0292db684 100644 --- a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCCreateCourseArchive.java +++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCCreateCourseArchive.java @@ -134,6 +134,9 @@ public class CCCreateCourseArchive extends DumbAwareAction { final TaskFile taskFile = taskFiles.getValue(); TaskFile taskFileSaved = new TaskFile(); taskFile.copy(taskFileSaved); + for (TaskWindow taskWindow : taskFile.getTaskWindows()) { + taskWindow.setLength(taskWindow.getReplacementLength()); + } CommandProcessor.getInstance().executeCommand(project, new Runnable() { @Override public void run() { @@ -168,7 +171,7 @@ public class CCCreateCourseArchive extends DumbAwareAction { ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { - document.replaceString(offset, offset + taskWindow.getReplacementLength(), taskText); + document.replaceString(offset, offset + taskWindow.getLength(), taskText); FileDocumentManager.getInstance().saveDocument(document); } }); @@ -254,11 +257,6 @@ public class CCCreateCourseArchive extends DumbAwareAction { super(taskFile); } - @Override - protected void updateTaskWindowLength(CharSequence fragment, TaskWindow taskWindow, int change) { - //we don't need to update task window length - } - @Override protected boolean useLength() { return true; diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCCreateLesson.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCCreateLesson.java index bd7cb83ecc99..58b950b5a637 100644 --- a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCCreateLesson.java +++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCCreateLesson.java @@ -14,6 +14,7 @@ import com.intellij.openapi.ui.Messages; import com.intellij.psi.PsiDirectory; import com.intellij.util.PlatformIcons; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.coursecreator.CCProjectService; import org.jetbrains.plugins.coursecreator.format.Course; import org.jetbrains.plugins.coursecreator.format.Lesson; @@ -43,17 +44,27 @@ public class CCCreateLesson extends DumbAwareAction { ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { - final PsiDirectory lessonDirectory = DirectoryUtil.createSubdirectories("lesson" + (size+1), directory, "\\/"); - if (lessonDirectory != null) { - view.selectElement(lessonDirectory); - final Lesson lesson = new Lesson(lessonName); - lesson.setIndex(size + 1); - course.addLesson(lesson, lessonDirectory); - } + createLesson(directory, size + 1, lessonName, view, course); } }); } + @Nullable + public static PsiDirectory createLesson(@NotNull final PsiDirectory projectDir, int index, String name, final IdeView view, + @NotNull final Course course) { + String lessonFolderName = "lesson" + index; + final PsiDirectory lessonDirectory = DirectoryUtil.createSubdirectories("lesson" + index, projectDir, "\\/"); + if (lessonDirectory != null) { + if (view != null) { + view.selectElement(lessonDirectory); + } + final Lesson lesson = new Lesson(name != null ? name : lessonFolderName); + lesson.setIndex(index); + course.addLesson(lesson, lessonDirectory); + } + return lessonDirectory; + } + @Override public void update(@NotNull AnActionEvent event) { if (!CCProjectService.setCCActionAvailable(event)) { diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCCreateTask.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCCreateTask.java index fed406ec8df0..a0a16687bf23 100644 --- a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCCreateTask.java +++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCCreateTask.java @@ -36,24 +36,35 @@ public class CCCreateTask extends DumbAwareAction { final IdeView view = e.getData(LangDataKeys.IDE_VIEW); final Project project = e.getData(CommonDataKeys.PROJECT); - if (view == null || project == null) { + if (project == null) { return; } final PsiDirectory directory = DirectoryChooserUtil.getOrChooseDirectory(view); if (directory == null) return; + createTask(view, project, directory, true); + } + + public static void createTask(final IdeView view, final Project project, final PsiDirectory lessonDir, boolean showDialog) { final CCProjectService service = CCProjectService.getInstance(project); final Course course = service.getCourse(); - final Lesson lesson = course.getLesson(directory.getName()); + final Lesson lesson = course.getLesson(lessonDir.getName()); final int size = lesson.getTaskList().size(); + final String taskName; + if (showDialog) { + taskName = Messages.showInputDialog("Name:", "Task Name", null, "task" + (size + 1), null); + } + else { + taskName = "task" + (size + 1); + } - final String taskName = Messages.showInputDialog("Name:", "Task Name", null, "task" + (size + 1), null); - if (taskName == null) return; - + if (taskName == null) { + return; + } ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { - final PsiDirectory taskDirectory = DirectoryUtil.createSubdirectories("task" + (size + 1), directory, "\\/"); + final PsiDirectory taskDirectory = DirectoryUtil.createSubdirectories("task" + (size + 1), lessonDir, "\\/"); if (taskDirectory != null) { final FileTemplate template = FileTemplateManager.getInstance().getInternalTemplate("task.html"); final FileTemplate testsTemplate = FileTemplateManager.getInstance().getInternalTemplate("tests"); @@ -74,16 +85,17 @@ public class CCCreateTask extends DumbAwareAction { for (VirtualFile virtualFile : fileEditorManager.getOpenFiles()) { fileEditorManager.closeFile(virtualFile); } - EditorHelper.openInEditor(testsFile, false); - EditorHelper.openInEditor(taskPyFile, false); - view.selectElement(taskFile); + if (view != null) { + EditorHelper.openInEditor(testsFile, false); + EditorHelper.openInEditor(taskPyFile, false); + view.selectElement(taskFile); + EditorHelper.openInEditor(taskFile, false); + } } }); } catch (Exception ignored) { } - - } } }); @@ -126,6 +138,5 @@ public class CCCreateTask extends DumbAwareAction { presentation.setVisible(true); presentation.setEnabled(true); - } } \ No newline at end of file diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCRunTestsAction.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCRunTestsAction.java index 462678c477ad..777afc4e1a85 100644 --- a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCRunTestsAction.java +++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCRunTestsAction.java @@ -84,6 +84,7 @@ public class CCRunTestsAction extends AnAction { if (lessonDir == null) return; if (course == null) return; final Lesson lesson = course.getLesson(lessonDir.getName()); + if (lesson == null) return; final Task task = lesson.getTask(taskDir.getName()); if (task == null) { presentation.setVisible(false); diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCShowTaskWindowDetails.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCShowTaskWindowDetails.java index d865902a8025..e08743fc6941 100644 --- a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCShowTaskWindowDetails.java +++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCShowTaskWindowDetails.java @@ -5,10 +5,7 @@ import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.coursecreator.CCProjectService; -import org.jetbrains.plugins.coursecreator.format.Course; -import org.jetbrains.plugins.coursecreator.format.Lesson; -import org.jetbrains.plugins.coursecreator.format.Task; -import org.jetbrains.plugins.coursecreator.format.TaskFile; +import org.jetbrains.plugins.coursecreator.format.*; import org.jetbrains.plugins.coursecreator.ui.CreateTaskWindowDialog; public class CCShowTaskWindowDetails extends CCTaskWindowAction { @@ -29,7 +26,8 @@ public class CCShowTaskWindowDetails extends CCTaskWindowAction { final Lesson lesson = course.getLesson(lessonDir.getName()); final Task task = lesson.getTask(taskDir.getName()); final TaskFile taskFile = state.getTaskFile(); - CreateTaskWindowDialog dlg = new CreateTaskWindowDialog(project, state.getTaskWindow(), lesson.getIndex(), task.getIndex(), + TaskWindow taskWindow = state.getTaskWindow(); + CreateTaskWindowDialog dlg = new CreateTaskWindowDialog(project, taskWindow, lesson.getIndex(), task.getIndex(), file.getVirtualFile().getNameWithoutExtension(), taskFile.getTaskWindows().size() + 1); dlg.show(); diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/Course.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/Course.java index e124a6eb305d..e9af3472f260 100644 --- a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/Course.java +++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/Course.java @@ -58,4 +58,20 @@ public class Course { } Collections.sort(lessons); } + + public String getAuthor() { + return author; + } + + public void setAuthor(String author) { + this.author = author; + } + + public void setName(String name) { + this.name = name; + } + + public void setDescription(String description) { + this.description = description; + } } diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/TaskFile.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/TaskFile.java index c48a4f41d7e1..9fb10eaa179a 100644 --- a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/TaskFile.java +++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/TaskFile.java @@ -6,9 +6,9 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.LogicalPosition; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.plugins.coursecreator.CCProjectService; import java.util.ArrayList; +import java.util.Collections; import java.util.List; public class TaskFile { @@ -57,52 +57,6 @@ public class TaskFile { return null; } - /** - * Updates task window lines - * - * @param startLine lines greater than this line and including this line will be updated - * @param change change to be added to line numbers - */ - public void incrementLines(int startLine, int change) { - for (TaskWindow taskTaskWindow : task_windows) { - if (taskTaskWindow.getLine() >= startLine) { - taskTaskWindow.setLine(taskTaskWindow.getLine() + change); - } - } - } - - /** - * Updates windows in specific line - * - * @param lineChange change in line number - * @param line line to be updated - * @param newEndOffsetInLine distance from line start to end of inserted fragment - * @param oldEndOffsetInLine distance from line start to end of changed fragment - */ - public void updateLine(int lineChange, int line, int newEndOffsetInLine, int oldEndOffsetInLine, boolean useLength) { - for (TaskWindow w : task_windows) { - if ((w.getLine() == line) && (w.getStart() > oldEndOffsetInLine)) { - int distance = w.getStart() - oldEndOffsetInLine; - boolean coveredByPrevTW = false; - int prevIndex = w.getIndex() - 1; - if (CCProjectService.indexIsValid(prevIndex, task_windows)) { - TaskWindow prevTW = task_windows.get(prevIndex - 1); - if (prevTW.getLine() == line) { - int prevLength = useLength ? prevTW.getLength() : prevTW.getReplacementLength(); - int endOffset = prevTW.getStart() + prevLength; - if (endOffset >= newEndOffsetInLine) { - coveredByPrevTW = true; - } - } - } - if (lineChange != 0 || newEndOffsetInLine <= w.getStart() || coveredByPrevTW) { - w.setStart(distance + newEndOffsetInLine); - w.setLine(line + lineChange); - } - } - } - } - public void copy(@NotNull final TaskFile target) { target.setIndex(myIndex); for (TaskWindow taskWindow : task_windows) { @@ -110,6 +64,7 @@ public class TaskFile { taskWindow.getLength(), ""); target.getTaskWindows().add(savedWindow); savedWindow.setIndex(taskWindow.getIndex()); + savedWindow.setReplacementLength(taskWindow.getReplacementLength()); } } @@ -121,6 +76,8 @@ public class TaskFile { } taskWindowUpdated.setLine(taskWindow.getLine()); taskWindowUpdated.setStart(taskWindow.getStart()); + taskWindowUpdated.setReplacementLength(taskWindow.getReplacementLength()); + taskWindowUpdated.setLength(taskWindow.getLength()); } } @@ -142,4 +99,11 @@ public class TaskFile { taskWindow.createGuardedBlocks(editor); } } + + public void sortTaskWindows() { + Collections.sort(task_windows); + for (int i = 0; i < task_windows.size(); i++) { + task_windows.get(i).setIndex(i + 1); + } + } } diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java index dc7551929e30..385aaeddf26e 100644 --- a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java +++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/format/TaskWindow.java @@ -20,7 +20,7 @@ import org.jetbrains.plugins.coursecreator.CCProjectService; import java.io.File; import java.util.List; -public class TaskWindow implements Comparable{ +public class TaskWindow implements Comparable { @Expose public int line; @Expose public int start; @@ -120,8 +120,7 @@ public class TaskWindow implements Comparable{ } @Override - public int compareTo(Object o) { - TaskWindow taskWindow = (TaskWindow)o; + public int compareTo(@NotNull TaskWindow taskWindow) { int lineDiff = line - taskWindow.line; if (lineDiff == 0) { return start - taskWindow.start; @@ -154,4 +153,8 @@ public class TaskWindow implements Comparable{ .addRangeHighlighter(start, end, HighlighterLayer.LAST + 1, null, HighlighterTargetArea.EXACT_RANGE); blocks.add(rh); } + + public void setLength(int length) { + this.length = length; + } } \ No newline at end of file diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CCNewProjectPanel.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CCNewProjectPanel.java index d463718978bb..de23a904422c 100644 --- a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CCNewProjectPanel.java +++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CCNewProjectPanel.java @@ -27,6 +27,13 @@ public class CCNewProjectPanel { myDescription.setLineWrap(true); } + public CCNewProjectPanel(String name, String author, String description) { + this(); + myName.setText(name); + myAuthorField.setText(author); + myDescription.setText(description); + } + public JPanel getMainPanel() { return myPanel; } @@ -54,7 +61,9 @@ public class CCNewProjectPanel { @Override protected void textChanged(DocumentEvent e) { - myValidationManager.validate(); + if (myValidationManager != null) { + myValidationManager.validate(); + } } } } diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowDialog.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowDialog.java index 2240d58a2846..213fc55e2cb7 100644 --- a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowDialog.java +++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowDialog.java @@ -95,6 +95,10 @@ public class CreateTaskWindowDialog extends DialogWrapper { myTaskWindow.setHint(hintName); String hintText = myPanel.getHintText(); createHint(hintName, hintText); + } else { + if (myTaskWindow.getHintName() != null) { + deleteHint(); + } } super.doOKAction(); } diff --git a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowPanel.java b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowPanel.java index 4fc64fa769c6..a09945718aaa 100644 --- a/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowPanel.java +++ b/python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/ui/CreateTaskWindowPanel.java @@ -32,7 +32,7 @@ public class CreateTaskWindowPanel extends JPanel { // 1 for checked enableHint(state == 1); if (state == 2) { - myDialog.deleteHint(); + resetHint(); } } }); diff --git a/python/edu/learn-python/resources/courses/introduction_course.zip b/python/edu/learn-python/resources/courses/introduction_course.zip index 07ad882088c1..c44ce1790f94 100644 Binary files a/python/edu/learn-python/resources/courses/introduction_course.zip and b/python/edu/learn-python/resources/courses/introduction_course.zip differ diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyDirectoryProjectGenerator.java b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyDirectoryProjectGenerator.java index b558667734bc..a9630f6f730c 100644 --- a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyDirectoryProjectGenerator.java +++ b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyDirectoryProjectGenerator.java @@ -5,19 +5,30 @@ import com.google.gson.stream.JsonReader; import com.intellij.facet.ui.FacetEditorValidator; import com.intellij.facet.ui.FacetValidatorsManager; import com.intellij.facet.ui.ValidationResult; +import com.intellij.ide.projectView.ProjectView; +import com.intellij.ide.projectView.impl.AbstractProjectViewPane; import com.intellij.lang.javascript.boilerplate.GithubDownloadUtil; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.module.Module; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Condition; +import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; +import com.intellij.openapi.vfs.newvfs.impl.VirtualDirectoryImpl; +import com.intellij.openapi.wm.ex.ToolWindowManagerAdapter; +import com.intellij.openapi.wm.ex.ToolWindowManagerEx; import com.intellij.platform.DirectoryProjectGenerator; import com.intellij.platform.templates.github.GeneratorException; import com.intellij.platform.templates.github.ZipUtil; -import com.jetbrains.python.edu.course.Course; -import com.jetbrains.python.edu.course.CourseInfo; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiManager; +import com.jetbrains.python.edu.course.*; import com.jetbrains.python.edu.ui.StudyNewProjectPanel; import com.jetbrains.python.newProject.PythonProjectGenerator; import icons.StudyIcons; @@ -150,12 +161,60 @@ public class StudyDirectoryProjectGenerator extends PythonProjectGenerator imple try { reader = new InputStreamReader(new FileInputStream(mySelectedCourseFile)); Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create(); - Course course = gson.fromJson(reader, Course.class); + final Course course = gson.fromJson(reader, Course.class); course.init(false); course.create(baseDir, new File(mySelectedCourseFile.getParent())); course.setResourcePath(mySelectedCourseFile.getAbsolutePath()); VirtualFileManager.getInstance().refreshWithoutFileWatcher(true); StudyTaskManager.getInstance(project).setCourse(course); + final boolean[] initialized = {false}; + ToolWindowManagerEx.getInstanceEx(myProject).addToolWindowManagerListener(new ToolWindowManagerAdapter() { + @Override + public void stateChanged() { + final AbstractProjectViewPane projectViewPane = ProjectView.getInstance(myProject).getCurrentProjectViewPane(); + if (projectViewPane == null || initialized[0]) return; + JTree tree = projectViewPane.getTree(); + if (tree == null) { + return; + } + tree.updateUI(); + + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + LocalFileSystem.getInstance().refresh(false); + final Lesson firstLesson = StudyUtils.getFirst(course.getLessons()); + final Task firstTask = StudyUtils.getFirst(firstLesson.getTaskList()); + final VirtualFile taskDir = firstTask.getTaskDir(myProject); + if (taskDir == null) return; + final Map taskFiles = firstTask.getTaskFiles(); + + VirtualFile activeVirtualFile = null; + for (Map.Entry entry : taskFiles.entrySet()) { + final String name = entry.getKey(); + final TaskFile taskFile = entry.getValue(); + final VirtualFile virtualFile = ((VirtualDirectoryImpl)taskDir).refreshAndFindChild(name); + if (virtualFile != null) { + FileEditorManager.getInstance(project).openFile(virtualFile, true); + if (!taskFile.getTaskWindows().isEmpty()) { + activeVirtualFile = virtualFile; + } + } + } + if (activeVirtualFile != null) { + final PsiFile file = PsiManager.getInstance(myProject).findFile(activeVirtualFile); + ProjectView.getInstance(project).select(file, activeVirtualFile, true); + initialized[0] = true; + } + } + }, ModalityState.current(), new Condition() { + @Override + public boolean value(Object o) { + return project.isDisposed(); + } + }); + } + }); } catch (FileNotFoundException e) { LOG.error(e); diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyDocumentListener.java b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyDocumentListener.java index 8955032aa408..848becfae1b1 100644 --- a/python/edu/learn-python/src/com/jetbrains/python/edu/StudyDocumentListener.java +++ b/python/edu/learn-python/src/com/jetbrains/python/edu/StudyDocumentListener.java @@ -2,22 +2,22 @@ package com.jetbrains.python.edu; import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.LogicalPosition; import com.intellij.openapi.editor.event.DocumentAdapter; import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.editor.impl.event.DocumentEventImpl; import com.jetbrains.python.edu.course.TaskFile; import com.jetbrains.python.edu.course.TaskWindow; +import java.util.ArrayList; +import java.util.List; + /** * Listens changes in study files and updates * coordinates of all the windows in current task file */ public class StudyDocumentListener extends DocumentAdapter { private final TaskFile myTaskFile; - private int myOldLine; - private int myOldLineStartOffset; - private TaskWindow myTaskWindow; + private List myTaskWindows = new ArrayList(); public StudyDocumentListener(TaskFile taskFile) { myTaskFile = taskFile; @@ -28,15 +28,13 @@ public class StudyDocumentListener extends DocumentAdapter { // with fragments containing "\n" @Override public void beforeDocumentChange(DocumentEvent e) { - int offset = e.getOffset(); - int oldEnd = offset + e.getOldLength(); Document document = e.getDocument(); - myOldLine = document.getLineNumber(oldEnd); - myOldLineStartOffset = document.getLineStartOffset(myOldLine); - int line = document.getLineNumber(offset); - int offsetInLine = offset - document.getLineStartOffset(line); - LogicalPosition pos = new LogicalPosition(line, offsetInLine); - myTaskWindow = myTaskFile.getTaskWindow(document, pos); + myTaskWindows.clear(); + for (TaskWindow taskWindow : myTaskFile.getTaskWindows()) { + int twStart = taskWindow.getRealStartOffset(document); + int twEnd = twStart + taskWindow.getLength(); + myTaskWindows.add(new TaskWindowWrapper(taskWindow, twStart, twEnd)); + } } @Override @@ -46,17 +44,47 @@ public class StudyDocumentListener extends DocumentAdapter { Document document = e.getDocument(); int offset = e.getOffset(); int change = event.getNewLength() - event.getOldLength(); - if (myTaskWindow != null) { - int newLength = myTaskWindow.getLength() + change; - myTaskWindow.setLength(newLength <= 0 ? 0 : newLength); + for (TaskWindowWrapper taskWindowWrapper : myTaskWindows) { + int twStart = taskWindowWrapper.getTwStart(); + if (twStart > offset) { + twStart += change; + } + int twEnd = taskWindowWrapper.getTwEnd(); + if (twEnd >= offset) { + twEnd += change; + } + TaskWindow taskWindow = taskWindowWrapper.getTaskWindow(); + int line = document.getLineNumber(twStart); + int start = twStart - document.getLineStartOffset(line); + int length = twEnd - twStart; + taskWindow.setLine(line); + taskWindow.setStart(start); + taskWindow.setLength(length); } - int newEnd = offset + event.getNewLength(); - int newLine = document.getLineNumber(newEnd); - int lineChange = newLine - myOldLine; - myTaskFile.incrementLines(myOldLine + 1, lineChange); - int newEndOffsetInLine = offset + e.getNewLength() - document.getLineStartOffset(newLine); - int oldEndOffsetInLine = offset + e.getOldLength() - myOldLineStartOffset; - myTaskFile.updateLine(lineChange, myOldLine, newEndOffsetInLine, oldEndOffsetInLine); + } + } + + private static class TaskWindowWrapper { + public TaskWindow myTaskWindow; + public int myTwStart; + public int myTwEnd; + + public TaskWindowWrapper(TaskWindow taskWindow, int twStart, int twEnd) { + myTaskWindow = taskWindow; + myTwStart = twStart; + myTwEnd = twEnd; + } + + public int getTwStart() { + return myTwStart; + } + + public int getTwEnd() { + return myTwEnd; + } + + public TaskWindow getTaskWindow() { + return myTaskWindow; } } } diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/course/TaskFile.java b/python/edu/learn-python/src/com/jetbrains/python/edu/course/TaskFile.java index aaaef375a093..e9281e957855 100644 --- a/python/edu/learn-python/src/com/jetbrains/python/edu/course/TaskFile.java +++ b/python/edu/learn-python/src/com/jetbrains/python/edu/course/TaskFile.java @@ -127,20 +127,6 @@ public class TaskFile implements Stateful { return null; } - /** - * Updates task window lines - * - * @param startLine lines greater than this line and including this line will be updated - * @param change change to be added to line numbers - */ - public void incrementLines(int startLine, int change) { - for (TaskWindow taskTaskWindow : taskWindows) { - if (taskTaskWindow.getLine() >= startLine) { - taskTaskWindow.setLine(taskTaskWindow.getLine() + change); - } - } - } - /** * Initializes state of task file * @@ -165,36 +151,6 @@ public class TaskFile implements Stateful { myIndex = index; } - /** - * Updates windows in specific line - * - * @param lineChange change in line number - * @param line line to be updated - * @param newEndOffsetInLine distance from line start to end of inserted fragment - * @param oldEndOffsetInLine distance from line start to end of changed fragment - */ - public void updateLine(int lineChange, int line, int newEndOffsetInLine, int oldEndOffsetInLine) { - for (TaskWindow w : taskWindows) { - if ((w.getLine() == line) && (w.getStart() > oldEndOffsetInLine)) { - int distance = w.getStart() - oldEndOffsetInLine; - boolean coveredByPrevTW = false; - int prevIndex = w.getIndex() - 1; - if (StudyUtils.indexIsValid(prevIndex, taskWindows)) { - TaskWindow prevTW = taskWindows.get(prevIndex); - if (prevTW.getLine() == line) { - int endOffset = prevTW.getStart() + prevTW.getLength(); - if (endOffset >= newEndOffsetInLine) { - coveredByPrevTW = true; - } - } - } - if (lineChange != 0 || newEndOffsetInLine <= w.getStart() || coveredByPrevTW) { - w.setStart(distance + newEndOffsetInLine); - w.setLine(line + lineChange); - } - } - } - } public static void copy(@NotNull final TaskFile source, @NotNull final TaskFile target) { List sourceTaskWindows = source.getTaskWindows(); diff --git a/python/edu/src/com/jetbrains/python/edu/PyExecuteFileLineMarkerProvider.java b/python/edu/src/com/jetbrains/python/edu/PyExecuteFileLineMarkerProvider.java index 19acded8f238..0c0208b1f144 100644 --- a/python/edu/src/com/jetbrains/python/edu/PyExecuteFileLineMarkerProvider.java +++ b/python/edu/src/com/jetbrains/python/edu/PyExecuteFileLineMarkerProvider.java @@ -54,23 +54,28 @@ public class PyExecuteFileLineMarkerProvider implements LineMarkerProvider { return new LineMarkerGutterIconRenderer(this){ @Override public AnAction getClickAction() { - final DefaultActionGroup group = new DefaultActionGroup(); - group.add(new PyRunCurrentFileAction()); - final PyExecuteFileExtensionPoint[] extensions = - ApplicationManager.getApplication().getExtensions(PyExecuteFileExtensionPoint.EP_NAME); - for (PyExecuteFileExtensionPoint extension : extensions) { - final AnAction action = extension.getRunAction(); - group.add(action); - } - if (group.getChildrenCount() == 1) { - return new PyRunCurrentFileAction(); - } + return new AnAction() { @Override public void actionPerformed(@NotNull AnActionEvent e) { - final ListPopup popup = - new PopupFactoryImpl().createActionGroupPopup(null, group, e.getDataContext(), false, false, false, null, 5); - popup.showInBestPositionFor(e.getDataContext()); + final DefaultActionGroup group = new DefaultActionGroup(); + group.add(new PyRunCurrentFileAction()); + final PyExecuteFileExtensionPoint[] extensions = + ApplicationManager.getApplication().getExtensions(PyExecuteFileExtensionPoint.EP_NAME); + for (PyExecuteFileExtensionPoint extension : extensions) { + final AnAction action = extension.getRunAction(); + action.update(e); + if (e.getPresentation().isEnabled()) + group.add(action); + } + if (group.getChildrenCount() == 1) { + new PyRunCurrentFileAction().actionPerformed(e); + } + else { + final ListPopup popup = + new PopupFactoryImpl().createActionGroupPopup(null, group, e.getDataContext(), false, false, false, null, 5); + popup.showInBestPositionFor(e.getDataContext()); + } } }; } diff --git a/python/helpers/pydev/_pydev_imports_tipper.py b/python/helpers/pydev/_pydev_imports_tipper.py index 76cf2cdc473f..35c4867533a8 100644 --- a/python/helpers/pydev/_pydev_imports_tipper.py +++ b/python/helpers/pydev/_pydev_imports_tipper.py @@ -178,8 +178,11 @@ def GenerateImportsTipForModule(obj_to_complete, dirComps=None, getattr=getattr, args = '' try: - obj = getattr(obj_to_complete, d) - except: #just ignore and get it without aditional info + try: + obj = getattr(obj_to_complete.__class__, d) + except: + obj = getattr(obj_to_complete, d) + except: #just ignore and get it without additional info ret.append((d, '', args, TYPE_BUILTIN)) else: diff --git a/python/helpers/pydev/third_party/pkgutil_old.py b/python/helpers/pydev/_pydev_imps/_pydev_pkgutil_old.py similarity index 100% rename from python/helpers/pydev/third_party/pkgutil_old.py rename to python/helpers/pydev/_pydev_imps/_pydev_pkgutil_old.py diff --git a/python/helpers/pydev/third_party/pluginbase.py b/python/helpers/pydev/_pydev_imps/_pydev_pluginbase.py similarity index 99% rename from python/helpers/pydev/third_party/pluginbase.py rename to python/helpers/pydev/_pydev_imps/_pydev_pluginbase.py index 18bd50524fc2..ed4203fb6f89 100644 --- a/python/helpers/pydev/third_party/pluginbase.py +++ b/python/helpers/pydev/_pydev_imps/_pydev_pluginbase.py @@ -16,14 +16,14 @@ from pydevd_constants import IS_PY24, IS_PY3K, IS_JYTHON if IS_PY24: - from third_party.uuid_old import uuid4 + from _pydev_imps._pydev_uuid_old import uuid4 else: from uuid import uuid4 if IS_PY3K: import pkgutil else: - import pkgutil_old as pkgutil + from _pydev_imps import _pydev_pkgutil_old as pkgutil import errno try: diff --git a/python/helpers/pydev/third_party/uuid_old.py b/python/helpers/pydev/_pydev_imps/_pydev_uuid_old.py similarity index 100% rename from python/helpers/pydev/third_party/uuid_old.py rename to python/helpers/pydev/_pydev_imps/_pydev_uuid_old.py diff --git a/python/helpers/pydev/_pydev_threading.py b/python/helpers/pydev/_pydev_threading.py index 62b300b86cd0..940dce5edcf3 100644 --- a/python/helpers/pydev/_pydev_threading.py +++ b/python/helpers/pydev/_pydev_threading.py @@ -1,4 +1,7 @@ -from threading import enumerate, currentThread, Condition, Event, Timer, Thread +from threading import * # Make up for things we may forget @UnusedWildImport + +# Force what we know we need +from threading import enumerate, currentThread, Condition, Event, Timer, Thread, Lock try: from threading import settrace except: diff --git a/python/helpers/pydev/import_hook.py b/python/helpers/pydev/import_hook.py new file mode 100644 index 000000000000..64bc623c22fe --- /dev/null +++ b/python/helpers/pydev/import_hook.py @@ -0,0 +1,43 @@ + +import sys +from pydevd_constants import IS_PY24, IS_PY3K +from pydevd_constants import DictContains +from types import ModuleType + + +class ImportHookManager(ModuleType): + def __init__(self, name, system_import): + ModuleType.__init__(self, name) + self._system_import = system_import + self._modules_to_patch = {} + + def add_module_name(self, module_name, activate_function): + self._modules_to_patch[module_name] = activate_function + + def do_import(self, name, globals=None, locals=None, fromlist=None, level=-2): + if level == -2: + # fake impossible value; default value depends on version + if IS_PY24: + # the level parameter was added in version 2.5 + return self._system_import(name, globals, locals, fromlist) + elif IS_PY3K: + # default value for level parameter in python 3 + level = 0 + else: + # default value for level parameter in other versions + level = -1 + module = self._system_import(name, globals, locals, fromlist, level) + if DictContains(self._modules_to_patch, name): + self._modules_to_patch[name]() #call activate function + self._modules_to_patch.pop(name) + return module + +try: + import __builtin__ as builtins +except ImportError: + import builtins + +import_hook_manager = ImportHookManager(__name__ + '.import_hook', builtins.__import__) +builtins.__import__ = import_hook_manager.do_import +sys.modules[import_hook_manager.__name__] = import_hook_manager +del builtins \ No newline at end of file diff --git a/python/helpers/pydev/pydev_ipython/matplotlibtools.py b/python/helpers/pydev/pydev_ipython/matplotlibtools.py new file mode 100644 index 000000000000..075c43781ab3 --- /dev/null +++ b/python/helpers/pydev/pydev_ipython/matplotlibtools.py @@ -0,0 +1,132 @@ + +import sys +from import_hook import import_hook_manager + +backends = {'tk': 'TkAgg', + 'gtk': 'GTKAgg', + 'wx': 'WXAgg', + 'qt': 'Qt4Agg', # qt3 not supported + 'qt4': 'Qt4Agg', + 'osx': 'MacOSX'} + +# We also need a reverse backends2guis mapping that will properly choose which +# GUI support to activate based on the desired matplotlib backend. For the +# most part it's just a reverse of the above dict, but we also need to add a +# few others that map to the same GUI manually: +backend2gui = dict(zip(backends.values(), backends.keys())) +backend2gui['Qt4Agg'] = 'qt' +# In the reverse mapping, there are a few extra valid matplotlib backends that +# map to the same GUI support +backend2gui['GTK'] = backend2gui['GTKCairo'] = 'gtk' +backend2gui['WX'] = 'wx' +backend2gui['CocoaAgg'] = 'osx' + + +def find_gui_and_backend(): + """Return the gui and mpl backend.""" + matplotlib = sys.modules['matplotlib'] + # WARNING: this assumes matplotlib 1.1 or newer!! + backend = matplotlib.rcParams['backend'] + # In this case, we need to find what the appropriate gui selection call + # should be for IPython, so we can activate inputhook accordingly + gui = backend2gui.get(backend, None) + return gui, backend + + +def is_interactive_backend(backend): + """ Check if backend is interactive """ + matplotlib = sys.modules['matplotlib'] + from matplotlib.rcsetup import interactive_bk, non_interactive_bk + if backend in interactive_bk: + return True + elif backend in non_interactive_bk: + return False + else: + return matplotlib.is_interactive() + + +def patch_use(interpreter): + """ Patch matplotlib function 'use' """ + matplotlib = sys.modules['matplotlib'] + def patched_use(*args, **kwargs): + matplotlib.real_use(*args, **kwargs) + gui, backend = find_gui_and_backend() + interpreter.enableGui(gui) + + setattr(matplotlib, "real_use", getattr(matplotlib, "use")) + setattr(matplotlib, "use", patched_use) + + +def activate_matplotlib(interpreter): + """Set interactive to True for interactive backends.""" + def activate_matplotlib_inner(): + matplotlib = sys.modules['matplotlib'] + gui, backend = find_gui_and_backend() + is_interactive = is_interactive_backend(backend) + if is_interactive: + interpreter.enableGui(gui) + if not matplotlib.is_interactive(): + sys.stdout.write("Backend %s is interactive backend. Turning interactive mode on.\n" % backend) + matplotlib.interactive(True) + else: + if matplotlib.is_interactive(): + sys.stdout.write("Backend %s is non-interactive backend. Turning interactive mode off.\n" % backend) + matplotlib.interactive(False) + patch_use(interpreter) + return activate_matplotlib_inner + + +def init_matplotlib(interpreter): + import_hook_manager.add_module_name("matplotlib", activate_matplotlib(interpreter)) + + +def flag_calls(func): + """Wrap a function to detect and flag when it gets called. + + This is a decorator which takes a function and wraps it in a function with + a 'called' attribute. wrapper.called is initialized to False. + + The wrapper.called attribute is set to False right before each call to the + wrapped function, so if the call fails it remains False. After the call + completes, wrapper.called is set to True and the output is returned. + + Testing for truth in wrapper.called allows you to determine if a call to + func() was attempted and succeeded.""" + + # don't wrap twice + if hasattr(func, 'called'): + return func + + def wrapper(*args,**kw): + wrapper.called = False + out = func(*args,**kw) + wrapper.called = True + return out + + wrapper.called = False + wrapper.__doc__ = func.__doc__ + return wrapper + + +def activate_pylab(): + pylab = sys.modules['pylab'] + pylab.show._needmain = False + # We need to detect at runtime whether show() is called by the user. + # For this, we wrap it into a decorator which adds a 'called' flag. + pylab.draw_if_interactive = flag_calls(pylab.draw_if_interactive) + + +def init_pylab(): + import_hook_manager.add_module_name("pylab", activate_pylab) + + +def activate_pyplot(): + pyplot = sys.modules['matplotlib.pyplot'] + pyplot.show._needmain = False + # We need to detect at runtime whether show() is called by the user. + # For this, we wrap it into a decorator which adds a 'called' flag. + pyplot.draw_if_interactive = flag_calls(pyplot.draw_if_interactive) + + +def init_pyplot(): + import_hook_manager.add_module_name("pyplot", activate_pyplot) \ No newline at end of file diff --git a/python/helpers/pydev/pydev_runfiles.py b/python/helpers/pydev/pydev_runfiles.py index bb704db3f240..b20927584088 100644 --- a/python/helpers/pydev/pydev_runfiles.py +++ b/python/helpers/pydev/pydev_runfiles.py @@ -3,11 +3,9 @@ from __future__ import nested_scopes import fnmatch import os.path from pydev_runfiles_coverage import StartCoverageSupport -import pydev_runfiles_unittest from pydevd_constants import * #@UnusedWildImport import re import time -import unittest #======================================================================================================================= @@ -558,6 +556,7 @@ class PydevTestRunner(object): def _decorate_test_suite(self, suite, pyfile, module_name): + import unittest if isinstance(suite, unittest.TestSuite): add = False suite.__pydev_pyfile__ = pyfile @@ -582,6 +581,8 @@ class PydevTestRunner(object): def find_tests_from_modules(self, file_and_modules_and_module_name): """ returns the unittests given a list of modules """ #Use our own suite! + import pydev_runfiles_unittest + import unittest unittest.TestLoader.suiteClass = pydev_runfiles_unittest.PydevTestSuite loader = unittest.TestLoader() @@ -628,6 +629,7 @@ class PydevTestRunner(object): def filter_tests(self, test_objs, internal_call=False): """ based on a filter name, only return those tests that have the test case names that match """ + import unittest if not internal_call: if not self.configuration.include_tests and not self.tests and not self.configuration.exclude_tests: #No need to filter if we have nothing to filter! @@ -692,6 +694,7 @@ class PydevTestRunner(object): def iter_tests(self, test_objs): #Note: not using yield because of Jython 2.1. + import unittest tests = [] for test_obj in test_objs: if isinstance(test_obj, unittest.TestSuite): @@ -762,6 +765,7 @@ class PydevTestRunner(object): all_tests = self.find_tests_from_modules(file_and_modules_and_module_name) all_tests = self.filter_tests(all_tests) + import pydev_runfiles_unittest test_suite = pydev_runfiles_unittest.PydevTestSuite(all_tests) import pydev_runfiles_xml_rpc pydev_runfiles_xml_rpc.notifyTestsCollected(test_suite.countTestCases()) diff --git a/python/helpers/pydev/pydevconsole.py b/python/helpers/pydev/pydevconsole.py index 9b4e2a89efba..b38847a82925 100644 --- a/python/helpers/pydev/pydevconsole.py +++ b/python/helpers/pydev/pydevconsole.py @@ -171,6 +171,11 @@ def process_exec_queue(interpreter): set_return_control_callback(return_control) + from pydev_ipython.matplotlibtools import init_matplotlib, init_pylab, init_pyplot + init_matplotlib(interpreter) + init_pylab() + init_pyplot() + while 1: # Running the request may have changed the inputhook in use inputhook = get_inputhook() @@ -322,6 +327,10 @@ def get_interpreter(): try: interpreterInterface = getattr(__builtin__, 'interpreter') except AttributeError: + # fake return_controll_callback function just to prevent exception in PyCharm bebug console + from pydev_ipython.inputhook import set_return_control_callback + set_return_control_callback(lambda x: True) + interpreterInterface = InterpreterInterface(None, None, threading.currentThread()) setattr(__builtin__, 'interpreter', interpreterInterface) diff --git a/python/helpers/pydev/pydevd.py b/python/helpers/pydev/pydevd.py index cd6a034e5aba..dfac7691dc79 100644 --- a/python/helpers/pydev/pydevd.py +++ b/python/helpers/pydev/pydevd.py @@ -116,9 +116,10 @@ DONT_TRACE = { 'threading.py':1, # thirs party libs that we don't want to trace - 'pluginbase.py':1, - 'pkgutil_old.py':1, - 'uuid_old.py':1, + '_pydev_pluginbase.py':1, + '_pydev_pkgutil_old.py':1, + '_pydev_uuid_old.py':1, + #things from pydev that we don't want to trace '_pydev_execfile.py':1, diff --git a/python/helpers/pydev/pydevd_attach_to_process/add_code_to_python_process.py b/python/helpers/pydev/pydevd_attach_to_process/add_code_to_python_process.py index 6a64f4e7ed41..51264ceea75e 100644 --- a/python/helpers/pydev/pydevd_attach_to_process/add_code_to_python_process.py +++ b/python/helpers/pydev/pydevd_attach_to_process/add_code_to_python_process.py @@ -263,6 +263,10 @@ def resolve_label(process, label): def is_python_64bit(): return (struct.calcsize('P') == 8) +def is_mac(): + import platform + return platform.system() == 'Darwin' + def run_python_code_windows(pid, python_code, connect_debugger_tracing=False, show_debug_info=0): assert '\'' not in python_code, 'Having a single quote messes with our command.' from winappdbg import compat @@ -395,11 +399,11 @@ def run_python_code_windows(pid, python_code, connect_debugger_tracing=False, sh def run_python_code_linux(pid, python_code, connect_debugger_tracing=False, show_debug_info=0): assert '\'' not in python_code, 'Having a single quote messes with our command.' filedir = os.path.dirname(__file__) - + # Valid arguments for arch are i386, i386:x86-64, i386:x64-32, i8086, # i386:intel, i386:x86-64:intel, i386:x64-32:intel, i386:nacl, # i386:x86-64:nacl, i386:x64-32:nacl, auto. - + if is_python_64bit(): suffix = 'amd64' arch = 'i386:x86-64' @@ -408,7 +412,7 @@ def run_python_code_linux(pid, python_code, connect_debugger_tracing=False, show arch = 'i386' print('Attaching with arch: %s'% (arch,)) - + target_dll = os.path.join(filedir, 'attach_linux_%s.so' % suffix) target_dll = os.path.normpath(target_dll) if not os.path.exists(target_dll): @@ -443,7 +447,7 @@ def run_python_code_linux(pid, python_code, connect_debugger_tracing=False, show "--eval-command='call DoAttach(%s, \"%s\", %s)'" % ( is_debug, python_code, show_debug_info) ]) - + if connect_debugger_tracing: cmd.extend([ @@ -472,8 +476,96 @@ def run_python_code_linux(pid, python_code, connect_debugger_tracing=False, show return out, err +def run_python_code_mac(pid, python_code, connect_debugger_tracing=False, show_debug_info=0): + assert '\'' not in python_code, 'Having a single quote messes with our command.' + filedir = os.path.dirname(__file__) + + # Valid arguments for arch are i386, i386:x86-64, i386:x64-32, i8086, + # i386:intel, i386:x86-64:intel, i386:x64-32:intel, i386:nacl, + # i386:x86-64:nacl, i386:x64-32:nacl, auto. + + if is_python_64bit(): + suffix = 'x86_64.dylib' + arch = 'i386:x86-64' + else: + suffix = 'x86.dylib' + arch = 'i386' + + print('Attaching with arch: %s'% (arch,)) + + target_dll = os.path.join(filedir, 'attach_%s' % suffix) + target_dll = os.path.normpath(target_dll) + if not os.path.exists(target_dll): + raise RuntimeError('Could not find dll file to inject: %s' % target_dll) + + lldb_threads_settrace_file = os.path.join(filedir, 'linux', 'lldb_threads_settrace.py') + lldb_threads_settrace_file = os.path.normpath(lldb_threads_settrace_file) + if not os.path.exists(lldb_threads_settrace_file): + raise RuntimeError('Could not find file to settrace: %s' % lldb_threads_settrace_file) + + # Note: we currently don't support debug builds + is_debug = 0 + # Note that the space in the beginning of each line in the multi-line is important! + cmd = [ + 'lldb', + '--no-lldbinit', # Do not automatically parse any '.lldbinit' files. + # '--attach-pid', + # str(pid), + # '--arch', + # arch, + '--script-language', + 'Python' + # '--batch-silent', + ] + + + cmd.extend([ + "-o 'process attach --pid %d'"%pid, + "-o 'expr (void*)dlopen(\"%s\", 2);'" % target_dll, + "-o 'expr (int)hello();'", + "-o 'expr (int)DoAttach(%s, \"%s\", %s);'" % ( + is_debug, python_code, show_debug_info), + ]) + + + if connect_debugger_tracing: + cmd.extend([ + # "-o 'expr (int) SetSysTraceFunc(0, 0);'", + "-o 'command script import %s'" % (lldb_threads_settrace_file,), + ]) + + cmd.extend([ + "-o 'process continue'", + "-o 'process detach'", + # "-o 'script import os; os._exit(1)'", + ]) + + #print ' '.join(cmd) + + env = os.environ.copy() + # Remove the PYTHONPATH (if gdb has a builtin Python it could fail if we + # have the PYTHONPATH for a different python version or some forced encoding). + env.pop('PYTHONIOENCODING', None) + env.pop('PYTHONPATH', None) + print('Running: %s' % (' '.join(cmd))) + p = subprocess.Popen( + ' '.join(cmd), + shell=True, + env=env, + stdout=None, + stderr=None, + ) + print('Running lldb in target process.') + out, err = p.communicate() + print('stdout: %s' % (out,)) + print('stderr: %s' % (err,)) + return out, err + + if sys.platform == 'win32': run_python_code = run_python_code_windows +elif is_mac(): + run_python_code = run_python_code_mac else: run_python_code = run_python_code_linux diff --git a/python/helpers/pydev/pydevd_attach_to_process/attach_x86.dylib b/python/helpers/pydev/pydevd_attach_to_process/attach_x86.dylib new file mode 100755 index 000000000000..ffa9f1d9db2f Binary files /dev/null and b/python/helpers/pydev/pydevd_attach_to_process/attach_x86.dylib differ diff --git a/python/helpers/pydev/pydevd_attach_to_process/attach_x86_64.dylib b/python/helpers/pydev/pydevd_attach_to_process/attach_x86_64.dylib new file mode 100755 index 000000000000..60e07417295a Binary files /dev/null and b/python/helpers/pydev/pydevd_attach_to_process/attach_x86_64.dylib differ diff --git a/python/helpers/pydev/pydevd_attach_to_process/linux/Makefile b/python/helpers/pydev/pydevd_attach_to_process/linux/Makefile new file mode 100644 index 000000000000..aedfe16ee56c --- /dev/null +++ b/python/helpers/pydev/pydevd_attach_to_process/linux/Makefile @@ -0,0 +1,64 @@ +# Defaults which can be overridden. +OS = macosx +ARCH_X86 = x86 +ARCH_X86_64 = x86_64 + +CC=g++ +LD=libtool +CPPFLAGS = -I. +CFLAGS +=-fPIC -D_REENTRANT -nostartfiles + +ARCH_FLAG_X86 = -arch i386 +ARCH_FLAG_X86_64 = -arch x86_64 + +INSTALL_DIR_X86 = ../os/$(OS)/$(ARCH_X86) +INSTALL_DIR_X86_64 = ../os/$(OS)/$(ARCH_X86_64) +INSTALL_DIR_LINUX_X86 = ../os/$(LINUX)/$(ARCH_X86) +INSTALL_DIR_LINUX_X86_64 = ../os/$(LINUX)/$(ARCH_X86_64) + +ATTACH = attach_mac.so +ATTACH_NAME_FULL_X86 = $(INSTALL_DIR_X86)/attach_x86.dylib +ATTACH_NAME_FULL_X86_64 = $(INSTALL_DIR_X86_64)/attach_x86_64.dylib + +OBJS_ATTACH_X86 = attach_linux_$(ARCH_X86).o +OBJS_ATTACH_X86_64 = attach_linux_$(ARCH_X86_64).o + +OBJS_X86 = $(OBJS_ATTACH_X86) +OBJS_X86_64 = $(OBJS_ATTACH_X86_64) + +all: x86 x86_64 + +x86: $(ATTACH_NAME_FULL_X86) + +x86_64: $(ATTACH_NAME_FULL_X86_64) + +linux_x86: $(ATTACH_NAME_FULL_LINUX_X86) +linux_x86_64: $(ATTACH_NAME_FULL_LINUX_X86_64) + +rebuild: clean all + +$(ATTACH_NAME_FULL_X86): $(OBJS_ATTACH_X86) + mkdir -p $(INSTALL_DIR_X86) + $(CC) -dynamiclib $(ARCH_FLAG_X86) -o $(ATTACH_NAME_FULL_X86) $(OBJS_ATTACH_X86) -lc + +$(ATTACH_NAME_FULL_X86_64): $(OBJS_ATTACH_X86_64) + mkdir -p $(INSTALL_DIR_X86_64) + $(CC) -dynamiclib $(ARCH_FLAG_X86_64) -o $(ATTACH_NAME_FULL_X86_64) $(OBJS_ATTACH_X86_64) -lc + +$(ATTACH_NAME_FULL_LINUX_X86): $(OBJS_ATTACH_X86) + mkdir -p $(INSTALL_DIR_LINUX_X86) + $(CC) -m32 -g -shared -Wl,-soname,$(ATTACH) $(LDFLAGS) -o $(ATTACH_NAME_FULL_LINUX_X86) $(OBJS_ATTACH_X86) + +$(ATTACH_NAME_FULL_LINUX_X86_64): $(OBJS_ATTACH_X86_64) + mkdir -p $(INSTALL_DIR_LINUX_X86_64) + $(CC) -g -shared -Wl,-soname,$(ATTACH) $(LDFLAGS) -o $(ATTACH_NAME_FULL_LINUX_X86_64) $(OBJS_ATTACH_X86_64) + +attach_linux_$(ARCH_X86).o: attach_linux.c + $(CC) $(CFLAGS) $(ARCH_FLAG_X86) $(CPPFLAGS) -c -o $@ attach_linux.c + +attach_linux_$(ARCH_X86_64).o: attach_linux.c + $(CC) $(CFLAGS) $(ARCH_FLAG_X86_64) $(CPPFLAGS) -c -o $@ attach_linux.c + +clean : + $(RM) $(OBJS_X86) $(ATTACH_NAME_FULL_X86) + $(RM) $(OBJS_X86_64) $(ATTACH_NAME_FULL_X86_64) diff --git a/python/helpers/pydev/pydevd_attach_to_process/linux/attach_linux.c b/python/helpers/pydev/pydevd_attach_to_process/linux/attach_linux.c index f5e0c2a556ab..46b170f90404 100644 --- a/python/helpers/pydev/pydevd_attach_to_process/linux/attach_linux.c +++ b/python/helpers/pydev/pydevd_attach_to_process/linux/attach_linux.c @@ -12,19 +12,22 @@ // Exported function: hello(): Just to print something and check that we've been // able to connect. -extern int hello(void); +extern "C" int hello(void); int hello() { printf("Hello world!\n"); - void *hndl = dlsym (NULL, "PyGILState_Ensure"); + + void *main_hndl = dlopen(NULL, 0x2); + + void *hndl = dlsym (main_hndl, "PyGILState_Ensure"); if(hndl == NULL){ printf("NULL\n"); }else{ printf("Worked (found PyGILState_Ensure)!\n"); - } + printf("%d", GetPythonVersion()); @@ -36,8 +39,8 @@ int hello() // which are already running. // isDebug is pretty important! Must be true on python debug builds (python_d) // If this value is passed wrongly the program will crash. -extern int SetSysTraceFunc(bool showDebugInfo, bool isDebug); -extern int DoAttach(bool isDebug, const char *command, bool showDebugInfo); +extern "C" int SetSysTraceFunc(bool showDebugInfo, bool isDebug); +extern "C" int DoAttach(bool isDebug, const char *command, bool showDebugInfo); // Internal function to keep on the tracing int _PYDEVD_ExecWithGILSetSysStrace(bool showDebugInfo, bool isDebug); @@ -123,7 +126,8 @@ public: int DoAttach(bool isDebug, const char *command, bool showDebugInfo) { Py_IsInitialized isInitFunc; - *(void**)(&isInitFunc) = dlsym(0, "Py_IsInitialized"); + void *main_hndl = dlopen(NULL, 0x2); + *(void**)(&isInitFunc) = dlsym(main_hndl, "Py_IsInitialized"); CHECK_NULL(isInitFunc, "Py_IsInitialized not found.\n", 1); if(!isInitFunc()){ @@ -136,7 +140,7 @@ int DoAttach(bool isDebug, const char *command, bool showDebugInfo) PythonVersion version = GetPythonVersion(); PyInterpreterState_Head interpHeadFunc; - *(void**)(&interpHeadFunc) = dlsym(0, "PyInterpreterState_Head"); + *(void**)(&interpHeadFunc) = dlsym(main_hndl, "PyInterpreterState_Head"); CHECK_NULL(interpHeadFunc, "PyInterpreterState_Head not found.\n", 3); PyInterpreterState* head = interpHeadFunc(); @@ -148,15 +152,15 @@ int DoAttach(bool isDebug, const char *command, bool showDebugInfo) // have any more problems. PyGILState_Ensure pyGilStateEnsureFunc; - *(void**)(&pyGilStateEnsureFunc) = dlsym(0, "PyGILState_Ensure"); + *(void**)(&pyGilStateEnsureFunc) = dlsym(main_hndl, "PyGILState_Ensure"); CHECK_NULL(pyGilStateEnsureFunc, "PyGILState_Ensure not found.\n", 5); PyGILState_Release pyGilStateReleaseFunc; - *(void**)(&pyGilStateReleaseFunc) = dlsym(0, "PyGILState_Release"); + *(void**)(&pyGilStateReleaseFunc) = dlsym(main_hndl, "PyGILState_Release"); CHECK_NULL(pyGilStateReleaseFunc, "PyGILState_Release not found.\n", 6); PyRun_SimpleString pyRun_SimpleString; - *(void**)(&pyRun_SimpleString) = dlsym(0, "PyRun_SimpleString"); + *(void**)(&pyRun_SimpleString) = dlsym(main_hndl, "PyRun_SimpleString"); CHECK_NULL(pyRun_SimpleString, "PyRun_SimpleString not found.\n", 6); PyGILState_STATE pyGILState = pyGilStateEnsureFunc(); @@ -176,7 +180,8 @@ int SetSysTraceFunc(bool showDebugInfo, bool isDebug) printf("SetSysTraceFunc started.\n"); } Py_IsInitialized isInitFunc; - *(void**)(&isInitFunc) = dlsym(0, "Py_IsInitialized"); + void *main_hndl = dlopen(NULL, 0x2); + *(void**)(&isInitFunc) = dlsym(main_hndl, "Py_IsInitialized"); CHECK_NULL(isInitFunc, "Py_IsInitialized not found.\n", 1); if(!isInitFunc()){ @@ -189,18 +194,18 @@ int SetSysTraceFunc(bool showDebugInfo, bool isDebug) PythonVersion version = GetPythonVersion(); PyInterpreterState_Head interpHeadFunc; - *(void**)(&interpHeadFunc) = dlsym(0, "PyInterpreterState_Head"); + *(void**)(&interpHeadFunc) = dlsym(main_hndl, "PyInterpreterState_Head"); CHECK_NULL(interpHeadFunc, "PyInterpreterState_Head not found.\n", 3); PyInterpreterState* head = interpHeadFunc(); CHECK_NULL(head, "Interpreter not initialized.\n", 4); PyGILState_Ensure pyGilStateEnsureFunc; - *(void**)(&pyGilStateEnsureFunc) = dlsym(0, "PyGILState_Ensure"); + *(void**)(&pyGilStateEnsureFunc) = dlsym(main_hndl, "PyGILState_Ensure"); CHECK_NULL(pyGilStateEnsureFunc, "PyGILState_Ensure not found.\n", 5); PyGILState_Release pyGilStateReleaseFunc; - *(void**)(&pyGilStateReleaseFunc) = dlsym(0, "PyGILState_Release"); + *(void**)(&pyGilStateReleaseFunc) = dlsym(main_hndl, "PyGILState_Release"); CHECK_NULL(pyGilStateReleaseFunc, "PyGILState_Release not found.\n", 6); PyGILState_STATE pyGILState = pyGilStateEnsureFunc(); @@ -213,17 +218,19 @@ int SetSysTraceFunc(bool showDebugInfo, bool isDebug) int _PYDEVD_ExecWithGILSetSysStrace(bool showDebugInfo, bool isDebug){ PyBool_FromLong boolFromLongFunc; - *(void**)(&boolFromLongFunc) = dlsym(0, "PyBool_FromLong"); + void *main_hndl = dlopen(NULL, 0x2); + + *(void**)(&boolFromLongFunc) = dlsym(main_hndl, "PyBool_FromLong"); CHECK_NULL(boolFromLongFunc, "PyBool_FromLong not found.\n", 7); PyObject_HasAttrString pyHasAttrFunc; - *(void**)(&pyHasAttrFunc) = dlsym(0, "PyObject_HasAttrString"); + *(void**)(&pyHasAttrFunc) = dlsym(main_hndl, "PyObject_HasAttrString"); CHECK_NULL(pyHasAttrFunc, "PyObject_HasAttrString not found.\n", 7); //Important: we need a non-blocking import here: PyImport_ImportModule //could end up crashing (this makes us work only from 2.6 onwards). PyImport_ImportModuleNoBlock pyImportModFunc; - *(void**)(&pyImportModFunc) = dlsym(0, "PyImport_ImportModuleNoBlock"); + *(void**)(&pyImportModFunc) = dlsym(main_hndl, "PyImport_ImportModuleNoBlock"); CHECK_NULL(pyImportModFunc, "PyImport_ImportModuleNoBlock not found.\n", 8); @@ -232,14 +239,14 @@ int _PYDEVD_ExecWithGILSetSysStrace(bool showDebugInfo, bool isDebug){ if(!pyHasAttrFunc(pydevdTracingMod.ToPython(), "_original_settrace")){ if(showDebugInfo){ - printf("pydevd_tracing module has no _original_settrace! "); + printf("pydevd_tracing module has no _original_settrace!\n"); } return 8; } PyObject_GetAttrString pyGetAttr; - *(void**)(&pyGetAttr) = dlsym(0, "PyObject_GetAttrString"); + *(void**)(&pyGetAttr) = dlsym(main_hndl, "PyObject_GetAttrString"); CHECK_NULL(pyGetAttr, "PyObject_GetAttrString not found.\n", 8); auto PyObjectHolder settrace = PyObjectHolder(isDebug, pyGetAttr(pydevdTracingMod.ToPython(), "_original_settrace")); @@ -252,7 +259,7 @@ int _PYDEVD_ExecWithGILSetSysStrace(bool showDebugInfo, bool isDebug){ CHECK_NULL(getGlobalDebugger.ToPython(), "pydevd.GetGlobalDebugger null.\n", 11); PyObject_CallFunctionObjArgs call; - *(void**)(&call) = dlsym(0, "PyObject_CallFunctionObjArgs"); + *(void**)(&call) = dlsym(main_hndl, "PyObject_CallFunctionObjArgs"); CHECK_NULL(call, "PyObject_CallFunctionObjArgs not found.\n", 11); auto PyObjectHolder globalDbg = PyObjectHolder(isDebug, call(getGlobalDebugger.ToPython(), NULL)); @@ -270,7 +277,7 @@ int _PYDEVD_ExecWithGILSetSysStrace(bool showDebugInfo, bool isDebug){ DecRef(call(settrace.ToPython(), traceFunc.ToPython(), NULL), isDebug); if(showDebugInfo){ - printf("sys.settrace(pydevd.GetGlobalDebugger().trace_dispatch) worked."); + printf("sys.settrace(pydevd.GetGlobalDebugger().trace_dispatch) worked.\n"); } return 0; diff --git a/python/helpers/pydev/pydevd_attach_to_process/linux/lldb_threads_settrace.py b/python/helpers/pydev/pydevd_attach_to_process/linux/lldb_threads_settrace.py new file mode 100644 index 000000000000..b457e1547cd2 --- /dev/null +++ b/python/helpers/pydev/pydevd_attach_to_process/linux/lldb_threads_settrace.py @@ -0,0 +1,36 @@ +# This file is meant to be run inside lldb as a command after +# the attach_linux.dylib dll has already been loaded to settrace for all threads. +def __lldb_init_module(debugger, internal_dict): + # Command Initialization code goes here + print('Startup LLDB in Python!') + import lldb + + try: + show_debug_info = 1 + is_debug = 0 + target = debugger.GetSelectedTarget() + if target: + process = target.GetProcess() + if process: + for thread in process: + # Get the first frame + print('Thread %s, suspended %s\n'%(thread, thread.IsStopped())) + + process.SetSelectedThread(thread) + + if not thread.IsStopped(): + error = process.Stop() + print(error) + + if thread: + frame = thread.GetSelectedFrame() + if frame: + print('Will settrace in: %s' % (frame,)) + res = frame.EvaluateExpression("(int) SetSysTraceFunc(%s, %s)" % ( + show_debug_info, is_debug), lldb.eDynamicCanRunTarget) + error = res.GetError() + if error: + print(error) + thread.Resume() + except: + import traceback;traceback.print_exc() diff --git a/python/helpers/pydev/pydevd_attach_to_process/linux/python.h b/python/helpers/pydev/pydevd_attach_to_process/linux/python.h index f1a6d81434f9..93bfe6e413c9 100644 --- a/python/helpers/pydev/pydevd_attach_to_process/linux/python.h +++ b/python/helpers/pydev/pydevd_attach_to_process/linux/python.h @@ -548,7 +548,8 @@ typedef const char* (*GetVersionFunc) (); static PythonVersion GetPythonVersion() { GetVersionFunc versionFunc; - *(void**)(&versionFunc) = dlsym(0, "Py_GetVersion"); + void *main_hndl = dlopen(NULL, 0x2); + *(void**)(&versionFunc) = dlsym(main_hndl, "Py_GetVersion"); if(versionFunc != NULL) { const char* version = versionFunc(); if(version != NULL && strlen(version) >= 3 && version[1] == '.') { diff --git a/python/helpers/pydev/pydevd_plugin_utils.py b/python/helpers/pydev/pydevd_plugin_utils.py index f50bdf8425e8..4ccdfb44e3df 100644 --- a/python/helpers/pydev/pydevd_plugin_utils.py +++ b/python/helpers/pydev/pydevd_plugin_utils.py @@ -2,7 +2,7 @@ import types import pydev_log import pydevd_trace_api -from third_party.pluginbase import PluginBase +from _pydev_imps._pydev_pluginbase import PluginBase from pydevd_constants import * # @UnusedWildImport def load_plugins(package): diff --git a/python/helpers/pydev/pydevd_resolver.py b/python/helpers/pydev/pydevd_resolver.py index cc67cd043cec..a1c7a1dfe40e 100644 --- a/python/helpers/pydev/pydevd_resolver.py +++ b/python/helpers/pydev/pydevd_resolver.py @@ -18,7 +18,7 @@ from pydevd_constants import DictIterItems, xrange # Note: 300 is already a lot to see in the outline (after that the user should really use the shell to get things) # and this also means we'll pass less information to the client side (which makes debugging faster). -MAX_ITEMS_TO_HANDLE = 300 +MAX_ITEMS_TO_HANDLE = 300 TOO_LARGE_MSG = 'Too large to show contents. Max items to show: ' + str(MAX_ITEMS_TO_HANDLE) TOO_LARGE_ATTR = 'Unable to handle:' @@ -284,11 +284,11 @@ class TupleResolver: #to enumerate tuples and lists for item in var: d[format_str % i] = item i += 1 - + if i > MAX_ITEMS_TO_HANDLE: d[TOO_LARGE_ATTR] = TOO_LARGE_MSG break - + d['__len__'] = len(var) return d @@ -319,12 +319,12 @@ class SetResolver: for item in var: i+= 1 d[id(item)] = item - + if i > MAX_ITEMS_TO_HANDLE: d[TOO_LARGE_ATTR] = TOO_LARGE_MSG break - + d['__len__'] = len(var) return d diff --git a/python/helpers/pydev/runfiles.py b/python/helpers/pydev/runfiles.py index c2db611055c4..3e6c0d656b3a 100644 --- a/python/helpers/pydev/runfiles.py +++ b/python/helpers/pydev/runfiles.py @@ -190,6 +190,8 @@ def main(): argv.append('-p') argv.append('pydev_runfiles_pytest2') + if 'unittest' in sys.modules or 'unittest2' in sys.modules: + sys.stderr.write('pydev test runner error: imported unittest before running pytest.main\n') return pytest.main(argv) else: diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/MultiProcessDebugger.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/MultiProcessDebugger.java index d54bac8de85f..834916b0cf2b 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/pydev/MultiProcessDebugger.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/MultiProcessDebugger.java @@ -359,6 +359,13 @@ public class MultiProcessDebugger implements ProcessDebugger { } } + @Override + public void setBreakpointWithFuncName(String typeId, String file, int line, String condition, String logExpression, String funcName) { + for (ProcessDebugger d : allDebuggers()) { + d.setBreakpointWithFuncName(typeId, file, line, condition, logExpression, funcName); + } + } + @Override public void removeBreakpoint(String typeId, String file, int line) { for (ProcessDebugger d : allDebuggers()) { diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/ProcessDebugger.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/ProcessDebugger.java index 0fa5a5c21931..22344434252b 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/pydev/ProcessDebugger.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/ProcessDebugger.java @@ -78,6 +78,8 @@ public interface ProcessDebugger { void setBreakpoint(String typeId, String file, int line, String condition, String logExpression); + void setBreakpointWithFuncName(String typeId, String file, int line, String condition, String logExpression, String funcName); + void removeBreakpoint(String typeId, String file, int line); void addCloseListener(RemoteDebuggerCloseListener remoteDebuggerCloseListener); diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/RemoteDebugger.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/RemoteDebugger.java index 4ce7bdf67434..2d97a3fb1a7e 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/pydev/RemoteDebugger.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/RemoteDebugger.java @@ -436,6 +436,16 @@ public class RemoteDebugger implements ProcessDebugger { execute(command); } + @Override + public void setBreakpointWithFuncName(String typeId, String file, int line, String condition, String logExpression, String funcName) { + final SetBreakpointCommand command = + new SetBreakpointCommand(this, typeId, file, line, + condition, + logExpression, + funcName); + execute(command); + } + @Override public void removeBreakpoint(String typeId, String file, int line) { final RemoveBreakpointCommand command = diff --git a/python/src/com/jetbrains/python/debugger/PyDebugProcess.java b/python/src/com/jetbrains/python/debugger/PyDebugProcess.java index dbeb5dedfd7c..532e374d5c74 100644 --- a/python/src/com/jetbrains/python/debugger/PyDebugProcess.java +++ b/python/src/com/jetbrains/python/debugger/PyDebugProcess.java @@ -24,6 +24,7 @@ import com.intellij.execution.ui.ConsoleView; import com.intellij.execution.ui.ConsoleViewContentType; import com.intellij.execution.ui.ExecutionConsole; import com.intellij.openapi.application.ApplicationInfo; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.Document; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.fileEditor.FileDocumentManager; @@ -34,6 +35,9 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Pair; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiElement; +import com.intellij.psi.util.PsiTreeUtil; import com.intellij.remote.RemoteProcessHandlerBase; import com.intellij.util.ui.UIUtil; import com.intellij.xdebugger.*; @@ -44,9 +48,11 @@ import com.intellij.xdebugger.breakpoints.XLineBreakpoint; import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider; import com.intellij.xdebugger.frame.XValueChildrenList; import com.intellij.xdebugger.stepping.XSmartStepIntoHandler; +import com.jetbrains.python.PythonFileType; import com.jetbrains.python.console.PythonDebugLanguageConsoleView; import com.jetbrains.python.console.pydev.PydevCompletionVariant; import com.jetbrains.python.debugger.pydev.*; +import com.jetbrains.python.psi.PyFunction; import com.jetbrains.python.run.PythonProcessHandler; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -594,12 +600,36 @@ public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, Pr return frame; } + private String getFunctionName(final XLineBreakpoint breakpoint) { + final VirtualFile file = breakpoint.getSourcePosition().getFile(); + final Document document = FileDocumentManager.getInstance().getDocument(file); + final Project project = getSession().getProject(); + final String[] funcName = new String[1]; + if (document != null) { + if (file.getFileType() == PythonFileType.INSTANCE) { + ApplicationManager.getApplication().runReadAction(new Runnable() { + @Override + public void run() { + PsiElement psiElement = XDebuggerUtil.getInstance().findContextElement(file, breakpoint.getSourcePosition().getOffset(), + project, false); + PyFunction function = PsiTreeUtil.getParentOfType(psiElement, PyFunction.class); + if (function != null) { + funcName[0] = function.getName(); + } + } + }); + } + } + return funcName[0]; + } + public void addBreakpoint(final PySourcePosition position, final XLineBreakpoint breakpoint) { myRegisteredBreakpoints.put(position, breakpoint); if (isConnected()) { - myDebugger.setBreakpoint(breakpoint.getType().getId(), position.getFile(), position.getLine(), - breakpoint.getCondition(), - breakpoint.getLogExpression()); + myDebugger.setBreakpointWithFuncName(breakpoint.getType().getId(), position.getFile(), position.getLine(), + breakpoint.getCondition(), + breakpoint.getLogExpression(), + getFunctionName(breakpoint)); } } diff --git a/python/src/com/jetbrains/python/inspections/quickfix/AddEncodingQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/AddEncodingQuickFix.java index 8b65e34ad3ca..82f61cfb1b57 100644 --- a/python/src/com/jetbrains/python/inspections/quickfix/AddEncodingQuickFix.java +++ b/python/src/com/jetbrains/python/inspections/quickfix/AddEncodingQuickFix.java @@ -17,8 +17,15 @@ package com.jetbrains.python.inspections.quickfix; import com.intellij.codeInspection.LocalQuickFix; import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.LogicalPosition; +import com.intellij.openapi.fileEditor.FileEditor; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.fileEditor.TextEditor; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiComment; +import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.jetbrains.python.PyBundle; @@ -32,13 +39,13 @@ import org.jetbrains.annotations.NotNull; * add missing encoding declaration * # -*- coding: -*- * to the source file - * + *

* User: catherine */ public class AddEncodingQuickFix implements LocalQuickFix { - private String myDefaultEncoding; - private int myEncodingFormatIndex; + private final String myDefaultEncoding; + private final int myEncodingFormatIndex; public AddEncodingQuickFix(String defaultEncoding, int encodingFormatIndex) { myDefaultEncoding = defaultEncoding; @@ -57,14 +64,28 @@ public class AddEncodingQuickFix implements LocalQuickFix { } public void applyFix(@NotNull final Project project, @NotNull final ProblemDescriptor descriptor) { - PsiFile file = descriptor.getPsiElement().getContainingFile(); + final PsiElement element = descriptor.getPsiElement(); + final PsiFile file = element.getContainingFile(); if (file == null) return; PsiElement firstLine = file.getFirstChild(); if (firstLine instanceof PsiComment && firstLine.getText().startsWith("#!")) { firstLine = firstLine.getNextSibling(); } - PsiComment encodingLine = PyElementGenerator.getInstance(project).createFromText(LanguageLevel.forElement(file), PsiComment.class, - String.format(PyEncodingUtil.ENCODING_FORMAT_PATTERN[myEncodingFormatIndex], myDefaultEncoding)); - file.addBefore(encodingLine, firstLine); + final String commentText = String.format(PyEncodingUtil.ENCODING_FORMAT_PATTERN[myEncodingFormatIndex], myDefaultEncoding); + final PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project); + PsiComment encodingComment = elementGenerator.createFromText(LanguageLevel.forElement(file), PsiComment.class, commentText); + encodingComment = (PsiComment)file.addBefore(encodingComment, firstLine); + + final FileEditor fileEditor = FileEditorManager.getInstance(project).getSelectedEditor(element.getContainingFile().getVirtualFile()); + if (fileEditor instanceof TextEditor) { + final Editor editor = ((TextEditor)fileEditor).getEditor(); + final Document document = editor.getDocument(); + final int insertedLineNumber = document.getLineNumber(encodingComment.getTextOffset()); + if (insertedLineNumber == document.getLineCount() - 1) { + PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(document); + document.insertString(document.getLineEndOffset(insertedLineNumber), "\n"); + } + editor.getCaretModel().moveToLogicalPosition(new LogicalPosition(insertedLineNumber + 1, 0)); + } } } diff --git a/python/testData/inspections/AddEncodingInEmptyFile.py b/python/testData/inspections/AddEncodingInEmptyFile.py new file mode 100644 index 000000000000..376db9193fc4 --- /dev/null +++ b/python/testData/inspections/AddEncodingInEmptyFile.py @@ -0,0 +1 @@ + diff --git a/python/testData/inspections/AddEncodingInEmptyFile_after.py b/python/testData/inspections/AddEncodingInEmptyFile_after.py new file mode 100644 index 000000000000..76ea4ea7c759 --- /dev/null +++ b/python/testData/inspections/AddEncodingInEmptyFile_after.py @@ -0,0 +1,2 @@ +# coding=utf-8 + \ No newline at end of file diff --git a/python/testData/inspections/AddEncoding_after.py b/python/testData/inspections/AddEncoding_after.py index ef08f90863b7..974d8ff7f054 100644 --- a/python/testData/inspections/AddEncoding_after.py +++ b/python/testData/inspections/AddEncoding_after.py @@ -1,2 +1,2 @@ # coding=utf-8 -c = "string" +c = "string" diff --git a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java index e49b0eaee6c8..c7e8d138aa83 100644 --- a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java +++ b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java @@ -349,11 +349,18 @@ public class PyQuickFixTest extends PyTestCase { PyBundle.message("QFIX.add.super"), true, true); } - public void testAddEncoding() { //PY-491 + //PY-491, PY-13297 + public void testAddEncoding() { doInspectionTest("AddEncoding.py", PyMandatoryEncodingInspection.class, PyBundle.message("QFIX.add.encoding"), true, true); } + // PY-13297 + public void testAddEncodingInEmptyFile() { + doInspectionTest("AddEncodingInEmptyFile.py", PyMandatoryEncodingInspection.class, + PyBundle.message("QFIX.add.encoding"), true, true); + } + public void testRemoveDecorator() { //PY-3348 doInspectionTest("RemoveDecorator.py", PyDecoratorInspection.class, PyBundle.message("QFIX.remove.decorator"), true, true); diff --git a/xml/impl/src/com/intellij/codeInsight/template/emmet/XmlEmmetParser.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/XmlEmmetParser.java index 01ddc3ca563f..57dc97700a74 100644 --- a/xml/impl/src/com/intellij/codeInsight/template/emmet/XmlEmmetParser.java +++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/XmlEmmetParser.java @@ -313,7 +313,7 @@ public class XmlEmmetParser extends EmmetParser { } if (token == ZenCodingTokens.DOT || token == ZenCodingTokens.SHARP) { - final String name = token == ZenCodingTokens.DOT ? CLASS : ID; + final String name = getAttributeName(token); advance(); token = getToken(); final String value = getAttributeValueByToken(token); @@ -326,6 +326,10 @@ public class XmlEmmetParser extends EmmetParser { return null; } + protected String getAttributeName(ZenCodingToken delimiterToken) { + return delimiterToken == ZenCodingTokens.DOT ? CLASS : ID; + } + @Nullable private List> parseAttributeList() { final List> result = new ArrayList>(); diff --git a/xml/impl/src/com/intellij/codeInsight/template/emmet/generators/ZenCodingGenerator.java b/xml/impl/src/com/intellij/codeInsight/template/emmet/generators/ZenCodingGenerator.java index b15df8b5da2e..846eb867d844 100644 --- a/xml/impl/src/com/intellij/codeInsight/template/emmet/generators/ZenCodingGenerator.java +++ b/xml/impl/src/com/intellij/codeInsight/template/emmet/generators/ZenCodingGenerator.java @@ -61,8 +61,8 @@ public abstract class ZenCodingGenerator { public static List getInstances() { List generators = new ArrayList(); - generators.add(XmlZenCodingGeneratorImpl.INSTANCE); Collections.addAll(generators, EP_NAME.getExtensions()); + generators.add(XmlZenCodingGeneratorImpl.INSTANCE); return generators; } diff --git a/xml/impl/src/com/intellij/xml/util/documentation/HtmlDocumentationProvider.java b/xml/impl/src/com/intellij/xml/util/documentation/HtmlDocumentationProvider.java index 271fd5b6ff3d..1b416f37b92d 100644 --- a/xml/impl/src/com/intellij/xml/util/documentation/HtmlDocumentationProvider.java +++ b/xml/impl/src/com/intellij/xml/util/documentation/HtmlDocumentationProvider.java @@ -19,6 +19,7 @@ import com.intellij.lang.Language; import com.intellij.lang.LanguageDocumentation; import com.intellij.lang.documentation.DocumentationProvider; import com.intellij.lang.documentation.DocumentationUtil; +import com.intellij.openapi.project.DumbService; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiManager; @@ -273,7 +274,7 @@ public class HtmlDocumentationProvider implements DocumentationProvider { if (result== null && styleProvider !=null) { result = styleProvider.getDocumentationElementForLink(psiManager, link, context); } - if (result== null && ourScriptProvider !=null) { + if (result== null && ourScriptProvider != null && !DumbService.isDumb(psiManager.getProject())) { result = ourScriptProvider.getDocumentationElementForLink(psiManager, link,context); } return result; diff --git a/xml/xml-psi-impl/src/com/intellij/psi/impl/source/xml/XmlEntityRefImpl.java b/xml/xml-psi-impl/src/com/intellij/psi/impl/source/xml/XmlEntityRefImpl.java index b74834a28162..ee6248b6c1bf 100644 --- a/xml/xml-psi-impl/src/com/intellij/psi/impl/source/xml/XmlEntityRefImpl.java +++ b/xml/xml-psi-impl/src/com/intellij/psi/impl/source/xml/XmlEntityRefImpl.java @@ -16,7 +16,10 @@ package com.intellij.psi.impl.source.xml; import com.intellij.ide.highlighter.DTDFileType; +import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.RecursionManager; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry; import com.intellij.psi.search.PsiElementProcessor; @@ -49,8 +52,6 @@ public class XmlEntityRefImpl extends XmlElementImpl implements XmlEntityRef { super(XmlElementType.XML_ENTITY_REF); } - private static final Key EVALUATION_IN_PROCESS = Key.create("EvalKey"); - @Override public XmlEntityDecl resolve(PsiFile targetFile) { String text = getText(); @@ -77,12 +78,12 @@ public class XmlEntityRefImpl extends XmlElementImpl implements XmlEntityRef { if (value == null) { final PsiManager manager = element.getManager(); if(manager == null){ - return resolveEntity(targetElement, entityName, containingFile).getValue(); + return doResolveEntity(targetElement, entityName, containingFile).getValue(); } value = CachedValuesManager.getManager(manager.getProject()).createCachedValue(new CachedValueProvider() { @Override public Result compute() { - return resolveEntity(targetElement, entityName, containingFile); + return doResolveEntity(targetElement, entityName, containingFile); } }); @@ -95,95 +96,94 @@ public class XmlEntityRefImpl extends XmlElementImpl implements XmlEntityRef { private static final Key DISABLE_ENTITY_EXPAND = Key.create("disable.entity.expand"); - private static CachedValueProvider.Result resolveEntity(final PsiElement targetElement, final String entityName, PsiFile contextFile) { - if (targetElement.getUserData(EVALUATION_IN_PROCESS) != null) { - return new CachedValueProvider.Result(null,targetElement); - } - try { - targetElement.putUserData(EVALUATION_IN_PROCESS, ""); - final List deps = new ArrayList(); - final XmlEntityDecl[] result = {null}; + private static CachedValueProvider.Result doResolveEntity(final PsiElement targetElement, + final String entityName, + final PsiFile contextFile) { + return RecursionManager.doPreventingRecursion(targetElement, true, new Computable>() { + @Override + public CachedValueProvider.Result compute() { + final List deps = new ArrayList(); + final XmlEntityDecl[] result = {null}; - PsiElementProcessor processor = new PsiElementProcessor() { - @Override - public boolean execute(@NotNull PsiElement element) { - if (element instanceof XmlDoctype) { - XmlDoctype xmlDoctype = (XmlDoctype)element; - final String dtdUri = getDtdForEntity(xmlDoctype); - if (dtdUri != null) { - XmlFile file = XmlUtil.getContainingFile(element); - if (file == null) return true; - final XmlFile xmlFile = XmlUtil.findNamespace(file, dtdUri); - if (xmlFile != null) { - if (xmlFile != targetElement) { - deps.add(xmlFile); - if(!XmlUtil.processXmlElements(xmlFile, this,true)) return false; + PsiElementProcessor processor = new PsiElementProcessor() { + @Override + public boolean execute(@NotNull PsiElement element) { + if (element instanceof XmlDoctype) { + XmlDoctype xmlDoctype = (XmlDoctype)element; + final String dtdUri = getDtdForEntity(xmlDoctype); + if (dtdUri != null) { + XmlFile file = XmlUtil.getContainingFile(element); + if (file == null) return true; + final XmlFile xmlFile = XmlUtil.findNamespace(file, dtdUri); + if (xmlFile != null) { + if (xmlFile != targetElement) { + deps.add(xmlFile); + if (!XmlUtil.processXmlElements(xmlFile, this, true)) return false; + } } } + final XmlMarkupDecl markupDecl = xmlDoctype.getMarkupDecl(); + if (markupDecl != null) { + if (!XmlUtil.processXmlElements(markupDecl, this, true)) return false; + } } - final XmlMarkupDecl markupDecl = xmlDoctype.getMarkupDecl(); - if (markupDecl != null) { - if (!XmlUtil.processXmlElements(markupDecl, this, true)) return false; + else if (element instanceof XmlEntityDecl) { + XmlEntityDecl entityDecl = (XmlEntityDecl)element; + final String declName = entityDecl.getName(); + if (StringUtil.equals(declName, entityName)) { + result[0] = entityDecl; + return false; + } } + + return true; } - else if (element instanceof XmlEntityDecl) { - XmlEntityDecl entityDecl = (XmlEntityDecl)element; - final String declName = entityDecl.getName(); - if (declName.equals(entityName)) { - result[0] = entityDecl; - return false; - } + }; + FileViewProvider provider = targetElement.getContainingFile().getViewProvider(); + deps.add(provider.getPsi(provider.getBaseLanguage())); + + boolean notfound = PsiTreeUtil.processElements(targetElement, processor); + if (notfound) { + if (contextFile != targetElement && contextFile != null && contextFile.isValid()) { + notfound = PsiTreeUtil.processElements(contextFile, processor); } - - return true; } - }; - FileViewProvider provider = targetElement.getContainingFile().getViewProvider(); - deps.add(provider.getPsi(provider.getBaseLanguage())); - boolean notfound = PsiTreeUtil.processElements(targetElement, processor); - if (notfound) { - if (contextFile != targetElement && contextFile != null && contextFile.isValid()) { - notfound = PsiTreeUtil.processElements(contextFile, processor); - } - } + if (notfound && // no dtd ref at all + targetElement instanceof XmlFile && + deps.size() == 1 && + ((XmlFile)targetElement).getFileType() != DTDFileType.INSTANCE + ) { + XmlDocument document = ((XmlFile)targetElement).getDocument(); + final XmlTag rootTag = document != null ? document.getRootTag() : null; + XmlFile descriptorFile = null; - if (notfound && // no dtd ref at all - targetElement instanceof XmlFile && - deps.size() == 1 && - ((XmlFile)targetElement).getFileType() != DTDFileType.INSTANCE - ) { - XmlDocument document = ((XmlFile)targetElement).getDocument(); - final XmlTag rootTag = document.getRootTag(); - XmlFile descriptorFile = null; - - if (HtmlUtil.isHtml5Document(document)) { - descriptorFile = XmlUtil.findXmlFile((XmlFile)targetElement, Html5SchemaProvider.getCharsDtdLocation()); - } else if (rootTag != null && document.getUserData(DISABLE_ENTITY_EXPAND) == null) { - final XmlElementDescriptor descriptor = rootTag.getDescriptor(); + if (HtmlUtil.isHtml5Document(document)) { + descriptorFile = XmlUtil.findXmlFile((XmlFile)targetElement, Html5SchemaProvider.getCharsDtdLocation()); + } + else if (rootTag != null && document.getUserData(DISABLE_ENTITY_EXPAND) == null) { + final XmlElementDescriptor descriptor = rootTag.getDescriptor(); if (descriptor != null && !(descriptor instanceof AnyXmlElementDescriptor)) { PsiElement element = descriptor.getDeclaration(); - final PsiFile containingFile = element != null ? element.getContainingFile():null; - descriptorFile = containingFile instanceof XmlFile ? (XmlFile)containingFile:null; + final PsiFile containingFile = element != null ? element.getContainingFile() : null; + descriptorFile = containingFile instanceof XmlFile ? (XmlFile)containingFile : null; } + } + if (descriptorFile != null && + !descriptorFile.getName().equals(((XmlFile)targetElement).getName() + ".dtd")) { + deps.add(descriptorFile); + XmlUtil.processXmlElements( + descriptorFile, + processor, + true + ); + } } - if (descriptorFile != null && - !descriptorFile.getName().equals(((XmlFile)targetElement).getName() + ".dtd")) { - deps.add(descriptorFile); - XmlUtil.processXmlElements( - descriptorFile, - processor, - true - ); - } - } - return new CachedValueProvider.Result(result[0], ArrayUtil.toObjectArray(deps)); - } - finally { - targetElement.putUserData(EVALUATION_IN_PROCESS, null); - } + return new CachedValueProvider.Result(result[0], ArrayUtil.toObjectArray(deps)); + } + }); } private static String getDtdForEntity(XmlDoctype xmlDoctype) { @@ -214,7 +214,7 @@ public class XmlEntityRefImpl extends XmlElementImpl implements XmlEntityRef { @Override @NotNull public PsiReference[] getReferences() { - return ReferenceProvidersRegistry.getReferencesFromProviders(this,XmlEntityRef.class); + return ReferenceProvidersRegistry.getReferencesFromProviders(this); } @Override diff --git a/xml/xml-psi-impl/src/com/intellij/xml/XmlPsiManager.java b/xml/xml-psi-impl/src/com/intellij/xml/XmlPsiManager.java index 3525a00163a7..b5c24bbe070b 100644 --- a/xml/xml-psi-impl/src/com/intellij/xml/XmlPsiManager.java +++ b/xml/xml-psi-impl/src/com/intellij/xml/XmlPsiManager.java @@ -21,7 +21,10 @@ import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFileSystemItem; import com.intellij.psi.impl.PsiManagerImpl; +import com.intellij.psi.impl.PsiTreeChangeEventImpl; import com.intellij.psi.impl.PsiTreeChangePreprocessorBase; +import com.intellij.psi.xml.XmlFile; +import org.jetbrains.annotations.NotNull; /** * Created by fedorkorotkov. @@ -52,6 +55,12 @@ public class XmlPsiManager extends AbstractProjectComponent { // cause we display even attributes and tag values in structure view return !isXml; } + + @Override + public void treeChanged(@NotNull PsiTreeChangeEventImpl event) { + if (!(event.getFile() instanceof XmlFile)) return; + super.treeChanged(event); + } }; } }