From dabe43a7657b1d50ffe9d9eabd04c51becb33bab Mon Sep 17 00:00:00 2001 From: Konstantin Kolosovsky Date: Tue, 23 Sep 2014 19:45:55 +0400 Subject: [PATCH 01/34] svn: Refactored CertificateCallbackCase - removed logic that ensures not more than one "accept certificate" request per svn command line client call (such logic is unnecessary as currently new callback instances are created on each svn command line client call) --- .../org/jetbrains/idea/svn/commandLine/AuthCallbackCase.java | 1 - .../idea/svn/commandLine/CertificateCallbackCase.java | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/AuthCallbackCase.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/AuthCallbackCase.java index 77cca02a04fb..7edfaa491935 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/AuthCallbackCase.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/AuthCallbackCase.java @@ -25,7 +25,6 @@ import org.tmatesoft.svn.core.SVNURL; */ public abstract class AuthCallbackCase { protected final SVNURL myUrl; - protected boolean myTried = false; @NotNull protected final AuthenticationService myAuthenticationService; AuthCallbackCase(@NotNull AuthenticationService authenticationService, SVNURL url) { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CertificateCallbackCase.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CertificateCallbackCase.java index f58a775079ed..b3a4111d464a 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CertificateCallbackCase.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CertificateCallbackCase.java @@ -56,9 +56,8 @@ public class CertificateCallbackCase extends AuthCallbackCase { realm = serverUrl != null ? serverUrl.toString() : realm; } - if (!myTried && myAuthenticationService.acceptSSLServerCertificate(myUrl, realm)) { + if (myAuthenticationService.acceptSSLServerCertificate(myUrl, realm)) { accepted = true; - myTried = true; return true; } throw new SvnBindException("Server SSL certificate rejected"); From 37bf4753e700b16a4384c84a4ad25bb76eb7d793 Mon Sep 17 00:00:00 2001 From: Konstantin Kolosovsky Date: Tue, 23 Sep 2014 23:30:07 +0400 Subject: [PATCH 02/34] svn: Refactoring - extracted common logic for configuring HttpClient instances with IDEA proxy settings, removed duplication --- .../intellij/util/net/HttpConfigurable.java | 28 ++++++++++++++++++- .../plugins/github/api/GithubConnection.java | 17 +++++------ .../httpclient/NewBaseRepositoryImpl.java | 13 +++------ 3 files changed, 38 insertions(+), 20 deletions(-) diff --git a/platform/platform-api/src/com/intellij/util/net/HttpConfigurable.java b/platform/platform-api/src/com/intellij/util/net/HttpConfigurable.java index 757706127a38..a49746b82fb0 100644 --- a/platform/platform-api/src/com/intellij/util/net/HttpConfigurable.java +++ b/platform/platform-api/src/com/intellij/util/net/HttpConfigurable.java @@ -43,6 +43,11 @@ import com.intellij.util.xmlb.annotations.Transient; import gnu.trove.THashMap; import gnu.trove.THashSet; import gnu.trove.TObjectObjectProcedure; +import org.apache.http.HttpHost; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.client.config.RequestConfig; import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -52,7 +57,10 @@ import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.net.*; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; @State( name = "HttpConfigurable", @@ -426,6 +434,24 @@ public class HttpConfigurable implements PersistentStateComponent> getJvmPropertiesList(final boolean withAutodetection, @Nullable final URI uri) { final HttpConfigurable me = getInstance(); if (! me.USE_HTTP_PROXY && ! me.USE_PROXY_PAC) { diff --git a/plugins/github/src/org/jetbrains/plugins/github/api/GithubConnection.java b/plugins/github/src/org/jetbrains/plugins/github/api/GithubConnection.java index 6358b79e38ee..e61e96fed8e0 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/api/GithubConnection.java +++ b/plugins/github/src/org/jetbrains/plugins/github/api/GithubConnection.java @@ -145,10 +145,9 @@ public class GithubConnection { .setSocketTimeout(timeout); final HttpConfigurable proxySettings = HttpConfigurable.getInstance(); - if (auth.isUseProxy() && proxySettings.USE_HTTP_PROXY && !StringUtil.isEmptyOrSpaces(proxySettings.PROXY_HOST)) { - builder - .setProxy(new HttpHost(proxySettings.PROXY_HOST, proxySettings.PROXY_PORT)); - } + boolean useProxy = auth.isUseProxy() && proxySettings.USE_HTTP_PROXY && !StringUtil.isEmptyOrSpaces(proxySettings.PROXY_HOST); + + proxySettings.setProxy(builder, useProxy); return builder.build(); } @@ -172,12 +171,10 @@ public class GithubConnection { final HttpConfigurable proxySettings = HttpConfigurable.getInstance(); //proxySettings.USE_HTTP_PROXY - if (auth.isUseProxy() && proxySettings.USE_HTTP_PROXY && !StringUtil.isEmptyOrSpaces(proxySettings.PROXY_HOST)) { - if (proxySettings.PROXY_AUTHENTICATION) { - provider.setCredentials(new AuthScope(proxySettings.PROXY_HOST, proxySettings.PROXY_PORT), - new UsernamePasswordCredentials(proxySettings.PROXY_LOGIN, proxySettings.getPlainProxyPassword())); - } - } + boolean useProxy = auth.isUseProxy() && proxySettings.USE_HTTP_PROXY && !StringUtil.isEmptyOrSpaces(proxySettings.PROXY_HOST); + + proxySettings.setProxyCredentials(provider, useProxy); + return provider; } diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/impl/httpclient/NewBaseRepositoryImpl.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/impl/httpclient/NewBaseRepositoryImpl.java index 082d49812d5b..5f71422c1ec1 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/impl/httpclient/NewBaseRepositoryImpl.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/impl/httpclient/NewBaseRepositoryImpl.java @@ -89,24 +89,19 @@ public abstract class NewBaseRepositoryImpl extends BaseRepository { provider.setCredentials(BASIC_AUTH_SCOPE, new UsernamePasswordCredentials(getUsername(), getPassword())); } // Proxy authentication - HttpConfigurable proxySettings = HttpConfigurable.getInstance(); - if (isUseProxy() && proxySettings.PROXY_AUTHENTICATION) { - provider.setCredentials(new AuthScope(proxySettings.PROXY_HOST, proxySettings.PROXY_PORT), - new UsernamePasswordCredentials(proxySettings.PROXY_LOGIN, proxySettings.getPlainProxyPassword())); - } + HttpConfigurable.getInstance().setProxyCredentials(provider, isUseProxy()); + return provider; } @NotNull protected RequestConfig createRequestConfig() { TaskSettings tasksSettings = TaskSettings.getInstance(); - HttpConfigurable proxySettings = HttpConfigurable.getInstance(); RequestConfig.Builder builder = RequestConfig.custom() .setConnectTimeout(3000) .setSocketTimeout(tasksSettings.CONNECTION_TIMEOUT); - if (isUseProxy()) { - builder.setProxy(new HttpHost(proxySettings.PROXY_HOST, proxySettings.PROXY_PORT)); - } + HttpConfigurable.getInstance().setProxy(builder, isUseProxy()); + return builder.build(); } From 28a051b26ad66d7a1b7fae9cc102b6f335d3068c Mon Sep 17 00:00:00 2001 From: Sergey Simonchik Date: Wed, 24 Sep 2014 00:03:29 +0400 Subject: [PATCH 03/34] get rid of yellow code --- .../testframework/ui/TestsOutputConsolePrinter.java | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/platform/testRunner/src/com/intellij/execution/testframework/ui/TestsOutputConsolePrinter.java b/platform/testRunner/src/com/intellij/execution/testframework/ui/TestsOutputConsolePrinter.java index b69636bd643e..535f2d454999 100644 --- a/platform/testRunner/src/com/intellij/execution/testframework/ui/TestsOutputConsolePrinter.java +++ b/platform/testRunner/src/com/intellij/execution/testframework/ui/TestsOutputConsolePrinter.java @@ -42,15 +42,7 @@ public class TestsOutputConsolePrinter implements Printer, Disposable { }; public TestsOutputConsolePrinter(@NotNull BaseTestsOutputConsoleView testsOutputConsoleView, final TestConsoleProperties properties, final AbstractTestProxy unboundOutputRoot) { - this(testsOutputConsoleView.getConsole(), properties, unboundOutputRoot); - } - - /** - * @deprecated left for JSTestDriver compatibility - */ - @Deprecated - public TestsOutputConsolePrinter(final ConsoleView console, final TestConsoleProperties properties, final AbstractTestProxy unboundOutputRoot) { - myConsole = console; + myConsole = testsOutputConsoleView.getConsole(); myProperties = properties; myUnboundOutputRoot = unboundOutputRoot; myProperties.addListener(TestConsoleProperties.SCROLL_TO_STACK_TRACE, myPropertyListener); @@ -148,7 +140,7 @@ public class TestsOutputConsolePrinter implements Printer, Disposable { } public boolean canPause() { - return myCurrentTest != null ? myCurrentTest.isInProgress() : false; + return myCurrentTest != null && myCurrentTest.isInProgress(); } protected void scrollToBeginning() { From d5bd26727b85034a31ffd288b6e089bafa970800 Mon Sep 17 00:00:00 2001 From: Sergey Simonchik Date: Wed, 24 Sep 2014 00:38:26 +0400 Subject: [PATCH 04/34] EA-59241 - CME: SMTestProxy.calcHasErrors --- .../intellij/execution/testframework/sm/runner/SMTestProxy.java | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/SMTestProxy.java b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/SMTestProxy.java index d0850c9eef85..1099da296f14 100644 --- a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/SMTestProxy.java +++ b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/SMTestProxy.java @@ -296,6 +296,7 @@ public class SMTestProxy extends AbstractTestProxy { } public List getChildren() { + ApplicationManager.getApplication().assertIsDispatchThread(); return myChildren != null ? myChildren : Collections.emptyList(); } From 677de47c0bbd06431d6020be899bfb7fbc565846 Mon Sep 17 00:00:00 2001 From: Sergey Simonchik Date: Wed, 24 Sep 2014 01:08:50 +0400 Subject: [PATCH 05/34] SMTestProxy: javadoc added --- .../execution/testframework/sm/runner/SMTestProxy.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/SMTestProxy.java b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/SMTestProxy.java index 1099da296f14..6e24904e1d33 100644 --- a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/SMTestProxy.java +++ b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/SMTestProxy.java @@ -42,6 +42,9 @@ import java.util.Collections; import java.util.List; /** + * Represents a test result tree node. + * Not thread-safe. All methods should be called in EDT only. + * * @author: Roman Chernyatchik */ public class SMTestProxy extends AbstractTestProxy { From f6991e77fafbf04d6c4e437da95cbabee333b266 Mon Sep 17 00:00:00 2001 From: Konstantin Kolosovsky Date: Wed, 24 Sep 2014 01:50:15 +0400 Subject: [PATCH 06/34] svn: Implemented ability to check and trust server certificate (for https protocol) without utilizing SVNKit authentication lifecycle - request is issued to the server and special TrustManager performs necessary certificate checks --- .../util/net/ssl/CertificateManager.java | 2 +- .../util/net/ssl/ConfirmingTrustManager.java | 19 ++- .../util/resources/misc/registry.properties | 1 + .../idea/svn/auth/AuthenticationService.java | 115 +++++++++++++++- .../svn/auth/CertificateTrustManager.java | 123 ++++++++++++++++++ .../svn/auth/SvnAuthenticationManager.java | 42 +++--- .../commandLine/CertificateCallbackCase.java | 25 +++- .../commandLine/UsernamePasswordCallback.java | 7 +- plugins/svn4idea/svn4idea.iml | 1 + 9 files changed, 308 insertions(+), 27 deletions(-) create mode 100644 plugins/svn4idea/src/org/jetbrains/idea/svn/auth/CertificateTrustManager.java diff --git a/platform/platform-api/src/com/intellij/util/net/ssl/CertificateManager.java b/platform/platform-api/src/com/intellij/util/net/ssl/CertificateManager.java index ac20c3ece2d4..7af12fca2bf7 100644 --- a/platform/platform-api/src/com/intellij/util/net/ssl/CertificateManager.java +++ b/platform/platform-api/src/com/intellij/util/net/ssl/CertificateManager.java @@ -168,7 +168,7 @@ public class CertificateManager implements PersistentStateComponent() { + boolean accepted = askUser && CertificateManager.showAcceptDialog(new Callable() { @Override public DialogWrapper call() throws Exception { // TODO may be another kind of warning, if default trust store is missing @@ -137,7 +144,9 @@ public class ConfirmingTrustManager extends ClientOnlyTrustManager { }); if (accepted) { LOG.info("Certificate was accepted by user"); - myCustomManager.addCertificate(endPoint); + if (addToKeyStore) { + myCustomManager.addCertificate(endPoint); + } } return accepted; } diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index 56005e124799..6a2f92e17781 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -339,6 +339,7 @@ svn.use.terminal=false svn.use.incoming.optimization=false svn.executable.locale=C.UTF-8 svn.lowest.supported.format.for.command.line=1.7.0 +svn.use.svnkit.for.https.server.certificate.check=true completion.enable.relevant.method.chain.suggestions=false ide.mac.message.sheets.java.emulation=false diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/auth/AuthenticationService.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/auth/AuthenticationService.java index 076fd59611f3..a8a0d7ccdc60 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/auth/AuthenticationService.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/auth/AuthenticationService.java @@ -22,23 +22,39 @@ import com.intellij.openapi.ui.popup.util.PopupUtil; import com.intellij.openapi.util.Getter; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.registry.Registry; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.ArrayUtil; import com.intellij.util.WaitForProgressToShow; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.net.HttpConfigurable; +import com.intellij.util.net.ssl.CertificateManager; import com.intellij.util.proxy.CommonProxy; +import org.apache.http.client.HttpClient; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.config.SocketConfig; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.impl.client.HttpClients; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.SvnBundle; import org.jetbrains.idea.svn.SvnConfiguration; import org.jetbrains.idea.svn.SvnVcs; +import org.jetbrains.idea.svn.commandLine.SvnBindException; import org.jetbrains.idea.svn.dialogs.SimpleCredentialsDialog; import org.tmatesoft.svn.core.SVNURL; import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager; import org.tmatesoft.svn.core.auth.SVNAuthentication; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLHandshakeException; +import javax.net.ssl.TrustManager; import java.io.File; import java.io.IOException; import java.net.*; +import java.security.KeyManagementException; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -165,12 +181,106 @@ public class AuthenticationService { }); } - public boolean acceptSSLServerCertificate(final SVNURL repositoryUrl, final String realm) { + public boolean acceptSSLServerCertificate(@Nullable SVNURL repositoryUrl, final String realm) throws SvnBindException { if (repositoryUrl == null) { return false; } - return new SSLServerCertificateAuthenticator(this, repositoryUrl, realm).tryAuthenticate(); + boolean result; + + if (Registry.is("svn.use.svnkit.for.https.server.certificate.check")) { + result = new SSLServerCertificateAuthenticator(this, repositoryUrl, realm).tryAuthenticate(); + } + else { + HttpClient client = getClient(repositoryUrl); + + try { + client.execute(new HttpGet(repositoryUrl.toDecodedString())); + result = true; + } + catch (IOException e) { + throw new SvnBindException(fixMessage(e), e); + } + } + + return result; + } + + @Nullable + private static String fixMessage(@NotNull IOException e) { + String message = null; + + if (e instanceof SSLHandshakeException) { + if (StringUtil.containsIgnoreCase(e.getMessage(), "received fatal alert: handshake_failure")) { + message = e.getMessage() + ". Please try to specify SSL protocol manually - SSLv3 or TLSv1"; + } + else if (e.getCause() != null) { + // SSLHandshakeException.getMessage() could contain full type name of cause exception - for instance when cause is + // CertificateException. We just use cause exception message not to show exception type to the user. + message = e.getCause().getMessage(); + } + } + + return message; + } + + @NotNull + private HttpClient getClient(@NotNull SVNURL repositoryUrl) { + // TODO: Implement algorithm of resolving necessary enabled protocols (TLSv1 vs SSLv3) instead of just using values from Settings. + SSLContext sslContext = createSslContext(repositoryUrl); + List supportedProtocols = getSupportedSslProtocols(); + SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext, ArrayUtil.toStringArray(supportedProtocols), null, + SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); + HttpConfigurable httpSettings = HttpConfigurable.getInstance(); + + // TODO: Seems more suitable here to read timeout values directly from config file - without utilizing SvnAuthenticationManager. + return HttpClients.custom() + .setSSLSocketFactory(socketFactory) + .setDefaultSocketConfig(SocketConfig.custom().setSoTimeout(getAuthenticationManager().getReadTimeout(repositoryUrl)).build()) + .setDefaultRequestConfig( + httpSettings.setProxy(RequestConfig.custom(), haveDataForTmpConfig()) + .setConnectTimeout(getAuthenticationManager().getConnectTimeout(repositoryUrl)) + .build()) + .setDefaultCredentialsProvider(httpSettings.setProxyCredentials(new BasicCredentialsProvider(), haveDataForTmpConfig())) + .build(); + } + + @NotNull + private List getSupportedSslProtocols() { + List result = ContainerUtil.newArrayList(); + + switch (myConfiguration.getSslProtocols()) { + case sslv3: + result.add("SSLv3"); + break; + case tlsv1: + result.add("TLSv1"); + break; + case all: + break; + } + + return result; + } + + @NotNull + private SSLContext createSslContext(@NotNull SVNURL url) { + SSLContext result = CertificateManager.getSystemSslContext(); + TrustManager trustManager = new CertificateTrustManager(this, url); + + try { + result.init(CertificateManager.getDefaultKeyManagers(), new TrustManager[]{trustManager}, null); + } + catch (KeyManagementException e) { + LOG.error(e); + } + + return result; + } + + @NotNull + public SvnAuthenticationManager getAuthenticationManager() { + return isActive() ? myConfiguration.getInteractiveManager(myVcs) : myConfiguration.getPassiveAuthenticationManager(myVcs.getProject()); } public void clearPassiveCredentials(String realm, SVNURL repositoryUrl, boolean password) { @@ -186,6 +296,7 @@ public class AuthenticationService { } } + // TODO: rename public boolean haveDataForTmpConfig() { final HttpConfigurable instance = HttpConfigurable.getInstance(); return SvnConfiguration.getInstance(myVcs.getProject()).isIsUseDefaultProxy() && diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/auth/CertificateTrustManager.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/auth/CertificateTrustManager.java new file mode 100644 index 000000000000..c5bd5a54182b --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/auth/CertificateTrustManager.java @@ -0,0 +1,123 @@ +/* + * 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.idea.svn.auth; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.util.net.ssl.CertificateManager; +import com.intellij.util.net.ssl.ClientOnlyTrustManager; +import org.apache.http.client.utils.URIBuilder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.SvnConfiguration; +import org.tmatesoft.svn.core.SVNURL; +import org.tmatesoft.svn.core.auth.ISVNAuthenticationProvider; + +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +/** + * We assume that this trust manager is only used when server certificate is valid but untrusted. So we do not perform any additional + * validation here - just checking if certificate is trusted in several ways: + * - runtime cache + * - java trust store + * - "Server Certificates" settings + * - ask user + * + * @author Konstantin Kolosovsky. + */ +public class CertificateTrustManager extends ClientOnlyTrustManager { + + private static final Logger LOG = Logger.getInstance(CertificateTrustManager.class); + + @NotNull private final AuthenticationService myAuthenticationService; + @NotNull private final SVNURL myRepositoryUrl; + @NotNull private final String myRealm; + + public CertificateTrustManager(@NotNull AuthenticationService authenticationService, @NotNull SVNURL repositoryUrl) { + myAuthenticationService = authenticationService; + myRepositoryUrl = repositoryUrl; + myRealm = new URIBuilder() + .setScheme(repositoryUrl.getProtocol()) + .setHost(repositoryUrl.getHost()) + .setPort(repositoryUrl.getPort()) + .toString(); + } + + @Override + public void checkServerTrusted(@Nullable X509Certificate[] chain, String authType) throws CertificateException { + if (chain != null && chain.length > 0 && chain[0] != null) { + X509Certificate certificate = chain[0]; + + if (!checkPassive(certificate)) { + if (!isAcceptedByIdea(chain, authType)) { + checkActive(certificate); + } + + // no exceptions - so certificate is trusted - save to runtime cache + acknowledge(certificate); + } + } + } + + private boolean checkPassive(@NotNull X509Certificate certificate) throws CertificateEncodingException { + Object cachedData = SvnConfiguration.RUNTIME_AUTH_CACHE.getDataWithLowerCheck("svn.ssl.server", myRealm); + + return certificate.equals(cachedData); + } + + private static boolean isAcceptedByIdea(@NotNull X509Certificate[] chain, String authType) { + boolean result; + + try { + CertificateManager.getInstance().getTrustManager().checkServerTrusted(chain, authType, false, false); + result = true; + } + catch (CertificateException e) { + LOG.debug(e); + result = false; + } + + return result; + } + + private void checkActive(@NotNull X509Certificate certificate) throws CertificateException { + boolean isStorageEnabled = + myAuthenticationService.getAuthenticationManager().getHostOptionsProvider().getHostOptions(myRepositoryUrl).isAuthStorageEnabled(); + int result = myAuthenticationService.getAuthenticationManager().getInnerProvider() + .acceptServerAuthentication(myRepositoryUrl, myRealm, certificate, isStorageEnabled); + + switch (result) { + case ISVNAuthenticationProvider.ACCEPTED: + // TODO: --trust-server-cert command line key does not allow caching credentials permanently - so permanent caching should be + // TODO: separately implemented. Try utilizing "Server Certificates" settings for this. + case ISVNAuthenticationProvider.ACCEPTED_TEMPORARY: + // acknowledge() is called in checkServerTrusted() + break; + case ISVNAuthenticationProvider.REJECTED: + throw new CertificateException("Server SSL certificate rejected"); + } + } + + private void acknowledge(@NotNull X509Certificate certificate) throws CertificateEncodingException { + myAuthenticationService.getVcs().getSvnConfiguration().acknowledge("svn.ssl.server", myRealm, certificate); + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return CertificateManager.getInstance().getTrustManager().getAcceptedIssuers(); + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/auth/SvnAuthenticationManager.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/auth/SvnAuthenticationManager.java index e844374cf341..88f7f65502aa 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/auth/SvnAuthenticationManager.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/auth/SvnAuthenticationManager.java @@ -41,6 +41,7 @@ import com.intellij.util.proxy.CommonProxy; import com.intellij.util.ui.UIUtil; import com.trilead.ssh2.auth.AgentProxy; import org.intellij.lang.annotations.MagicConstant; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.*; import org.jetbrains.idea.svn.config.ProxyGroup; @@ -722,20 +723,20 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager im // 30 seconds private final static int DEFAULT_READ_TIMEOUT = 30 * 1000; - @Override - public int getReadTimeout(final SVNRepository repository) { - String protocol = repository.getLocation().getProtocol(); + public int getReadTimeout(@NotNull SVNURL url) { + String protocol = url.getProtocol(); if (HTTP.equals(protocol) || HTTPS.equals(protocol)) { - String host = repository.getLocation().getHost(); - String timeout = getServersPropertyIdea(host, "http-timeout"); - if (timeout != null) { - try { - return Integer.parseInt(timeout)*1000; - } catch (NumberFormatException nfe) { - // use default - } + String host = url.getHost(); + String timeout = getServersPropertyIdea(host, "http-timeout"); + if (timeout != null) { + try { + return Integer.parseInt(timeout) * 1000; } - return DEFAULT_READ_TIMEOUT; + catch (NumberFormatException nfe) { + // use default + } + } + return DEFAULT_READ_TIMEOUT; } if (SVN_SSH.equals(protocol)) { return (int)getConfig().getSshReadTimeout(); @@ -743,19 +744,28 @@ public class SvnAuthenticationManager extends DefaultSVNAuthenticationManager im return 0; } - @Override - public int getConnectTimeout(SVNRepository repository) { - String protocol = repository.getLocation().getProtocol(); + public int getConnectTimeout(@NotNull SVNURL url) { + String protocol = url.getProtocol(); if (SVN_SSH.equals(protocol)) { return (int)getConfig().getSshConnectionTimeout(); } - final int connectTimeout = super.getConnectTimeout(repository); + final int connectTimeout = getHostOptionsProvider().getHostOptions(url).getConnectTimeout(); if ((HTTP.equals(protocol) || HTTPS.equals(protocol)) && (connectTimeout <= 0)) { return DEFAULT_READ_TIMEOUT; } return connectTimeout; } + @Override + public int getReadTimeout(@NotNull SVNRepository repository) { + return getReadTimeout(repository.getLocation()); + } + + @Override + public int getConnectTimeout(@NotNull SVNRepository repository) { + return getConnectTimeout(repository.getLocation()); + } + // taken from default manager as is private String getServersPropertyIdea(String host, final String name) { final SVNCompositeConfigFile serversFile = getHostOptionsProvider().getServersFile(); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CertificateCallbackCase.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CertificateCallbackCase.java index b3a4111d464a..79d7a7ac3055 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CertificateCallbackCase.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CertificateCallbackCase.java @@ -15,6 +15,7 @@ */ package org.jetbrains.idea.svn.commandLine; +import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.idea.svn.auth.AuthenticationService; @@ -29,6 +30,7 @@ public class CertificateCallbackCase extends AuthCallbackCase { private static final String CERTIFICATE_ERROR = "Error validating server certificate for"; private static final String UNTRUSTED_SERVER_CERTIFICATE = "Server SSL certificate untrusted"; private static final String CERTIFICATE_VERIFICATION_FAILED = "certificate verification failed"; + private static final String CERTIFICATE_VERIFICATION_FAILED_ISSUER_NOT_TRUSTED = "certificate verification failed: issuer is not trusted"; private boolean accepted; @@ -38,11 +40,22 @@ public class CertificateCallbackCase extends AuthCallbackCase { @Override public boolean canHandle(String error) { + boolean useSvnKit = Registry.is("svn.use.svnkit.for.https.server.certificate.check"); + return error.startsWith(CERTIFICATE_ERROR) || // https one-way protocol untrusted server certificate error.contains(UNTRUSTED_SERVER_CERTIFICATE) || - // for instance, certificate issued for a different hostname, issuer is not trusted - for both 1.7 and 1.8 - error.contains(CERTIFICATE_VERIFICATION_FAILED); + // any certificate verification failure - for instance, "certificate issued for a different hostname" and/or + // "issuer is not trusted" - for both 1.7 and 1.8. + // SVNKit-based implementation persists credentials (by utilizing SVNKit api) emulating situation as if credentials were cached + // by Subversion. That is why we could process both untrusted and invalid certificate errors. + useSvnKit && isCertificateVerificationFailed(error) || + // valid but untrusted certificates - "issuer is not trusted" error - for both 1.7 and 1.8. + // Implementation not based on SVNKit does not persist credentials to emulate situation as if credentials were cached by + // Subversion. And in "--non-interactive" mode we could only make Subversion accept untrusted, but not invalid certificate. + // So we explicitly check that verification failure is only "issuer is not trusted". If certificate has some other failures, + // command will end with error. + !useSvnKit && isValidButUntrustedCertificate(error); } @Override @@ -86,6 +99,14 @@ public class CertificateCallbackCase extends AuthCallbackCase { } } + public static boolean isValidButUntrustedCertificate(@NotNull String error) { + return error.contains(CERTIFICATE_VERIFICATION_FAILED_ISSUER_NOT_TRUSTED); + } + + public static boolean isCertificateVerificationFailed(@NotNull String error) { + return error.contains(CERTIFICATE_VERIFICATION_FAILED); + } + private SVNURL getServerUrl(String realm) { SVNURL result = parseUrl(realm); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/UsernamePasswordCallback.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/UsernamePasswordCallback.java index ad908b213ab3..63930418e327 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/UsernamePasswordCallback.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/UsernamePasswordCallback.java @@ -15,6 +15,7 @@ */ package org.jetbrains.idea.svn.commandLine; +import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.idea.svn.auth.AuthenticationService; @@ -49,13 +50,17 @@ public class UsernamePasswordCallback extends AuthCallbackCase { @Override public boolean canHandle(String error) { + boolean useSvnKit = Registry.is("svn.use.svnkit.for.https.server.certificate.check"); + return // http/https protocol invalid credentials error.contains(AUTHENTICATION_FAILED_MESSAGE) || // svn protocol invalid credentials - messages could be "Can't get password", "Can't get username or password" error.contains(INVALID_CREDENTIALS_FOR_SVN_PROTOCOL) && error.contains(PASSWORD_STRING) || // http/https protocol, svn 1.7, non-interactive - error.contains(UNABLE_TO_CONNECT_MESSAGE) || + // we additionally check that error is not related to certificate verification - as CertificateCallbackCase could only handle + // untrusted certificates, but not invalid when useSvnKit = false + (error.contains(UNABLE_TO_CONNECT_MESSAGE) && (useSvnKit || !CertificateCallbackCase.isCertificateVerificationFailed(error))) || // http, svn 1.6, non-interactive StringUtil.containsIgnoreCase(error, COULD_NOT_AUTHENTICATE_TO_SERVER_MESSAGE); } diff --git a/plugins/svn4idea/svn4idea.iml b/plugins/svn4idea/svn4idea.iml index 20482fa75610..f88970ebf676 100644 --- a/plugins/svn4idea/svn4idea.iml +++ b/plugins/svn4idea/svn4idea.iml @@ -91,6 +91,7 @@ + From 913434d3167d56bfd91f874fb8d5fc7a1f0f8ce1 Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Wed, 24 Sep 2014 00:43:02 +0200 Subject: [PATCH 07/34] markup interface for remote run profiles --- .../configurations/RemoteRunProfile.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 platform/lang-api/src/com/intellij/execution/configurations/RemoteRunProfile.java diff --git a/platform/lang-api/src/com/intellij/execution/configurations/RemoteRunProfile.java b/platform/lang-api/src/com/intellij/execution/configurations/RemoteRunProfile.java new file mode 100644 index 000000000000..4ba1a8f8e976 --- /dev/null +++ b/platform/lang-api/src/com/intellij/execution/configurations/RemoteRunProfile.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 com.intellij.execution.configurations; + +/** + * Special interface for remote debug processes + * + * @author Konstantin Bulenkov + * @since 14 + */ +public interface RemoteRunProfile extends RunProfile { +} From 281fb3f2000c0995f409d73878d444b088a0f6ee Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Wed, 24 Sep 2014 00:43:46 +0200 Subject: [PATCH 08/34] mark java remote configuration as RemoteRunProfile --- .../com/intellij/execution/remote/RemoteConfiguration.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/java/execution/impl/src/com/intellij/execution/remote/RemoteConfiguration.java b/java/execution/impl/src/com/intellij/execution/remote/RemoteConfiguration.java index 57c84c9fa929..92a9c9ad7ad6 100644 --- a/java/execution/impl/src/com/intellij/execution/remote/RemoteConfiguration.java +++ b/java/execution/impl/src/com/intellij/execution/remote/RemoteConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,8 +41,8 @@ import org.jetbrains.annotations.NotNull; import java.util.Collection; -public class RemoteConfiguration extends ModuleBasedConfiguration implements - RunConfigurationWithSuppressedDefaultRunAction { +public class RemoteConfiguration extends ModuleBasedConfiguration + implements RunConfigurationWithSuppressedDefaultRunAction, RemoteRunProfile { @Override public void writeExternal(final Element element) throws WriteExternalException { From 6213a4d37c7edb8bf39fc556f9e0c399cf98a340 Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Wed, 24 Sep 2014 00:44:50 +0200 Subject: [PATCH 09/34] don't run children calculation of 'this' if under remote debug --- .../impl/ui/tree/nodes/XValueContainerNode.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/nodes/XValueContainerNode.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/nodes/XValueContainerNode.java index 0bbe12da965a..787a2cb5bb54 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/nodes/XValueContainerNode.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/nodes/XValueContainerNode.java @@ -15,6 +15,11 @@ */ package com.intellij.xdebugger.impl.ui.tree.nodes; +import com.intellij.execution.configurations.RemoteRunProfile; +import com.intellij.execution.runners.ExecutionEnvironment; +import com.intellij.ide.DataManager; +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.actionSystem.LangDataKeys; import com.intellij.openapi.util.registry.Registry; import com.intellij.ui.SimpleTextAttributes; import com.intellij.util.ObjectUtils; @@ -92,7 +97,7 @@ public abstract class XValueContainerNode createGroupNodes(List groups, @Nullable List prevNodes, From e12d7828c2f638b185bef31f63fa0d6737634279 Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Wed, 24 Sep 2014 01:48:32 +0200 Subject: [PATCH 10/34] set editor as parent disposable for evaluation hint's disposable parent --- .../com/intellij/xdebugger/impl/evaluate/quick/XValueHint.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/quick/XValueHint.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/quick/XValueHint.java index e1435be03d2b..589c8aed2abf 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/quick/XValueHint.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/quick/XValueHint.java @@ -28,6 +28,7 @@ import com.intellij.openapi.actionSystem.ShortcutSet; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.impl.EditorImpl; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.keymap.KeymapUtil; import com.intellij.openapi.project.DumbAwareAction; @@ -115,6 +116,7 @@ public class XValueHint extends AbstractValueHint { boolean result = super.showHint(component); if (result && getType() == ValueHintType.MOUSE_OVER_HINT) { myDisposable = Disposer.newDisposable(); + Disposer.register(((EditorImpl)getEditor()).getDisposable(), myDisposable); ShortcutSet shortcut = ActionManager.getInstance().getAction("ShowErrorDescription").getShortcutSet(); new DumbAwareAction() { @Override @@ -133,7 +135,6 @@ public class XValueHint extends AbstractValueHint { super.hideHint(); if (myDisposable != null) { Disposer.dispose(myDisposable); - } } From b41b041b06d8ef5a42101ba307b2c0316d7f2295 Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Wed, 24 Sep 2014 01:55:34 +0200 Subject: [PATCH 11/34] fix CME in inline debugger --- .../impl/ui/tree/nodes/XValueNodeImpl.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/nodes/XValueNodeImpl.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/nodes/XValueNodeImpl.java index 292cca541699..595322d761f1 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/nodes/XValueNodeImpl.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/nodes/XValueNodeImpl.java @@ -149,13 +149,14 @@ public class XValueNodeImpl extends XValueContainerNode implements XValu if (doc == null) return; int line = sourcePosition.getLine(); Pair key = Pair.create(file, line); - Set presentations = map.get(key); - if (presentations == null) { - presentations = new LinkedHashSet(); - map.put(key, presentations); - timestamps.put(file, doc.getModificationStamp()); - } + Set presentations = new LinkedHashSet(); + Set old = map.get(key); + map.put(key, presentations); + timestamps.put(file, doc.getModificationStamp()); presentations.add(XValueNodeImpl.this); + if (old != null) { + presentations.addAll(old); + } } }); } From faba1a6352676c5e461e94e80295c56ec9044451 Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Wed, 24 Sep 2014 02:13:39 +0200 Subject: [PATCH 12/34] clear variables cache when debug session ends --- .../src/com/intellij/xdebugger/impl/XDebugSessionImpl.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebugSessionImpl.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebugSessionImpl.java index ddc28745934a..a28c2cc2b42b 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebugSessionImpl.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebugSessionImpl.java @@ -61,6 +61,7 @@ import com.intellij.xdebugger.frame.XStackFrame; import com.intellij.xdebugger.frame.XSuspendContext; import com.intellij.xdebugger.frame.XValueMarkerProvider; import com.intellij.xdebugger.impl.breakpoints.*; +import com.intellij.xdebugger.impl.evaluate.XDebuggerEditorLinePainter; import com.intellij.xdebugger.impl.evaluate.quick.common.ValueLookupManager; import com.intellij.xdebugger.impl.frame.XValueMarkers; import com.intellij.xdebugger.impl.settings.XDebuggerSettingsManager; @@ -891,6 +892,7 @@ public class XDebugSessionImpl implements XDebugSession { myStopped = true; myDebuggerManager.removeSession(this); myDispatcher.getMulticaster().sessionStopped(); + myProject.putUserData(XDebuggerEditorLinePainter.CACHE, null); } public boolean isInactiveSlaveBreakpoint(final XBreakpoint breakpoint) { From bf00fdd33678fffad7e426b63c2aee77d24f3a95 Mon Sep 17 00:00:00 2001 From: nik Date: Wed, 24 Sep 2014 09:43:20 +0400 Subject: [PATCH 13/34] Google App Engine: corrected test SDK for Windows --- .../testData/sdk/1.3.7/bin/{appcfg.bat => appcfg.cmd} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename plugins/google-app-engine/testData/sdk/1.3.7/bin/{appcfg.bat => appcfg.cmd} (100%) diff --git a/plugins/google-app-engine/testData/sdk/1.3.7/bin/appcfg.bat b/plugins/google-app-engine/testData/sdk/1.3.7/bin/appcfg.cmd similarity index 100% rename from plugins/google-app-engine/testData/sdk/1.3.7/bin/appcfg.bat rename to plugins/google-app-engine/testData/sdk/1.3.7/bin/appcfg.cmd From 27c7a0c504394f009e662f737a4f0488edf9bcea Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Wed, 24 Sep 2014 09:14:56 +0200 Subject: [PATCH 14/34] fix testOverrideStripTrailingSpaces test --- .../intellij/openapi/editor/impl/StripTrailingSpacesTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/StripTrailingSpacesTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/StripTrailingSpacesTest.java index 7ac5f7bb2a7f..5189252e3530 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/StripTrailingSpacesTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/editor/impl/StripTrailingSpacesTest.java @@ -40,6 +40,7 @@ public class StripTrailingSpacesTest extends LightPlatformCodeInsightTestCase { super.setUp(); EditorSettingsExternalizable settings = EditorSettingsExternalizable.getInstance(); oldSettings = settings.getState(); + settings.loadState(new EditorSettingsExternalizable.OptionSet()); settings.setStripTrailingSpaces(EditorSettingsExternalizable.STRIP_TRAILING_SPACES_CHANGED); settings.setVirtualSpace(false); } From 0c0b29bf42b68deda55818c6766a58ec2816fe68 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Wed, 24 Sep 2014 09:43:59 +0200 Subject: [PATCH 15/34] Cleanup (better assertions, warnings) --- .../FileDocumentManagerImplTest.java | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/fileEditor/FileDocumentManagerImplTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/fileEditor/FileDocumentManagerImplTest.java index a5cdcbef8439..4acf5edb5892 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/fileEditor/FileDocumentManagerImplTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/fileEditor/FileDocumentManagerImplTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,6 +34,7 @@ import com.intellij.psi.PsiFile; import com.intellij.testFramework.PlatformLangTestCase; import com.intellij.util.IncorrectOperationException; import com.intellij.util.LocalTimeCounter; +import com.intellij.util.ObjectUtils; import com.intellij.util.PairProcessor; import org.jetbrains.annotations.NotNull; @@ -41,12 +42,13 @@ import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.OutputStream; -import java.util.Arrays; + +import static org.junit.Assert.assertNotEquals; public class FileDocumentManagerImplTest extends PlatformLangTestCase { private FileDocumentManagerImpl myDocumentManager; + private Boolean myReloadFromDisk; - public Boolean myReloadFromDisk; @Override protected void setUp() throws Exception { super.setUp(); @@ -148,7 +150,7 @@ public class FileDocumentManagerImplTest extends PlatformLangTestCase { final Document[] unsavedDocuments = myDocumentManager.getUnsavedDocuments(); assertEquals(1, unsavedDocuments.length); assertSame(document, unsavedDocuments[0]); - assertTrue(Arrays.equals("test".getBytes("UTF-8"), file.contentsToByteArray())); + assertEquals("test", new String(file.contentsToByteArray(), "UTF-8")); } public void testGetUnsavedDocuments_afterSaveAllDocuments() throws Exception { @@ -216,7 +218,7 @@ public class FileDocumentManagerImplTest extends PlatformLangTestCase { final Document[] unsavedDocuments = myDocumentManager.getUnsavedDocuments(); assertEquals(1, unsavedDocuments.length); assertSame(document, unsavedDocuments[0]); - assertTrue(Arrays.equals("test".getBytes("UTF-8"), file.contentsToByteArray())); + assertEquals("test", new String(file.contentsToByteArray(), "UTF-8")); } finally { myDocumentManager.dropAllUnsavedDocuments(); @@ -231,7 +233,7 @@ public class FileDocumentManagerImplTest extends PlatformLangTestCase { WriteCommandAction.runWriteCommandAction(myProject, new Runnable() { @Override public void run() { - myDocumentManager.getDocument(file).insertString(0, "xxx"); + ObjectUtils.assertNotNull(myDocumentManager.getDocument(file)).insertString(0, "xxx"); } }); @@ -252,7 +254,7 @@ public class FileDocumentManagerImplTest extends PlatformLangTestCase { WriteCommandAction.runWriteCommandAction(myProject, new Runnable() { @Override public void run() { - myDocumentManager.getDocument(file).insertString(0, "xxx"); + ObjectUtils.assertNotNull(myDocumentManager.getDocument(file)).insertString(0, "xxx"); } }); @@ -294,7 +296,7 @@ public class FileDocumentManagerImplTest extends PlatformLangTestCase { myDocumentManager.saveDocument(document); assertTrue(stamp != file.getModificationStamp()); assertEquals(document.getModificationStamp(), file.getModificationStamp()); - assertTrue(Arrays.equals("xxx test".getBytes("UTF-8"), file.contentsToByteArray())); + assertEquals("xxx test", new String(file.contentsToByteArray(), "UTF-8")); } public void testSaveAllDocuments_DocumentWasChanged() throws Exception { @@ -309,10 +311,9 @@ public class FileDocumentManagerImplTest extends PlatformLangTestCase { } }); - myDocumentManager.saveAllDocuments(); - assertTrue(stamp != file.getModificationStamp()); - assertTrue(Arrays.equals("xxx test".getBytes("UTF-8"), file.contentsToByteArray())); + assertNotEquals(stamp, file.getModificationStamp()); + assertEquals("xxx test", new String(file.contentsToByteArray(), "UTF-8")); } public void testGetFile() throws Exception { @@ -341,7 +342,7 @@ public class FileDocumentManagerImplTest extends PlatformLangTestCase { }); myDocumentManager.saveAllDocuments(); - assertTrue(Arrays.equals("xxx test\rtest".getBytes("UTF-8"), file.contentsToByteArray())); + assertEquals("xxx test\rtest", new String(file.contentsToByteArray(), "UTF-8")); } public void testContentChanged_noDocument() throws Exception { @@ -531,7 +532,7 @@ public class FileDocumentManagerImplTest extends PlatformLangTestCase { assertEquals("old test", document.getText()); assertEquals(file.getModificationStamp(), document.getModificationStamp()); - assertTrue(Arrays.equals("old test".getBytes("UTF-8"), file.contentsToByteArray())); + assertEquals("old test", new String(file.contentsToByteArray(), "UTF-8")); assertEquals(documentStamp, document.getModificationStamp()); } @@ -631,6 +632,7 @@ public class FileDocumentManagerImplTest extends PlatformLangTestCase { assertNotNull(virtualFile.getPath(), original); final PsiFile file = getPsiFile(original); + assertNotNull(file); FileDocumentManagerListener saveListener = new FileDocumentManagerAdapter() { @Override public void beforeDocumentSaving(@NotNull Document document) { @@ -650,6 +652,7 @@ public class FileDocumentManagerImplTest extends PlatformLangTestCase { }; getProject().getMessageBus().connect(getTestRootDisposable()).subscribe(AppTopics.FILE_DOCUMENT_SYNC, saveListener); final Document document = PsiDocumentManager.getInstance(getProject()).getDocument(file); + assertNotNull(document); WriteCommandAction.runWriteCommandAction(getProject(), new Runnable() { @Override public void run() { From ec603b4814c3636be44adc03fb2108bae16cea88 Mon Sep 17 00:00:00 2001 From: Rustam Vishnyakov Date: Wed, 24 Sep 2014 11:44:11 +0400 Subject: [PATCH 16/34] File indent options provider API: a flag to skip a provider on full reformat, logging --- .../codeStyle/TabPostFormatProcessor.java | 2 +- .../reformatFileSupported_after.java | 8 +++ .../reformatFileSupported_before.java | 8 +++ .../indentProvider/reformatFile_after.java | 12 ++--- .../reformatTextFullSupported_after.java | 8 +++ .../reformatTextFullSupported_before.java | 8 +++ .../reformatTextRange_after.java | 8 +++ .../reformatTextRange_before.java | 8 +++ .../indentProvider/reformatText_after.java | 12 ++--- .../FileIndentProviderTest.java | 40 +++++++++++++++ .../psi/codeStyle/CodeStyleSettings.java | 49 +++++++++++++++++-- .../codeStyle/CodeStyleSettingsManager.java | 2 +- .../codeStyle/FileIndentOptionsProvider.java | 8 +++ .../source/codeStyle/CodeFormatterFacade.java | 4 +- 14 files changed, 156 insertions(+), 21 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/indentProvider/reformatFileSupported_after.java create mode 100644 java/java-tests/testData/codeInsight/indentProvider/reformatFileSupported_before.java create mode 100644 java/java-tests/testData/codeInsight/indentProvider/reformatTextFullSupported_after.java create mode 100644 java/java-tests/testData/codeInsight/indentProvider/reformatTextFullSupported_before.java create mode 100644 java/java-tests/testData/codeInsight/indentProvider/reformatTextRange_after.java create mode 100644 java/java-tests/testData/codeInsight/indentProvider/reformatTextRange_before.java diff --git a/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/TabPostFormatProcessor.java b/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/TabPostFormatProcessor.java index 495155145a5e..31358cdc045f 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/TabPostFormatProcessor.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/TabPostFormatProcessor.java @@ -72,7 +72,7 @@ public class TabPostFormatProcessor implements PostFormatProcessor { if (!source.isValid()) return range; PsiFile file = source.getContainingFile(); - CommonCodeStyleSettings.IndentOptions indentOptions = settings.getIndentOptionsByFile(file); + CommonCodeStyleSettings.IndentOptions indentOptions = settings.getIndentOptionsByFile(file, range); boolean useTabs = indentOptions.USE_TAB_CHARACTER; boolean smartTabs = indentOptions.SMART_TABS; diff --git a/java/java-tests/testData/codeInsight/indentProvider/reformatFileSupported_after.java b/java/java-tests/testData/codeInsight/indentProvider/reformatFileSupported_after.java new file mode 100644 index 000000000000..2c4a5a27aa56 --- /dev/null +++ b/java/java-tests/testData/codeInsight/indentProvider/reformatFileSupported_after.java @@ -0,0 +1,8 @@ +class A { + private void foo(boolean b) { + int x; + if (b) { + System.out.println(x); + } + } +} diff --git a/java/java-tests/testData/codeInsight/indentProvider/reformatFileSupported_before.java b/java/java-tests/testData/codeInsight/indentProvider/reformatFileSupported_before.java new file mode 100644 index 000000000000..288ed2b2cc3e --- /dev/null +++ b/java/java-tests/testData/codeInsight/indentProvider/reformatFileSupported_before.java @@ -0,0 +1,8 @@ +class A { + private void foo(boolean b) { + int x; + if (b) { + System.out.println(x); + } + } +} diff --git a/java/java-tests/testData/codeInsight/indentProvider/reformatFile_after.java b/java/java-tests/testData/codeInsight/indentProvider/reformatFile_after.java index 2c4a5a27aa56..4dca5f373a24 100644 --- a/java/java-tests/testData/codeInsight/indentProvider/reformatFile_after.java +++ b/java/java-tests/testData/codeInsight/indentProvider/reformatFile_after.java @@ -1,8 +1,8 @@ class A { - private void foo(boolean b) { - int x; - if (b) { - System.out.println(x); - } - } + private void foo(boolean b) { + int x; + if (b) { + System.out.println(x); + } + } } diff --git a/java/java-tests/testData/codeInsight/indentProvider/reformatTextFullSupported_after.java b/java/java-tests/testData/codeInsight/indentProvider/reformatTextFullSupported_after.java new file mode 100644 index 000000000000..2c4a5a27aa56 --- /dev/null +++ b/java/java-tests/testData/codeInsight/indentProvider/reformatTextFullSupported_after.java @@ -0,0 +1,8 @@ +class A { + private void foo(boolean b) { + int x; + if (b) { + System.out.println(x); + } + } +} diff --git a/java/java-tests/testData/codeInsight/indentProvider/reformatTextFullSupported_before.java b/java/java-tests/testData/codeInsight/indentProvider/reformatTextFullSupported_before.java new file mode 100644 index 000000000000..288ed2b2cc3e --- /dev/null +++ b/java/java-tests/testData/codeInsight/indentProvider/reformatTextFullSupported_before.java @@ -0,0 +1,8 @@ +class A { + private void foo(boolean b) { + int x; + if (b) { + System.out.println(x); + } + } +} diff --git a/java/java-tests/testData/codeInsight/indentProvider/reformatTextRange_after.java b/java/java-tests/testData/codeInsight/indentProvider/reformatTextRange_after.java new file mode 100644 index 000000000000..2c4a5a27aa56 --- /dev/null +++ b/java/java-tests/testData/codeInsight/indentProvider/reformatTextRange_after.java @@ -0,0 +1,8 @@ +class A { + private void foo(boolean b) { + int x; + if (b) { + System.out.println(x); + } + } +} diff --git a/java/java-tests/testData/codeInsight/indentProvider/reformatTextRange_before.java b/java/java-tests/testData/codeInsight/indentProvider/reformatTextRange_before.java new file mode 100644 index 000000000000..288ed2b2cc3e --- /dev/null +++ b/java/java-tests/testData/codeInsight/indentProvider/reformatTextRange_before.java @@ -0,0 +1,8 @@ +class A { + private void foo(boolean b) { + int x; + if (b) { + System.out.println(x); + } + } +} diff --git a/java/java-tests/testData/codeInsight/indentProvider/reformatText_after.java b/java/java-tests/testData/codeInsight/indentProvider/reformatText_after.java index 2c4a5a27aa56..4dca5f373a24 100644 --- a/java/java-tests/testData/codeInsight/indentProvider/reformatText_after.java +++ b/java/java-tests/testData/codeInsight/indentProvider/reformatText_after.java @@ -1,8 +1,8 @@ class A { - private void foo(boolean b) { - int x; - if (b) { - System.out.println(x); - } - } + private void foo(boolean b) { + int x; + if (b) { + System.out.println(x); + } + } } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/indentOptionsProvider/FileIndentProviderTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/indentOptionsProvider/FileIndentProviderTest.java index c542f1a5a4e0..9ba42b9aaaa8 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/indentOptionsProvider/FileIndentProviderTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/indentOptionsProvider/FileIndentProviderTest.java @@ -38,6 +38,7 @@ public class FileIndentProviderTest extends LightPlatformCodeInsightFixtureTestC private final static FileIndentOptionsProvider TEST_FILE_INDENT_OPTIONS_PROVIDER = new TestIndentOptionsProvider(); private static CommonCodeStyleSettings.IndentOptions myTestIndentOptions; + private static boolean myUseOnFullReformat; @Override protected void setUp() throws Exception { @@ -54,6 +55,7 @@ public class FileIndentProviderTest extends LightPlatformCodeInsightFixtureTestC Extensions.getRootArea().getExtensionPoint(FileIndentOptionsProvider.EP_NAME); extensionPoint.unregisterExtension(TEST_FILE_INDENT_OPTIONS_PROVIDER); myTestIndentOptions = null; + myUseOnFullReformat = false; super.tearDown(); } @@ -80,6 +82,11 @@ public class FileIndentProviderTest extends LightPlatformCodeInsightFixtureTestC public CommonCodeStyleSettings.IndentOptions getIndentOptions(@NotNull PsiFile file) { return myTestIndentOptions; } + + @Override + public boolean useOnFullReformat() { + return myUseOnFullReformat; + } } public void testTypeEnter() { @@ -111,6 +118,16 @@ public class FileIndentProviderTest extends LightPlatformCodeInsightFixtureTestC myFixture.checkResultByFile(getTestName(true) + "_after.java"); } + public void testReformatFileSupported() { + myUseOnFullReformat = true; + myTestIndentOptions.INDENT_SIZE = 3; + myTestIndentOptions.TAB_SIZE = 2; + myTestIndentOptions.USE_TAB_CHARACTER = true; + PsiFile file = myFixture.configureByFile(getTestName(true) + "_before.java"); + CodeStyleManager.getInstance(getProject()).reformat(file); + myFixture.checkResultByFile(getTestName(true) + "_after.java"); + } + public void testReformatText() { myTestIndentOptions.INDENT_SIZE = 3; myTestIndentOptions.TAB_SIZE = 2; @@ -119,4 +136,27 @@ public class FileIndentProviderTest extends LightPlatformCodeInsightFixtureTestC CodeStyleManager.getInstance(getProject()).reformatText(file, 0, file.getTextRange().getEndOffset()); myFixture.checkResultByFile(getTestName(true) + "_after.java"); } + + /** + * Reformat using indent provider if a part of the file is selected. + */ + public void testReformatTextRange() { + myTestIndentOptions.INDENT_SIZE = 3; + myTestIndentOptions.TAB_SIZE = 2; + myTestIndentOptions.USE_TAB_CHARACTER = true; + PsiFile file = myFixture.configureByFile(getTestName(true) + "_before.java"); + // Just any range smaller than the file + CodeStyleManager.getInstance(getProject()).reformatText(file, 6, file.getTextRange().getEndOffset() - 1); + myFixture.checkResultByFile(getTestName(true) + "_after.java"); + } + + public void testReformatTextFullSupported() { + myUseOnFullReformat = true; + myTestIndentOptions.INDENT_SIZE = 3; + myTestIndentOptions.TAB_SIZE = 2; + myTestIndentOptions.USE_TAB_CHARACTER = true; + PsiFile file = myFixture.configureByFile(getTestName(true) + "_before.java"); + CodeStyleManager.getInstance(getProject()).reformatText(file, 0, file.getTextRange().getEndOffset()); + myFixture.checkResultByFile(getTestName(true) + "_after.java"); + } } diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettings.java b/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettings.java index ae9f2f960640..eecdc4b6e937 100644 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettings.java +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettings.java @@ -663,26 +663,65 @@ public class CodeStyleSettings extends CommonCodeStyleSettings implements Clonea @NotNull public IndentOptions getIndentOptionsByFile(@Nullable PsiFile file) { - return getIndentOptionsByFile(file, false); + return getIndentOptionsByFile(file, null); } @NotNull - public IndentOptions getIndentOptionsByFile(@Nullable PsiFile file, boolean ignoreDocOptions) { + public IndentOptions getIndentOptionsByFile(@Nullable PsiFile file, @Nullable TextRange formatRange) { + return getIndentOptionsByFile(file, formatRange, false); + } + + /** + * Retrieves indent options for PSI file from an associated document or (if not defined in the document) from file indent options + * providers. + * @param file The PSI file to retrieve options for. + * @param formatRange The text range within the file for formatting purposes or null if there is either no specific range or multiple + * ranges. If the range covers the entire file (full reformat), options stored in the document are ignored and + * indent options are taken from file indent options providers. + * @param ignoreDocOptions Ignore options stored in the document and use file indent options providers even if there is no text range + * or the text range doesn't cover the entire file. + * @return Indent options from the associated document or file indent options providers. + * @see com.intellij.psi.codeStyle.FileIndentOptionsProvider + */ + @NotNull + public IndentOptions getIndentOptionsByFile(@Nullable PsiFile file, @Nullable TextRange formatRange, boolean ignoreDocOptions) { if (file != null && file.isValid()) { - if (!ignoreDocOptions) { + boolean isFullReformat = isFileFullyCoveredByRange(file, formatRange); + if (!ignoreDocOptions && !isFullReformat) { IndentOptions docOptions = IndentOptions.retrieveFromAssociatedDocument(file); if (docOptions != null) return docOptions; } FileIndentOptionsProvider[] providers = Extensions.getExtensions(FileIndentOptionsProvider.EP_NAME); for (FileIndentOptionsProvider provider : providers) { - IndentOptions indentOptions = provider.getIndentOptions(file); - if (indentOptions != null) return indentOptions; + if (!isFullReformat || provider.useOnFullReformat()) { + IndentOptions indentOptions = provider.getIndentOptions(file); + if (indentOptions != null) { + logIndentOptions(file, provider, indentOptions); + return indentOptions; + } + } } return getIndentOptions(file.getFileType()); } else return OTHER_INDENT_OPTIONS; } + + private static boolean isFileFullyCoveredByRange(@NotNull PsiFile file, @Nullable TextRange formatRange) { + return + formatRange != null && + file.getTextRange().equals(formatRange); + } + + private static void logIndentOptions(@NotNull PsiFile file, + @NotNull FileIndentOptionsProvider provider, + @NotNull IndentOptions options) { + LOG.info("Indent options returned by " + provider.getClass().getName() + + " for " + file.getName() + + ": indent size=" + options.INDENT_SIZE + + ", use tabs=" + options.USE_TAB_CHARACTER + + ", tab size=" + options.TAB_SIZE); + } @Nullable private IndentOptions getLanguageIndentOptions(@Nullable FileType fileType) { diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettingsManager.java b/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettingsManager.java index 9dd266a0b3aa..08b742beea04 100644 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettingsManager.java +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettingsManager.java @@ -139,7 +139,7 @@ public class CodeStyleSettingsManager implements PersistentStateComponent 0) { try { FormatterEx.getInstanceEx().format( - model, mySettings,mySettings.getIndentOptionsByFile(fileToFormat), new FormatTextRanges(range, true) + model, mySettings,mySettings.getIndentOptionsByFile(fileToFormat, range), new FormatTextRanges(range, true) ); wrapLongLinesIfNecessary(file, document, startOffset, endOffset); @@ -242,7 +242,7 @@ public class CodeFormatterFacade { indentOptions = ((FormattingModelBuilderEx)builder).getIndentOptionsToUse(file, ranges, mySettings); } if (indentOptions == null) { - indentOptions = mySettings.getIndentOptionsByFile(file); + indentOptions = mySettings.getIndentOptionsByFile(file, textRanges.size() == 1 ? textRanges.get(0).getTextRange() : null); } formatter.format(model, mySettings, indentOptions, ranges); From 01e885effca2888d43448b788e4946801b23ee31 Mon Sep 17 00:00:00 2001 From: Alexander Doroshko Date: Wed, 24 Sep 2014 11:52:07 +0400 Subject: [PATCH 17/34] 'Stop Pub Serve' action in the Pub Serve tool window --- .../builtInWebServer/ConsoleManager.java | 23 +++++++++++++++++-- .../builtInWebServer/NetService.java | 12 +++++++++- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/xml/impl/src/org/jetbrains/builtInWebServer/ConsoleManager.java b/xml/impl/src/org/jetbrains/builtInWebServer/ConsoleManager.java index 854ae09923fc..1857c7f17f6c 100644 --- a/xml/impl/src/org/jetbrains/builtInWebServer/ConsoleManager.java +++ b/xml/impl/src/org/jetbrains/builtInWebServer/ConsoleManager.java @@ -3,10 +3,17 @@ package org.jetbrains.builtInWebServer; import com.intellij.execution.filters.TextConsoleBuilder; import com.intellij.execution.filters.TextConsoleBuilderFactory; import com.intellij.execution.ui.ConsoleView; +import com.intellij.openapi.actionSystem.ActionGroup; +import com.intellij.openapi.actionSystem.ActionManager; +import com.intellij.openapi.actionSystem.ActionPlaces; +import com.intellij.openapi.actionSystem.ActionToolbar; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.ui.SimpleToolWindowPanel; +import com.intellij.openapi.util.Disposer; import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowAnchor; import com.intellij.openapi.wm.ToolWindowManager; +import com.intellij.ui.content.Content; import com.intellij.ui.content.ContentFactory; import org.jetbrains.annotations.NotNull; @@ -29,9 +36,21 @@ public final class ConsoleManager { ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { - ToolWindow toolWindow = ToolWindowManager.getInstance(netService.project).registerToolWindow(netService.getConsoleToolWindowId(), false, ToolWindowAnchor.BOTTOM, netService.project, true); + ActionGroup actionGroup = netService.getConsoleToolWindowActions(); + ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, actionGroup, false); + + SimpleToolWindowPanel toolWindowPanel = new SimpleToolWindowPanel(false, true); + toolWindowPanel.setContent(console.getComponent()); + toolWindowPanel.setToolbar(toolbar.getComponent()); + + ToolWindow toolWindow = ToolWindowManager.getInstance(netService.project) + .registerToolWindow(netService.getConsoleToolWindowId(), false, ToolWindowAnchor.BOTTOM, netService.project, true); toolWindow.setIcon(netService.getConsoleToolWindowIcon()); - toolWindow.getContentManager().addContent(ContentFactory.SERVICE.getInstance().createContent(console.getComponent(), "", false)); + + Content content = ContentFactory.SERVICE.getInstance().createContent(toolWindowPanel, "", false); + Disposer.register(content, console); + + toolWindow.getContentManager().addContent(content); } }, netService.project.getDisposed()); } diff --git a/xml/impl/src/org/jetbrains/builtInWebServer/NetService.java b/xml/impl/src/org/jetbrains/builtInWebServer/NetService.java index a218d2af218a..fcbbd8045125 100644 --- a/xml/impl/src/org/jetbrains/builtInWebServer/NetService.java +++ b/xml/impl/src/org/jetbrains/builtInWebServer/NetService.java @@ -8,6 +8,8 @@ import com.intellij.execution.process.ProcessAdapter; import com.intellij.execution.process.ProcessEvent; import com.intellij.execution.ui.ConsoleViewContentType; import com.intellij.openapi.Disposable; +import com.intellij.openapi.actionSystem.ActionGroup; +import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; @@ -119,7 +121,10 @@ public abstract class NetService implements Disposable { @Nullable protected abstract OSProcessHandler createProcessHandler(@NotNull Project project, int port) throws ExecutionException; - protected void connectToProcess(@NotNull AsyncResult asyncResult, int port, @NotNull OSProcessHandler processHandler, @NotNull Consumer errorOutputConsumer) { + protected void connectToProcess(@NotNull AsyncResult asyncResult, + int port, + @NotNull OSProcessHandler processHandler, + @NotNull Consumer errorOutputConsumer) { asyncResult.setDone(processHandler); } @@ -139,6 +144,11 @@ public abstract class NetService implements Disposable { @NotNull protected abstract Icon getConsoleToolWindowIcon(); + @NotNull + public ActionGroup getConsoleToolWindowActions() { + return new DefaultActionGroup(); + } + private final class MyProcessAdapter extends ProcessAdapter implements Consumer { @Override public void onTextAvailable(ProcessEvent event, Key outputType) { From 5b3c3525e894170aba063f97e7662aa1ffecf7a1 Mon Sep 17 00:00:00 2001 From: "Vassiliy.Kudryashov" Date: Wed, 24 Sep 2014 12:20:38 +0400 Subject: [PATCH 18/34] https://ea.jetbrains.com/browser/ea_problems/60655 NPE: HintManagerImpl._getHintPosition --- .../codeInsight/hint/HintManagerImpl.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/codeInsight/hint/HintManagerImpl.java b/platform/platform-impl/src/com/intellij/codeInsight/hint/HintManagerImpl.java index 6c7a28854914..3d2a61020be4 100644 --- a/platform/platform-impl/src/com/intellij/codeInsight/hint/HintManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/codeInsight/hint/HintManagerImpl.java @@ -626,26 +626,34 @@ public class HintManagerImpl extends HintManager implements Disposable { int col2 = pos2.column; Point location; - JLayeredPane layeredPane = editor.getComponent().getRootPane().getLayeredPane(); + @NotNull JComponent externalComponent = editor.getComponent(); + JRootPane rootPane = externalComponent.getRootPane(); + if (rootPane != null) { + externalComponent = rootPane; + JLayeredPane layeredPane = rootPane.getLayeredPane(); + if (layeredPane != null) { + externalComponent = layeredPane; + } + } JComponent internalComponent = editor.getContentComponent(); if (constraint == RIGHT_UNDER) { Point p = editor.logicalPositionToXY(new LogicalPosition(line2, col2)); if (!showByBalloon) { p.y += editor.getLineHeight(); } - location = SwingUtilities.convertPoint(internalComponent, p, layeredPane); + location = SwingUtilities.convertPoint(internalComponent, p, externalComponent); } else { Point p = editor.logicalPositionToXY(new LogicalPosition(line1, col1)); if (constraint == UNDER) { p.y += editor.getLineHeight(); } - location = SwingUtilities.convertPoint(internalComponent, p, layeredPane); + location = SwingUtilities.convertPoint(internalComponent, p, externalComponent); } if (constraint == ABOVE && !showByBalloon) { location.y -= hintSize.height; - int diff = location.x + hintSize.width - layeredPane.getWidth(); + int diff = location.x + hintSize.width - externalComponent.getWidth(); if (diff > 0) { location.x = Math.max(location.x - diff, 0); } From c981420815c3c713576e74f0fabd61c15854a795 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Wed, 24 Sep 2014 10:32:33 +0200 Subject: [PATCH 19/34] platform: private API compatibility temporarily restored (restores Markdown plugin compatibility, WI-24562) --- .../impl/TrailingSpacesStripper.java | 30 +++++++++++++++++++ .../platform-resources/src/brokenPlugins.txt | 2 +- 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/TrailingSpacesStripper.java diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/TrailingSpacesStripper.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/TrailingSpacesStripper.java new file mode 100644 index 000000000000..93aa865bd6b8 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/TrailingSpacesStripper.java @@ -0,0 +1,30 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.fileEditor.impl; + +import com.intellij.openapi.util.Key; + +/** @deprecated use {@link com.intellij.openapi.editor.impl.TrailingSpacesStripper} (to be removed in IDEA 15) */ +@SuppressWarnings("UnusedDeclaration") +public class TrailingSpacesStripper { + + public static final Key OVERRIDE_STRIP_TRAILING_SPACES_KEY = + com.intellij.openapi.editor.impl.TrailingSpacesStripper.OVERRIDE_STRIP_TRAILING_SPACES_KEY; + + public static final Key OVERRIDE_ENSURE_NEWLINE_KEY = + com.intellij.openapi.editor.impl.TrailingSpacesStripper.OVERRIDE_ENSURE_NEWLINE_KEY; + +} diff --git a/platform/platform-resources/src/brokenPlugins.txt b/platform/platform-resources/src/brokenPlugins.txt index 60191981cc8a..9b347f110eff 100644 --- a/platform/platform-resources/src/brokenPlugins.txt +++ b/platform/platform-resources/src/brokenPlugins.txt @@ -16,4 +16,4 @@ SBT 1.0.0 1.1.0 1.2.0 1.3.0 1.3.1 1.4.0 1.5.0 AngularJS 134.1094 0.1.8 0.1.9 org.jetbrains.plugins.vagrant 0.1 0.2 org.intellij.clojure 0.2.1.178 -net.nicoulaj.idea.markdown 0.9.6 0.9.5 0.9.4 0.9.3 0.9.2 0.9.1 0.8.3 0.8.2 0.8.1 0.8 0.7 0.6.1 0.6 0.5.1 0.5 0.4 0.3 0.2 0.1 +net.nicoulaj.idea.markdown 0.9.5 0.9.4 0.9.3 0.9.2 0.9.1 0.8.3 0.8.2 0.8.1 0.8 0.7 0.6.1 0.6 0.5.1 0.5 0.4 0.3 0.2 0.1 From d0c234db26104a008a50a09ce50ab79ab4b0359c Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Wed, 24 Sep 2014 10:48:58 +0200 Subject: [PATCH 20/34] =?UTF-8?q?ability=20to=20configire=20exportable=20p?= =?UTF-8?q?resentable=20name=20Live=20Edit,=20AbbreviationManager,=20Edito?= =?UTF-8?q?rSettingsExternalizable,=20MeteorSettings,=20CssFoldingSettings?= =?UTF-8?q?=20=E2=80=94=20don't=20use=20ExportComponent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../openapi/options/OptionsBundle.java | 2 +- .../ide/actions/ExportSettingsAction.java | 106 ++++++++++++------ .../impl/AbbreviationManagerImpl.java | 18 +-- .../components/impl/ServiceManagerImpl.java | 22 ++-- .../ex/EditorSettingsExternalizable.java | 17 +-- .../src/messages/OptionsBundle.properties | 6 +- .../src/META-INF/PlatformExtensions.xml | 5 +- 7 files changed, 96 insertions(+), 80 deletions(-) diff --git a/platform/platform-api/src/com/intellij/openapi/options/OptionsBundle.java b/platform/platform-api/src/com/intellij/openapi/options/OptionsBundle.java index faaa5629a21c..20f2561ce50c 100644 --- a/platform/platform-api/src/com/intellij/openapi/options/OptionsBundle.java +++ b/platform/platform-api/src/com/intellij/openapi/options/OptionsBundle.java @@ -34,7 +34,7 @@ public class OptionsBundle { } private static Reference ourBundle; - @NonNls protected static final String PATH_TO_BUNDLE = "messages.OptionsBundle"; + @NonNls public static final String PATH_TO_BUNDLE = "messages.OptionsBundle"; private OptionsBundle() { } diff --git a/platform/platform-impl/src/com/intellij/ide/actions/ExportSettingsAction.java b/platform/platform-impl/src/com/intellij/ide/actions/ExportSettingsAction.java index 0d54096c56e8..e115a9825faa 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/ExportSettingsAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/ExportSettingsAction.java @@ -19,6 +19,8 @@ */ package com.intellij.ide.actions; +import com.intellij.AbstractBundle; +import com.intellij.CommonBundle; import com.intellij.ide.IdeBundle; import com.intellij.ide.plugins.IdeaPluginDescriptor; import com.intellij.ide.plugins.PluginManager; @@ -31,10 +33,13 @@ import com.intellij.openapi.application.impl.ApplicationImpl; import com.intellij.openapi.components.*; import com.intellij.openapi.components.impl.ServiceManagerImpl; import com.intellij.openapi.components.impl.stores.StateStorageManager; +import com.intellij.openapi.extensions.PluginDescriptor; +import com.intellij.openapi.options.OptionsBundle; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.PairProcessor; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.MultiMap; import com.intellij.util.io.ZipUtil; @@ -128,10 +133,10 @@ public class ExportSettingsAction extends AnAction implements DumbAware { } @NotNull - public static MultiMap getExportableComponentsMap(boolean onlyExisting) { + public static MultiMap getExportableComponentsMap(final boolean onlyExisting) { ExportableApplicationComponent[] components1 = ApplicationManager.getApplication().getComponents(ExportableApplicationComponent.class); List components2 = ServiceBean.loadServicesFromBeans(ExportableComponent.EXTENSION_POINT, ExportableComponent.class); - MultiMap result = MultiMap.createSet(); + final MultiMap result = MultiMap.createSet(); for (ExportableComponent component : ContainerUtil.concat(Arrays.asList(components1), components2)) { for (File exportFile : component.getExportFiles()) { result.putValue(exportFile, component); @@ -147,42 +152,79 @@ public class ExportSettingsAction extends AnAction implements DumbAware { } ApplicationImpl application = (ApplicationImpl)ApplicationManager.getApplication(); - StateStorageManager storageManager = application.getStateStore().getStateStorageManager(); - List> classes = ServiceManagerImpl.getAllImplementationClasses(application); - for (Class aClass : classes) { - final State stateAnnotation = aClass.getAnnotation(State.class); - if (stateAnnotation != null && stateAnnotation.storages().length == 1 && !StringUtil.isEmpty(stateAnnotation.name())) { - if (ExportableComponent.class.isAssignableFrom(aClass)) { - continue; - } + final StateStorageManager storageManager = application.getStateStore().getStateStorageManager(); + ServiceManagerImpl.processAllImplementationClasses(application, new PairProcessor, PluginDescriptor>() { + @Override + public boolean process(@NotNull Class aClass, @Nullable PluginDescriptor pluginDescriptor) { + State stateAnnotation = aClass.getAnnotation(State.class); + if (stateAnnotation != null && stateAnnotation.storages().length == 1 && !StringUtil.isEmpty(stateAnnotation.name())) { + if (ExportableComponent.class.isAssignableFrom(aClass)) { + return true; + } - Storage storage = stateAnnotation.storages()[0]; - if (storage.roamingType() != RoamingType.DISABLED && - storage.storageClass().equals(StateStorage.class) && - storage.scheme() == StorageScheme.DEFAULT && - !StringUtil.isEmpty(storage.file()) && - storage.file().startsWith(StoragePathMacros.APP_CONFIG)) { - final File file = new File(storageManager.expandMacros(storage.file())); - if (!onlyExisting || file.exists()) { - result.putValue(file, new ExportableComponent() { - @NotNull - @Override - public File[] getExportFiles() { - return new File[]{file}; - } - - @NotNull - @Override - public String getPresentableName() { - return stateAnnotation.name(); - } - }); + Storage storage = stateAnnotation.storages()[0]; + if (storage.roamingType() != RoamingType.DISABLED && + storage.storageClass().equals(StateStorage.class) && + storage.scheme() == StorageScheme.DEFAULT && + !StringUtil.isEmpty(storage.file()) && + storage.file().startsWith(StoragePathMacros.APP_CONFIG)) { + File file = new File(storageManager.expandMacros(storage.file())); + if (!onlyExisting || file.exists()) { + result.putValue(file, new MyExportableComponent(file, getExportableComponentPresentableName(stateAnnotation.name(), aClass, pluginDescriptor))); + } } } + return true; } + }); + return result; + } + + @NotNull + private static String getExportableComponentPresentableName(@NotNull String defaultName, @NotNull Class aClass, @Nullable PluginDescriptor pluginDescriptor) { + String resourceBundleName; + if (pluginDescriptor != null && pluginDescriptor instanceof IdeaPluginDescriptor && !"com.intellij".equals(pluginDescriptor.getPluginId().getIdString())) { + resourceBundleName = ((IdeaPluginDescriptor)pluginDescriptor).getResourceBundleBaseName(); + } + else { + resourceBundleName = OptionsBundle.PATH_TO_BUNDLE; } - return result; + if (resourceBundleName == null) { + return defaultName; + } + + ClassLoader classLoader = pluginDescriptor == null ? null : pluginDescriptor.getPluginClassLoader(); + classLoader = classLoader == null ? aClass.getClassLoader() : classLoader; + if (classLoader != null) { + ResourceBundle bundle = AbstractBundle.getResourceBundle(resourceBundleName, classLoader); + if (bundle != null) { + return CommonBundle.messageOrDefault(bundle, "exportable." + defaultName + ".presentable.name", defaultName); + } + } + return defaultName; + } + + private static final class MyExportableComponent implements ExportableComponent { + private final File file; + private final String name; + + public MyExportableComponent(@NotNull File file, @NotNull String name) { + this.file = file; + this.name = name; + } + + @NotNull + @Override + public File[] getExportFiles() { + return new File[]{file}; + } + + @NotNull + @Override + public String getPresentableName() { + return name; + } } } diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/AbbreviationManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/AbbreviationManagerImpl.java index fd96e54fc98e..0016d909ede8 100644 --- a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/AbbreviationManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/AbbreviationManagerImpl.java @@ -16,14 +16,11 @@ package com.intellij.openapi.actionSystem.impl; import com.intellij.openapi.actionSystem.AbbreviationManager; -import com.intellij.openapi.application.PathManager; import com.intellij.openapi.components.*; import gnu.trove.THashMap; import org.jdom.Element; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.io.File; import java.util.*; /** @@ -33,8 +30,7 @@ import java.util.*; name = "AbbreviationManager", storages = {@Storage(file = StoragePathMacros.APP_CONFIG + "/abbreviations.xml", roamingType = RoamingType.PER_PLATFORM)} ) -public class AbbreviationManagerImpl extends AbbreviationManager implements - ExportableComponent, PersistentStateComponent { +public class AbbreviationManagerImpl extends AbbreviationManager implements PersistentStateComponent { private final Map> myAbbreviation2ActionId = new THashMap>(); private final Map> myActionId2Abbreviations = new THashMap>(); private final Map> myPluginsActionId2Abbreviations = new THashMap>(); @@ -109,18 +105,6 @@ public class AbbreviationManagerImpl extends AbbreviationManager implements } } - @NotNull - @Override - public File[] getExportFiles() { - return new File[]{PathManager.getOptionsFile("abbreviations")}; - } - - @NotNull - @Override - public String getPresentableName() { - return "Actions"; - } - @Override public Set getAbbreviations() { final Set result = new HashSet(); diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/ServiceManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/ServiceManagerImpl.java index 9c95d681967f..68e536284274 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/ServiceManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/ServiceManagerImpl.java @@ -25,6 +25,7 @@ import com.intellij.openapi.extensions.*; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Disposer; +import com.intellij.util.PairProcessor; import com.intellij.util.io.storage.HeavyProcessLatch; import com.intellij.util.pico.AssignableToComponentAdapter; import com.intellij.util.pico.ConstructorInjectionComponentAdapter; @@ -32,7 +33,9 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.picocontainer.*; -import java.util.*; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; public class ServiceManagerImpl implements BaseComponent { private static final ExtensionPointName APP_SERVICES = new ExtensionPointName("com.intellij.applicationService"); @@ -83,31 +86,34 @@ public class ServiceManagerImpl implements BaseComponent { return Arrays.asList(extensions); } - @NotNull - public static List> getAllImplementationClasses(@NotNull ComponentManager componentManager) { + public static void processAllImplementationClasses(@NotNull ComponentManager componentManager, @NotNull PairProcessor, PluginDescriptor> processor) { Collection adapters = componentManager.getPicoContainer().getComponentAdapters(); if (adapters.isEmpty()) { - return Collections.emptyList(); + return; } - List> classes = new ArrayList>(512); for (Object o : adapters) { if (o instanceof MyComponentAdapter) { MyComponentAdapter adapter = (MyComponentAdapter)o; ComponentAdapter delegate = adapter.myDelegate; - // we cannot use getDelegate - not all components are instantiable (JobSchedulerImpl, for example, causes such error) + Class aClass; try { - classes.add(delegate == null ? adapter.loadClass(adapter.myDescriptor.getImplementation()) : delegate.getComponentImplementation()); + // we cannot use getDelegate - not all components are instantiable (JobSchedulerImpl, for example, causes such error) + aClass = delegate == null ? adapter.loadClass(adapter.myDescriptor.getImplementation()) : delegate.getComponentImplementation(); } catch (RuntimeException e) { // ignore ClassNotFoundException - invalid entry (GithubSslSupport, for example) if (!(e.getCause() instanceof ClassNotFoundException)) { throw e; } + continue; + } + + if (!processor.process(aClass, adapter.myPluginDescriptor)) { + break; } } } - return classes; } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorSettingsExternalizable.java b/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorSettingsExternalizable.java index 7ab5edb57334..9ed804f92265 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorSettingsExternalizable.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorSettingsExternalizable.java @@ -16,10 +16,8 @@ package com.intellij.openapi.editor.ex; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.PathManager; import com.intellij.openapi.components.*; import com.intellij.openapi.editor.impl.softwrap.SoftWrapAppliancePlaces; -import com.intellij.openapi.options.OptionsBundle; import com.intellij.openapi.util.text.StringUtil; import org.intellij.lang.annotations.MagicConstant; import org.jetbrains.annotations.NonNls; @@ -28,7 +26,6 @@ import org.jetbrains.annotations.Nullable; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; -import java.io.File; import java.util.EnumSet; import java.util.Set; @@ -36,7 +33,7 @@ import java.util.Set; name = "EditorSettings", storages = {@Storage(file = StoragePathMacros.APP_CONFIG + "/editor.xml")} ) -public class EditorSettingsExternalizable implements PersistentStateComponent, ExportableComponent { +public class EditorSettingsExternalizable implements PersistentStateComponent { //Q: make it interface? public static final class OptionSet { public String LINE_SEPARATOR; @@ -389,18 +386,6 @@ public class EditorSettingsExternalizable implements PersistentStateComponent - - - + From 4d4e5f0a3bc6a2d74df0cfbf1e5e2f0c49613b4f Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Wed, 24 Sep 2014 12:53:30 +0400 Subject: [PATCH 21/34] Added additional logging to detect exceptions in MultiScopeSeverityIcon --- .../ui/inspectionsTree/MultiScopeSeverityIcon.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/inspectionsTree/MultiScopeSeverityIcon.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/inspectionsTree/MultiScopeSeverityIcon.java index 099dbb622261..8c13be3eb822 100644 --- a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/inspectionsTree/MultiScopeSeverityIcon.java +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/inspectionsTree/MultiScopeSeverityIcon.java @@ -19,6 +19,7 @@ package com.intellij.profile.codeInspection.ui.inspectionsTree; import com.intellij.codeHighlighting.HighlightDisplayLevel; import com.intellij.codeInspection.ex.InspectionProfileImpl; import com.intellij.lang.annotation.HighlightSeverity; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.profile.codeInspection.ui.ScopeOrderComparator; import com.intellij.ui.JBColor; @@ -31,6 +32,8 @@ import java.util.List; * @author Dmitry Batkovich */ public class MultiScopeSeverityIcon implements Icon { + private final static Logger LOG = Logger.getInstance(MultiScopeSeverityIcon.class); + private final static JBColor MIXED_SEVERITY_COLOR = JBColor.DARK_GRAY; private final static int SIZE = 12; @@ -63,7 +66,12 @@ public class MultiScopeSeverityIcon implements Icon { final Collection values = myScopeToAverageSeverityMap.values(); int idx = 0; for (final HighlightSeverity severity : values) { - final Icon icon = HighlightDisplayLevel.find(severity).getIcon(); + final HighlightDisplayLevel level = HighlightDisplayLevel.find(severity); + if (level == null) { + LOG.error(String.format("Level for severity \"%s\" not found", severity)); + continue; + } + final Icon icon = level.getIcon(); g.setColor(icon instanceof HighlightDisplayLevel.SingleColorIconWithMask ? ((HighlightDisplayLevel.SingleColorIconWithMask)icon).getColor() : MIXED_SEVERITY_COLOR); final int x = i + partWidth * idx; From 4f1c460e76f616147f9fc46af491bb125010ab08 Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Tue, 23 Sep 2014 21:22:27 +0400 Subject: [PATCH 22/34] github: simplify --- .../github/GithubCreatePullRequestWorker.java | 5 ++--- .../github/ui/GithubCreatePullRequestDialog.java | 2 +- .../plugins/github/util/GithubNotifications.java | 14 +++++++------- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/plugins/github/src/org/jetbrains/plugins/github/GithubCreatePullRequestWorker.java b/plugins/github/src/org/jetbrains/plugins/github/GithubCreatePullRequestWorker.java index 07ed51bfca7d..972c240db1d0 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/GithubCreatePullRequestWorker.java +++ b/plugins/github/src/org/jetbrains/plugins/github/GithubCreatePullRequestWorker.java @@ -7,7 +7,6 @@ import com.intellij.openapi.progress.EmptyProgressIndicator; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Couple; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.ThrowableComputable; @@ -488,12 +487,12 @@ public class GithubCreatePullRequestWorker { String localBranchName = "'" + myCurrentBranch + "'"; String targetBranchName = "'" + fork.getRemoteName() + "/" + branch.getRemoteName() + "'"; if (info.getInfo().getBranchToHeadCommits(myGitRepository).isEmpty()) { - return Messages.YES == GithubNotifications + return GithubNotifications .showYesNoDialog(myProject, "Do you want to proceed anyway?", "Empty pull request: the branch " + localBranchName + " is fully merged to the branch " + targetBranchName); } if (!info.getInfo().getHeadToBranchCommits(myGitRepository).isEmpty()) { - return Messages.YES == GithubNotifications + return GithubNotifications .showYesNoDialog(myProject, "Do you want to proceed anyway?", "The branch " + targetBranchName + " is not fully merged to the branch " + localBranchName); } diff --git a/plugins/github/src/org/jetbrains/plugins/github/ui/GithubCreatePullRequestDialog.java b/plugins/github/src/org/jetbrains/plugins/github/ui/GithubCreatePullRequestDialog.java index 482ef9492fdc..c9b213db4358 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/ui/GithubCreatePullRequestDialog.java +++ b/plugins/github/src/org/jetbrains/plugins/github/ui/GithubCreatePullRequestDialog.java @@ -103,7 +103,7 @@ public class GithubCreatePullRequestDialog extends DialogWrapper { createRemote = GithubNotifications.showYesNoDialog(project, "Can't Find Remote", "Configure remote for '" + fork.getPath().getUser() + "'?", - ourDoNotAskOption) == Messages.YES; + ourDoNotAskOption); break; } diff --git a/plugins/github/src/org/jetbrains/plugins/github/util/GithubNotifications.java b/plugins/github/src/org/jetbrains/plugins/github/util/GithubNotifications.java index d34441bf1ba3..1b8d7920f4b8 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/util/GithubNotifications.java +++ b/plugins/github/src/org/jetbrains/plugins/github/util/GithubNotifications.java @@ -136,15 +136,15 @@ public class GithubNotifications { } @Messages.YesNoResult - public static int showYesNoDialog(@Nullable Project project, @NotNull String title, @NotNull String message) { - return Messages.showYesNoDialog(project, message, title, Messages.getQuestionIcon()); + public static boolean showYesNoDialog(@Nullable Project project, @NotNull String title, @NotNull String message) { + return Messages.YES == Messages.showYesNoDialog(project, message, title, Messages.getQuestionIcon()); } @Messages.YesNoResult - public static int showYesNoDialog(@Nullable Project project, - @NotNull String title, - @NotNull String message, - @NotNull DialogWrapper.DoNotAskOption doNotAskOption) { - return Messages.showYesNoDialog(project, message, title, Messages.getQuestionIcon(), doNotAskOption); + public static boolean showYesNoDialog(@Nullable Project project, + @NotNull String title, + @NotNull String message, + @NotNull DialogWrapper.DoNotAskOption doNotAskOption) { + return Messages.YES == Messages.showYesNoDialog(project, message, title, Messages.getQuestionIcon(), doNotAskOption); } } From f36967a5a95c3d7acb125a1f57ced743e9f247f4 Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Wed, 24 Sep 2014 12:24:10 +0400 Subject: [PATCH 23/34] github: remove check for consistency It's the only place with such check, so let's fail with everyone else --- .../org/jetbrains/plugins/github/api/GithubConnection.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/github/src/org/jetbrains/plugins/github/api/GithubConnection.java b/plugins/github/src/org/jetbrains/plugins/github/api/GithubConnection.java index e61e96fed8e0..b51b1ca00def 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/api/GithubConnection.java +++ b/plugins/github/src/org/jetbrains/plugins/github/api/GithubConnection.java @@ -145,7 +145,7 @@ public class GithubConnection { .setSocketTimeout(timeout); final HttpConfigurable proxySettings = HttpConfigurable.getInstance(); - boolean useProxy = auth.isUseProxy() && proxySettings.USE_HTTP_PROXY && !StringUtil.isEmptyOrSpaces(proxySettings.PROXY_HOST); + boolean useProxy = auth.isUseProxy() && proxySettings.USE_HTTP_PROXY; proxySettings.setProxy(builder, useProxy); @@ -171,7 +171,7 @@ public class GithubConnection { final HttpConfigurable proxySettings = HttpConfigurable.getInstance(); //proxySettings.USE_HTTP_PROXY - boolean useProxy = auth.isUseProxy() && proxySettings.USE_HTTP_PROXY && !StringUtil.isEmptyOrSpaces(proxySettings.PROXY_HOST); + boolean useProxy = auth.isUseProxy() && proxySettings.USE_HTTP_PROXY; proxySettings.setProxyCredentials(provider, useProxy); From c33f57957c7dfac14828c8728670c9e2dbd79396 Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Wed, 24 Sep 2014 12:27:47 +0400 Subject: [PATCH 24/34] github: sanitize util method --- .../com/intellij/util/net/HttpConfigurable.java | 10 ++++++++++ .../plugins/github/api/GithubConnection.java | 15 ++++++--------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/platform/platform-api/src/com/intellij/util/net/HttpConfigurable.java b/platform/platform-api/src/com/intellij/util/net/HttpConfigurable.java index a49746b82fb0..1edcdc4015ca 100644 --- a/platform/platform-api/src/com/intellij/util/net/HttpConfigurable.java +++ b/platform/platform-api/src/com/intellij/util/net/HttpConfigurable.java @@ -434,6 +434,16 @@ public class HttpConfigurable implements PersistentStateComponent Date: Tue, 23 Sep 2014 21:27:37 +0400 Subject: [PATCH 25/34] IDEA-124356 github: try to share project even if found some github remote --- .../plugins/github/GithubShareAction.java | 66 ++++++++++++++++--- 1 file changed, 57 insertions(+), 9 deletions(-) diff --git a/plugins/github/src/org/jetbrains/plugins/github/GithubShareAction.java b/plugins/github/src/org/jetbrains/plugins/github/GithubShareAction.java index c372b09805a1..537a580775c8 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/GithubShareAction.java +++ b/plugins/github/src/org/jetbrains/plugins/github/GithubShareAction.java @@ -49,14 +49,16 @@ import git4idea.repo.GitRepositoryManager; import git4idea.util.GitFileUtils; import git4idea.util.GitUIUtil; import icons.GithubIcons; +import org.apache.http.HttpStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.plugins.github.api.GithubApiUtil; -import org.jetbrains.plugins.github.api.GithubConnection; -import org.jetbrains.plugins.github.api.GithubRepo; -import org.jetbrains.plugins.github.api.GithubUserDetailed; +import org.jetbrains.plugins.github.api.*; +import org.jetbrains.plugins.github.exceptions.GithubStatusCodeException; import org.jetbrains.plugins.github.ui.GithubShareDialog; -import org.jetbrains.plugins.github.util.*; +import org.jetbrains.plugins.github.util.GithubAuthDataHolder; +import org.jetbrains.plugins.github.util.GithubNotifications; +import org.jetbrains.plugins.github.util.GithubUrlUtil; +import org.jetbrains.plugins.github.util.GithubUtil; import javax.swing.*; import java.io.IOException; @@ -114,19 +116,18 @@ public class GithubShareAction extends DumbAwareAction { final boolean gitDetected = gitRepository != null; final VirtualFile root = gitDetected ? gitRepository.getRoot() : project.getBaseDir(); + final GithubAuthDataHolder authHolder = GithubAuthDataHolder.createFromSettings(); + // check for existing git repo boolean externalRemoteDetected = false; if (gitDetected) { final String githubRemote = GithubUtil.findGithubRemoteUrl(gitRepository); if (githubRemote != null) { - GithubNotifications.showInfoURL(project, "Project is already on GitHub", "GitHub", githubRemote); - return; + if (!checkExistingRemote(project, authHolder, githubRemote)) return; } externalRemoteDetected = !gitRepository.getRemotes().isEmpty(); } - final GithubAuthDataHolder authHolder = GithubAuthDataHolder.createFromSettings(); - // get available GitHub repos with modal progress final GithubInfo githubInfo = loadGithubInfoWithModal(authHolder, project); if (githubInfo == null) { @@ -203,6 +204,53 @@ public class GithubShareAction extends DumbAwareAction { }.queue(); } + private static boolean checkExistingRemote(@NotNull final Project project, + @NotNull final GithubAuthDataHolder authHolder, + @NotNull String remote) { + final GithubFullPath path = GithubUrlUtil.getUserAndRepositoryFromRemoteUrl(remote); + if (path == null) { + return GithubNotifications.showYesNoDialog(project, + "Project is already on GitHub", + "Can't connect to repository from configured remote. You could want to check .git config.\n" + + "Do you want to proceed anyway?"); + } + + try { + GithubRepo repo = + GithubUtil.computeValueInModal(project, "Access to GitHub", new ThrowableConvertor() { + @NotNull + @Override + public GithubRepo convert(ProgressIndicator indicator) throws IOException { + return GithubUtil + .runTask(project, authHolder, indicator, new ThrowableConvertor() { + @NotNull + @Override + public GithubRepo convert(@NotNull GithubConnection connection) throws IOException { + return GithubApiUtil.getDetailedRepoInfo(connection, path.getUser(), path.getRepository()); + } + }); + } + }); + GithubNotifications.showInfoURL(project, "Project is already on GitHub", "GitHub", repo.getHtmlUrl()); + return false; + } + catch (GithubStatusCodeException e) { + if (e.getStatusCode() == HttpStatus.SC_NOT_FOUND) { + return GithubNotifications.showYesNoDialog(project, + "Project is already on GitHub", + "Can't connect to repository from configured remote. You could want to check .git config.\n" + + "Do you want to proceed anyway?"); + } + + GithubNotifications.showErrorDialog(project, "Failed to connect to GitHub", e); + return false; + } + catch (IOException e) { + GithubNotifications.showErrorDialog(project, "Failed to connect to GitHub", e); + return false; + } + } + @Nullable private static GithubInfo loadGithubInfoWithModal(@NotNull final GithubAuthDataHolder authHolder, @NotNull final Project project) { try { From b4a02a60ee848be64b12d8ecd281dc2cd3f525eb Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Wed, 24 Sep 2014 13:12:53 +0400 Subject: [PATCH 26/34] InspectionFilterAction became Toggleable (IDEA-128048) --- .../codeInspection/ui/filter/InspectionFilterAction.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/filter/InspectionFilterAction.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/filter/InspectionFilterAction.java index bccd2db28e21..30f4461f8359 100644 --- a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/filter/InspectionFilterAction.java +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/filter/InspectionFilterAction.java @@ -22,6 +22,7 @@ import com.intellij.icons.AllIcons; import com.intellij.lang.annotation.HighlightSeverity; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.DefaultActionGroup; +import com.intellij.openapi.actionSystem.Toggleable; import com.intellij.openapi.actionSystem.ex.CheckboxAction; import com.intellij.profile.codeInspection.SeverityProvider; import com.intellij.profile.codeInspection.ui.LevelChooserAction; @@ -33,7 +34,7 @@ import java.util.SortedSet; /** * @author Dmitry Batkovich */ -public class InspectionFilterAction extends DefaultActionGroup { +public class InspectionFilterAction extends DefaultActionGroup implements Toggleable { private final SeverityRegistrar mySeverityRegistrar; private final InspectionsFilter myInspectionsFilter; @@ -46,6 +47,12 @@ public class InspectionFilterAction extends DefaultActionGroup { tune(); } + @Override + public void update(AnActionEvent e) { + super.update(e); + e.getPresentation().putClientProperty(Toggleable.SELECTED_PROPERTY, !myInspectionsFilter.isEmptyFilter()); + } + private void tune() { addAction(new ShowEnabledOrDisabledInspectionsAction(null)); addAction(new ShowEnabledOrDisabledInspectionsAction(true)); From 9d57b07c328ea56d45cd5af65d52579f85872144 Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 24 Sep 2014 11:10:34 +0200 Subject: [PATCH 27/34] dfa: perform expensive state merge only after method calls that have chances to produce mergeable states, i.e. have contracts --- .../com/intellij/codeInspection/dataFlow/DataFlowRunner.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowRunner.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowRunner.java index 10e0a0f4acd3..834bf53b675a 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowRunner.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowRunner.java @@ -124,7 +124,7 @@ public class DataFlowRunner { joinInstructions.add(myInstructions[((GotoInstruction)instruction).getOffset()]); } else if (instruction instanceof ConditionalGotoInstruction) { joinInstructions.add(myInstructions[((ConditionalGotoInstruction)instruction).getOffset()]); - } else if (instruction instanceof MethodCallInstruction) { + } else if (instruction instanceof MethodCallInstruction && !((MethodCallInstruction)instruction).getContracts().isEmpty()) { joinInstructions.add(myInstructions[index + 1]); } } From 2eec25fc7b41562de653b10556ec9817bffc0d6f Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 24 Sep 2014 11:37:42 +0200 Subject: [PATCH 28/34] faster FList.equals/hashCode --- .../com/intellij/util/containers/FList.java | 52 +++++++++++++++---- 1 file changed, 41 insertions(+), 11 deletions(-) diff --git a/platform/util/src/com/intellij/util/containers/FList.java b/platform/util/src/com/intellij/util/containers/FList.java index 12f5fad9a0e6..dde6dcf3fcba 100644 --- a/platform/util/src/com/intellij/util/containers/FList.java +++ b/platform/util/src/com/intellij/util/containers/FList.java @@ -15,9 +15,12 @@ */ package com.intellij.util.containers; +import com.intellij.openapi.util.Comparing; import org.jetbrains.annotations.NotNull; -import java.util.*; +import java.util.AbstractList; +import java.util.Iterator; +import java.util.NoSuchElementException; /** * Immutable list in functional style @@ -25,18 +28,15 @@ import java.util.*; * @author nik */ public class FList extends AbstractList { - private static final FList EMPTY_LIST = new FList(); - private E myHead; - private FList myTail; - private int mySize; + @SuppressWarnings("unchecked") private static final FList EMPTY_LIST = new FList(null, null, 0); + private final E myHead; + private final FList myTail; + private final int mySize; - private FList() { - } - - private FList(E head, FList tail) { + private FList(E head, FList tail, int size) { myHead = head; myTail = tail; - mySize = tail.size()+1; + mySize = size; } @Override @@ -58,7 +58,7 @@ public class FList extends AbstractList { } public FList prepend(E elem) { - return new FList(elem, this); + return new FList(elem, this, mySize + 1); } public FList without(E elem) { @@ -120,7 +120,37 @@ public class FList extends AbstractList { return mySize; } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o instanceof FList) { + FList list1 = this; + FList list2 = (FList)o; + if (mySize != list2.mySize) return false; + while (list1 != null) { + if (!Comparing.equal(list1.myHead, list2.myHead)) return false; + list1 = list1.getTail(); + list2 = list2.getTail(); + if (list1 == list2) return true; + } + return true; + } + return super.equals(o); + } + + @Override + public int hashCode() { + int result = 1; + FList each = this; + while (each != null) { + result = result * 31 + (each.myHead != null ? each.myHead.hashCode() : 0); + each = each.getTail(); + } + return result; + } + public static FList emptyList() { + //noinspection unchecked return (FList)EMPTY_LIST; } } From f8827da0ff6d667e15f97dcbebb206462ca8f6a3 Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 24 Sep 2014 11:42:45 +0200 Subject: [PATCH 29/34] show comma between external/inferred annotation attributes on the gutter --- .../intellij/codeInsight/InferredAnnotationsManagerImpl.java | 2 +- .../com/intellij/codeInsight/javadoc/JavaDocInfoGenerator.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/InferredAnnotationsManagerImpl.java b/java/java-analysis-impl/src/com/intellij/codeInsight/InferredAnnotationsManagerImpl.java index 77a227d49847..d693e15f2ad7 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/InferredAnnotationsManagerImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/InferredAnnotationsManagerImpl.java @@ -95,7 +95,7 @@ public class InferredAnnotationsManagerImpl extends InferredAnnotationsManager { private PsiAnnotation createContractAnnotation(List contracts, boolean pure) { final String attrs; if (!contracts.isEmpty() && pure) { - attrs = "value = " + "\"" + StringUtil.join(contracts, "; ") + "\"; pure = true"; + attrs = "value = " + "\"" + StringUtil.join(contracts, "; ") + "\", pure = true"; } else if (pure) { attrs = "pure = true"; } else if (!contracts.isEmpty()) { diff --git a/java/java-psi-impl/src/com/intellij/codeInsight/javadoc/JavaDocInfoGenerator.java b/java/java-psi-impl/src/com/intellij/codeInsight/javadoc/JavaDocInfoGenerator.java index 67b1ef813de8..7d7d697d724e 100644 --- a/java/java-psi-impl/src/com/intellij/codeInsight/javadoc/JavaDocInfoGenerator.java +++ b/java/java-psi-impl/src/com/intellij/codeInsight/javadoc/JavaDocInfoGenerator.java @@ -746,7 +746,7 @@ public class JavaDocInfoGenerator { buffer.append("("); boolean first = true; for (PsiNameValuePair pair : attributes) { - if (!first) buffer.append(" "); + if (!first) buffer.append(", "); first = false; final String name = pair.getName(); if (name != null) { From 7d67bf34889805db7612ee25df5ad16d4bd07dd7 Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Wed, 24 Sep 2014 13:51:58 +0400 Subject: [PATCH 30/34] NPE in MultiScopeSeverityIcon fixed,not fount highlight display levels not shown --- .../InspectionsConfigTreeTable.java | 8 +++--- .../MultiScopeSeverityIcon.java | 25 +++++++++++-------- .../ScopesAndSeveritiesHintTable.java | 15 ++++++----- 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/inspectionsTree/InspectionsConfigTreeTable.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/inspectionsTree/InspectionsConfigTreeTable.java index 59c747eec329..892cec10a1d7 100644 --- a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/inspectionsTree/InspectionsConfigTreeTable.java +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/inspectionsTree/InspectionsConfigTreeTable.java @@ -84,14 +84,14 @@ public class InspectionsConfigTreeTable extends TreeTable { final int row = rowAtPoint(point); final Object maybeIcon = getModel().getValueAt(row, column); if (maybeIcon instanceof MultiScopeSeverityIcon) { - final LinkedHashMap scopeToAverageSeverityMap = + final LinkedHashMap scopeToAverageSeverityMap = ((MultiScopeSeverityIcon)maybeIcon).getScopeToAverageSeverityMap(); final JComponent component; if (scopeToAverageSeverityMap.size() == 1) { - final HighlightSeverity severity = ContainerUtil.getFirstItem(scopeToAverageSeverityMap.values()); + final HighlightDisplayLevel level = ContainerUtil.getFirstItem(scopeToAverageSeverityMap.values()); final JLabel label = new JLabel(); - label.setIcon(HighlightDisplayLevel.find(severity).getIcon()); - label.setText(SingleInspectionProfilePanel.renderSeverity(severity)); + label.setIcon(level.getIcon()); + label.setText(SingleInspectionProfilePanel.renderSeverity(level.getSeverity())); component = label; } else { component = new ScopesAndSeveritiesHintTable(scopeToAverageSeverityMap); diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/inspectionsTree/MultiScopeSeverityIcon.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/inspectionsTree/MultiScopeSeverityIcon.java index 8c13be3eb822..3d5bbd6d6db5 100644 --- a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/inspectionsTree/MultiScopeSeverityIcon.java +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/inspectionsTree/MultiScopeSeverityIcon.java @@ -38,22 +38,30 @@ public class MultiScopeSeverityIcon implements Icon { private final static int SIZE = 12; - private final LinkedHashMap myScopeToAverageSeverityMap; + private final LinkedHashMap myScopeToAverageSeverityMap; public MultiScopeSeverityIcon(final Map scopeToAverageSeverityMap, final String defaultScopeName, final InspectionProfileImpl inspectionProfile) { final List sortedScopeNames = new ArrayList(scopeToAverageSeverityMap.keySet()); - myScopeToAverageSeverityMap = new LinkedHashMap(); + myScopeToAverageSeverityMap = new LinkedHashMap(); Collections.sort(sortedScopeNames, new ScopeOrderComparator(inspectionProfile)); sortedScopeNames.remove(defaultScopeName); sortedScopeNames.add(defaultScopeName); for (final String scopeName : sortedScopeNames) { - myScopeToAverageSeverityMap.put(scopeName, scopeToAverageSeverityMap.get(scopeName)); + final HighlightSeverity severity = scopeToAverageSeverityMap.get(scopeName); + if (severity == null) { + continue; + } + final HighlightDisplayLevel level = HighlightDisplayLevel.find(severity); + if (level == null) { + continue; + } + myScopeToAverageSeverityMap.put(scopeName, level); } } - public LinkedHashMap getScopeToAverageSeverityMap() { + public LinkedHashMap getScopeToAverageSeverityMap() { return myScopeToAverageSeverityMap; } @@ -63,14 +71,9 @@ public class MultiScopeSeverityIcon implements Icon { final int partWidth = iconWidth / myScopeToAverageSeverityMap.size(); - final Collection values = myScopeToAverageSeverityMap.values(); + final Collection values = myScopeToAverageSeverityMap.values(); int idx = 0; - for (final HighlightSeverity severity : values) { - final HighlightDisplayLevel level = HighlightDisplayLevel.find(severity); - if (level == null) { - LOG.error(String.format("Level for severity \"%s\" not found", severity)); - continue; - } + for (final HighlightDisplayLevel level : values) { final Icon icon = level.getIcon(); g.setColor(icon instanceof HighlightDisplayLevel.SingleColorIconWithMask ? ((HighlightDisplayLevel.SingleColorIconWithMask)icon).getColor() : MIXED_SEVERITY_COLOR); diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/inspectionsTree/ScopesAndSeveritiesHintTable.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/inspectionsTree/ScopesAndSeveritiesHintTable.java index 5d619ce6a0ed..9ff70c59a0cf 100644 --- a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/inspectionsTree/ScopesAndSeveritiesHintTable.java +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/inspectionsTree/ScopesAndSeveritiesHintTable.java @@ -16,7 +16,6 @@ package com.intellij.profile.codeInspection.ui.inspectionsTree; import com.intellij.codeHighlighting.HighlightDisplayLevel; -import com.intellij.lang.annotation.HighlightSeverity; import com.intellij.profile.codeInspection.ui.SingleInspectionProfilePanel; import com.intellij.ui.table.JBTable; import com.intellij.util.ui.UIUtil; @@ -37,7 +36,7 @@ public class ScopesAndSeveritiesHintTable extends JBTable { private final static int SCOPE_COLUMN = 0; private final static int SEVERITY_COLUMN = 1; - public ScopesAndSeveritiesHintTable(final LinkedHashMap scopeToAverageSeverityMap) { + public ScopesAndSeveritiesHintTable(final LinkedHashMap scopeToAverageSeverityMap) { super(new MyModel(scopeToAverageSeverityMap)); getColumnModel().getColumn(SCOPE_COLUMN).setCellRenderer(new DefaultTableCellRenderer() { @@ -64,9 +63,9 @@ public class ScopesAndSeveritiesHintTable extends JBTable { final int row, final int column) { super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - final HighlightSeverity severity = (HighlightSeverity)value; - setIcon(HighlightDisplayLevel.find(severity).getIcon()); - setText(SingleInspectionProfilePanel.renderSeverity(severity)); + final HighlightDisplayLevel level = (HighlightDisplayLevel)value; + setIcon(level.getIcon()); + setText(SingleInspectionProfilePanel.renderSeverity(level.getSeverity())); setOpaque(false); UIUtil.applyStyle(UIUtil.ComponentStyle.SMALL, this); return this; @@ -90,10 +89,10 @@ public class ScopesAndSeveritiesHintTable extends JBTable { private final static class MyModel extends AbstractTableModel { - private final LinkedHashMap myScopeToAverageSeverityMap; + private final LinkedHashMap myScopeToAverageSeverityMap; private final List myScopes; - public MyModel(final LinkedHashMap scopeToAverageSeverityMap) { + public MyModel(final LinkedHashMap scopeToAverageSeverityMap) { myScopeToAverageSeverityMap = scopeToAverageSeverityMap; myScopes = new ArrayList(myScopeToAverageSeverityMap.keySet()); } @@ -102,7 +101,7 @@ public class ScopesAndSeveritiesHintTable extends JBTable { public Class getColumnClass(final int columnIndex) { switch (columnIndex) { case SCOPE_COLUMN: return String.class; - case SEVERITY_COLUMN: return HighlightSeverity.class; + case SEVERITY_COLUMN: return HighlightDisplayLevel.class; default: throw new IllegalArgumentException(); } } From 97d0ea079d921de84aa401784c1459057a7113ea Mon Sep 17 00:00:00 2001 From: Dmitry Batrak Date: Wed, 24 Sep 2014 12:54:16 +0400 Subject: [PATCH 31/34] tests for new folding actions (IDEA-15412,IDEA-74010) --- .../folding/JavaFoldingTest.groovy | 77 +++++++++++++++++++ .../openapi/actionSystem/IdeActions.java | 7 ++ 2 files changed, 84 insertions(+) diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/folding/JavaFoldingTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/folding/JavaFoldingTest.groovy index 0baf4d8510ee..f87cd5bc331e 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/folding/JavaFoldingTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/folding/JavaFoldingTest.groovy @@ -920,4 +920,81 @@ public class Test { myFixture.performEditorAction(IdeActions.ACTION_EDITOR_UNSELECT_WORD_AT_CARET) assert 'return field;' == myFixture.editor.selectionModel.selectedText } + + public void "test expand and collapse regions in selection"() { + def text = """ +class Foo { + public static void main() { + new Runnable(){ + public void run() { + } + }.run(); + } +} +""" + configure text + assertEquals 3, foldRegionsCount + myFixture.performEditorAction(IdeActions.ACTION_EXPAND_ALL_REGIONS) + assertEquals 3, expandedFoldRegionsCount + + + myFixture.editor.selectionModel.setSelection(text.indexOf("new"), text.indexOf("run();")) + myFixture.performEditorAction(IdeActions.ACTION_COLLAPSE_ALL_REGIONS) + assertEquals 1, expandedFoldRegionsCount + myFixture.performEditorAction(IdeActions.ACTION_EXPAND_ALL_REGIONS) + assertEquals 3, expandedFoldRegionsCount + } + + public void "test expand and collapse recursively"() { + def text = """ +class Foo { + public static void main() { + new Runnable(){ + public void run() { + } + }.run(); + } +} +""" + configure text + assertEquals 3, foldRegionsCount + myFixture.performEditorAction(IdeActions.ACTION_EXPAND_ALL_REGIONS) + assertEquals 3, expandedFoldRegionsCount + + + myFixture.editor.caretModel.moveToOffset(text.indexOf("new")) + myFixture.performEditorAction(IdeActions.ACTION_COLLAPSE_REGION_RECURSIVELY) + assertEquals 1, expandedFoldRegionsCount + myFixture.performEditorAction(IdeActions.ACTION_EXPAND_REGION_RECURSIVELY) + assertEquals 3, expandedFoldRegionsCount + } + + public void "test expand to level"() { + def text = """ +class Foo { + public static void main() { + new Runnable(){ + public void run() { + } + }.run(); + } +} +""" + configure text + assertEquals 3, foldRegionsCount + + myFixture.editor.caretModel.moveToOffset(text.indexOf("new")) + myFixture.performEditorAction(IdeActions.ACTION_EXPAND_TO_LEVEL_1) + assertEquals 2, expandedFoldRegionsCount + myFixture.performEditorAction(IdeActions.ACTION_EXPAND_ALL_TO_LEVEL_1) + assertEquals 1, expandedFoldRegionsCount + } + + private int getFoldRegionsCount() { + return myFixture.editor.foldingModel.allFoldRegions.length + } + + private int getExpandedFoldRegionsCount() { + return myFixture.editor.foldingModel.allFoldRegions.count { it.isExpanded() ? 1 : 0} + } } diff --git a/platform/platform-api/src/com/intellij/openapi/actionSystem/IdeActions.java b/platform/platform-api/src/com/intellij/openapi/actionSystem/IdeActions.java index b6b15047aa24..e43fe3ffa31b 100644 --- a/platform/platform-api/src/com/intellij/openapi/actionSystem/IdeActions.java +++ b/platform/platform-api/src/com/intellij/openapi/actionSystem/IdeActions.java @@ -150,6 +150,13 @@ public interface IdeActions { @NonNls String ACTION_COLLAPSE_ALL = "CollapseAll"; @NonNls String ACTION_EXPORT_TO_TEXT_FILE = "ExportToTextFile"; + @NonNls String ACTION_EXPAND_ALL_REGIONS = "ExpandAllRegions"; + @NonNls String ACTION_COLLAPSE_ALL_REGIONS = "CollapseAllRegions"; + @NonNls String ACTION_EXPAND_REGION_RECURSIVELY = "ExpandRegionRecursively"; + @NonNls String ACTION_COLLAPSE_REGION_RECURSIVELY = "CollapseRegionRecursively"; + @NonNls String ACTION_EXPAND_TO_LEVEL_1 = "ExpandToLevel1"; + @NonNls String ACTION_EXPAND_ALL_TO_LEVEL_1 = "ExpandAllToLevel1"; + @NonNls String ACTION_NEW_HORIZONTAL_TAB_GROUP = "NewHorizontalTabGroup"; @NonNls String ACTION_NEW_VERTICAL_TAB_GROUP = "NewVerticalTabGroup"; @NonNls String ACTION_MOVE_EDITOR_TO_OPPOSITE_TAB_GROUP = "MoveEditorToOppositeTabGroup"; From 0d52c7dc57c2b3e8368eeaf74865c8d41fd157d2 Mon Sep 17 00:00:00 2001 From: Dmitry Batrak Date: Wed, 24 Sep 2014 13:51:45 +0400 Subject: [PATCH 32/34] keymap fixes for new folding actions (IDEA-74010) --- .../src/idea/Keymap_EclipseMac.xml | 10 ++++++++++ .../src/idea/Keymap_Mac.xml | 20 +++++++++++++++++++ .../src/idea/Keymap_MacClassic.xml | 20 +++++++++++++++++++ .../openapi/keymap/KeymapsTestCase.java | 5 +++-- 4 files changed, 53 insertions(+), 2 deletions(-) diff --git a/platform/platform-resources/src/idea/Keymap_EclipseMac.xml b/platform/platform-resources/src/idea/Keymap_EclipseMac.xml index d267f18f7649..69086f9f9b2b 100644 --- a/platform/platform-resources/src/idea/Keymap_EclipseMac.xml +++ b/platform/platform-resources/src/idea/Keymap_EclipseMac.xml @@ -91,6 +91,16 @@ + + + + + + + + + + diff --git a/platform/platform-resources/src/idea/Keymap_Mac.xml b/platform/platform-resources/src/idea/Keymap_Mac.xml index 2ad9c7f7e2c6..0cbcef8c36e0 100644 --- a/platform/platform-resources/src/idea/Keymap_Mac.xml +++ b/platform/platform-resources/src/idea/Keymap_Mac.xml @@ -555,5 +555,25 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/platform/platform-resources/src/idea/Keymap_MacClassic.xml b/platform/platform-resources/src/idea/Keymap_MacClassic.xml index 730477d2af8a..bd4e97e6d743 100644 --- a/platform/platform-resources/src/idea/Keymap_MacClassic.xml +++ b/platform/platform-resources/src/idea/Keymap_MacClassic.xml @@ -353,5 +353,25 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/platform/testFramework/testSrc/com/intellij/openapi/keymap/KeymapsTestCase.java b/platform/testFramework/testSrc/com/intellij/openapi/keymap/KeymapsTestCase.java index da59c1fb5460..e722fcd1a72e 100644 --- a/platform/testFramework/testSrc/com/intellij/openapi/keymap/KeymapsTestCase.java +++ b/platform/testFramework/testSrc/com/intellij/openapi/keymap/KeymapsTestCase.java @@ -213,7 +213,8 @@ public abstract class KeymapsTestCase extends PlatformTestCase { { "shift F2", "GotoPreviousError", "GotoDeclaration"}, { "shift control F7", "FindUsagesInFile", "HighlightUsagesInFile"}, { "shift control I", "ImplementMethods", "QuickImplementations"}, - { "alt F9", "ViewBreakpoints", "EditBreakpoint"}, + { "alt F9", "ViewBreakpoints", "EditBreakpoint"}, + { "alt MULTIPLY", "ShowExecutionPoint", "Images.Thumbnails.ToggleRecursive"}, }); put("Default for XWin", new String[][] { }); @@ -277,7 +278,7 @@ public abstract class KeymapsTestCase extends PlatformTestCase { { "control 2", "ActivateProjectToolWindow", "FileChooser.GotoProject", "DuplicatesForm.SendToRight"}, { "control 3", "ActivateProjectToolWindow", "FileChooser.GotoModule"}, { "control BACK_SPACE", "EditorDeleteToWordStart", "ToggleDockMode"}, - { "control DIVIDE", "CommentByLineComment", "Images.Editor.ActualSize"}, + { "control DIVIDE", "CollapseRegionRecursively", "Images.Editor.ActualSize"}, { "control D", "EditorDuplicate", "CompareDirs", "CompareTwoFiles", "SendEOF", "FileChooser.GotoDesktop"}, { "control M", "Vcs.ShowMessageHistory", "Move"}, { "control R", "RenameElement", "Console.TableResult.Reload", "org.jetbrains.plugins.ruby.rails.console.ReloadSources"}, From 93283c8255c799d7a3d1a7a85295faabdb7c78d4 Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 24 Sep 2014 11:55:48 +0200 Subject: [PATCH 33/34] System.exit is not pure --- .../codeInsight/InferredAnnotationsManagerImpl.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/InferredAnnotationsManagerImpl.java b/java/java-analysis-impl/src/com/intellij/codeInsight/InferredAnnotationsManagerImpl.java index d693e15f2ad7..0de6c43cc178 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/InferredAnnotationsManagerImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/InferredAnnotationsManagerImpl.java @@ -65,9 +65,9 @@ public class InferredAnnotationsManagerImpl extends InferredAnnotationsManager { return null; } - private PsiAnnotation getHardcodedContractAnnotation(PsiMethod listOwner) { - List contracts = HardcodedContracts.getHardcodedContracts(listOwner, null); - return contracts.isEmpty() ? null : createContractAnnotation(contracts, true); + private PsiAnnotation getHardcodedContractAnnotation(PsiMethod method) { + List contracts = HardcodedContracts.getHardcodedContracts(method, null); + return contracts.isEmpty() ? null : createContractAnnotation(contracts, !"java.lang.System.exit".equals(PsiUtil.getMemberQualifiedName(method))); } private static boolean ignoreBytecodeInference(PsiModifierListOwner owner, String annotationFQN) { From b315ca0ed56fe0566810c1b72000dc2bb8e5d31f Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 24 Sep 2014 11:56:57 +0200 Subject: [PATCH 34/34] don't infer contracts for library source (getOriginalElement doesn't work well for anonymous classes) --- .../codeInspection/dataFlow/ContractInference.java | 11 ++++++++++- .../codeInspection/dataFlow/PurityInference.java | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInference.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInference.java index f919ac5f8796..3af865b19bde 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInference.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInference.java @@ -17,13 +17,16 @@ package com.intellij.codeInspection.dataFlow; import com.intellij.codeInsight.NullableNotNullManager; import com.intellij.codeInspection.dataFlow.MethodContract.ValueConstraint; +import com.intellij.openapi.roots.FileIndexFacade; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.RecursionManager; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.CachedValueProvider; import com.intellij.psi.util.CachedValuesManager; +import com.intellij.psi.util.PsiUtilCore; import com.intellij.util.Function; import com.intellij.util.NullableFunction; import com.intellij.util.containers.ContainerUtil; @@ -44,7 +47,7 @@ public class ContractInference { @NotNull public static List inferContracts(@NotNull final PsiMethod method) { - if (method instanceof PsiCompiledElement) { + if (isLibraryCode(method)) { return Collections.emptyList(); } @@ -56,6 +59,12 @@ public class ContractInference { } }); } + + static boolean isLibraryCode(@NotNull PsiMethod method) { + if (method instanceof PsiCompiledElement) return true; + VirtualFile virtualFile = PsiUtilCore.getVirtualFile(method); + return virtualFile != null && FileIndexFacade.getInstance(method.getProject()).isInLibrarySource(virtualFile); + } } class ContractInferenceInterpreter { diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/PurityInference.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/PurityInference.java index 7686cf21855c..6550cd60d270 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/PurityInference.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/PurityInference.java @@ -34,7 +34,7 @@ import java.util.List; public class PurityInference { public static boolean inferPurity(@NotNull final PsiMethod method) { - if (method instanceof PsiCompiledElement || method.getReturnType() == PsiType.VOID || method.getBody() == null || method.isConstructor()) { + if (ContractInference.isLibraryCode(method) || method.getReturnType() == PsiType.VOID || method.getBody() == null || method.isConstructor()) { return false; }