diff options
author | Dana Dahlstrom <dahlstrom@google.com> | 2022-06-24 20:00:00 -0700 |
---|---|---|
committer | Dana Dahlstrom <dahlstrom@google.com> | 2022-06-24 20:00:00 -0700 |
commit | 4d573076b17387943d28b5733899900405a0ed32 (patch) | |
tree | d8c70b0e7ac9197a6d63125e090d41657e2495f7 | |
parent | bc61b8c5206ae3afb2455cbba7cd15989f58a4ad (diff) | |
parent | d5bf17fe27f61040fa11032fd075fc375658a567 (diff) | |
download | idea-4d573076b17387943d28b5733899900405a0ed32.tar.gz |
Merge IntelliJ IDEA 2022.1.3 221.5921.22
Change-Id: Id5bf17fe27f61040fa11032fd075fc375658a567
112 files changed, 1176 insertions, 569 deletions
diff --git a/bin/mac/libmacscreenmenu64.dylib b/bin/mac/libmacscreenmenu64.dylib Binary files differindex 2763946a05c1..8aabfd7209f6 100755 --- a/bin/mac/libmacscreenmenu64.dylib +++ b/bin/mac/libmacscreenmenu64.dylib diff --git a/build.txt b/build.txt index c4d55ece6d28..66d5774b2fdb 100644 --- a/build.txt +++ b/build.txt @@ -1 +1 @@ -221.5787.30.2211.SNAPSHOT +221.5921.22.2211.SNAPSHOT diff --git a/community-resources/src/idea/IdeaApplicationInfo.xml b/community-resources/src/idea/IdeaApplicationInfo.xml index 0de0448af119..6db949992468 100644 --- a/community-resources/src/idea/IdeaApplicationInfo.xml +++ b/community-resources/src/idea/IdeaApplicationInfo.xml @@ -1,7 +1,7 @@ <component xmlns="http://jetbrains.org/intellij/schema/application-info" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jetbrains.org/intellij/schema/application-info http://jetbrains.org/intellij/schema/ApplicationInfo.xsd"> - <version major="2022" minor="1.2"/> + <version major="2022" minor="1.3"/> <company name="JetBrains s.r.o." url="https://www.jetbrains.com"/> <build number="IC-__BUILD__" date="__BUILD_DATE__" majorReleaseDate="20220412" /> <logo url="/idea_community_logo.png" textcolor="ffffff" progressColor="ffae00" progressY="396" progressHeight="4" /> diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/CompoundPositionManager.java b/java/debugger/impl/src/com/intellij/debugger/engine/CompoundPositionManager.java index b89b2daf11da..63f8c932d959 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/CompoundPositionManager.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/CompoundPositionManager.java @@ -15,6 +15,7 @@ import com.intellij.execution.filters.LineNumbersMapping; import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.fileTypes.FileTypeManager; +import com.intellij.openapi.fileTypes.UnknownFileType; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; @@ -70,7 +71,7 @@ public class CompoundPositionManager implements PositionManagerWithConditionEval private <T> T iterate(Producer<? extends T> processor, T defaultValue, @Nullable FileType fileType, boolean ignorePCE) { for (PositionManager positionManager : myPositionManagers) { - if (fileType != null) { + if (fileType != null && fileType != UnknownFileType.INSTANCE) { Set<? extends FileType> types = positionManager.getAcceptedFileTypes(); if (types != null && !types.contains(fileType)) { continue; diff --git a/java/java-impl/src/com/intellij/spi/psi/SPIFile.java b/java/java-impl/src/com/intellij/spi/psi/SPIFile.java index 91eb1946fd1d..3efd04d0f833 100644 --- a/java/java-impl/src/com/intellij/spi/psi/SPIFile.java +++ b/java/java-impl/src/com/intellij/spi/psi/SPIFile.java @@ -22,6 +22,7 @@ import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; +import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry; import com.intellij.psi.util.ClassUtil; import com.intellij.spi.SPIFileType; import com.intellij.util.IncorrectOperationException; @@ -45,6 +46,9 @@ public class SPIFile extends PsiFileBase { @Override public PsiReference @NotNull [] getReferences() { + PsiReference[] references = ReferenceProvidersRegistry.getReferencesFromProviders(this); + if (references.length > 0) return references; + return ReadAction.compute(() -> { final List<PsiReference> refs = new ArrayList<>(); diff --git a/java/java-impl/src/com/intellij/spi/psi/SPIPackageOrClassReferenceElement.java b/java/java-impl/src/com/intellij/spi/psi/SPIPackageOrClassReferenceElement.java index 16908aca1cdb..7a65f14f380e 100644 --- a/java/java-impl/src/com/intellij/spi/psi/SPIPackageOrClassReferenceElement.java +++ b/java/java-impl/src/com/intellij/spi/psi/SPIPackageOrClassReferenceElement.java @@ -19,6 +19,7 @@ import com.intellij.extapi.psi.ASTWrapperPsiElement; import com.intellij.lang.ASTNode; import com.intellij.openapi.util.TextRange; import com.intellij.psi.*; +import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry; import com.intellij.psi.util.ClassUtil; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.spi.SPIFileType; @@ -109,4 +110,12 @@ public class SPIPackageOrClassReferenceElement extends ASTWrapperPsiElement impl public PsiReference getReference() { return this; } + + @Override + public PsiReference @NotNull [] getReferences() { + PsiReference[] references = ReferenceProvidersRegistry.getReferencesFromProviders(this); + if (references.length > 0) return references; + + return super.getReferences(); + } } diff --git a/java/java-tests/testSrc/com/intellij/java/openapi/vfs/JrtFileSystemTest.java b/java/java-tests/testSrc/com/intellij/java/openapi/vfs/JrtFileSystemTest.java index 47050a9471d8..026431e856bc 100644 --- a/java/java-tests/testSrc/com/intellij/java/openapi/vfs/JrtFileSystemTest.java +++ b/java/java-tests/testSrc/com/intellij/java/openapi/vfs/JrtFileSystemTest.java @@ -6,17 +6,21 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.io.IoTestUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.openapi.vfs.impl.jrt.JrtFileSystemImpl; import com.intellij.openapi.vfs.jrt.JrtFileSystem; import com.intellij.openapi.vfs.newvfs.events.VFileEvent; +import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess; import com.intellij.openapi.vfs.pointers.VirtualFilePointer; import com.intellij.openapi.vfs.pointers.VirtualFilePointerManager; import com.intellij.testFramework.VfsTestUtil; import com.intellij.testFramework.fixtures.BareTestFixtureTestCase; import com.intellij.testFramework.rules.TempDirectory; +import com.intellij.util.UriUtil; +import com.intellij.util.containers.ContainerUtil; import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -30,8 +34,6 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.*; @@ -42,7 +44,7 @@ public class JrtFileSystemTest extends BareTestFixtureTestCase { private final Disposable myDisposable = Disposer.newDisposable(); private Path myTestData; private Path myJrtPath; - private VirtualFile myRoot; + private VirtualFile myRoot; // in jrt:// FS @Before public void setUp() throws IOException { @@ -147,11 +149,27 @@ public class JrtFileSystemTest extends BareTestFixtureTestCase { } private static List<String> childNames(VirtualFile dir) { - return Stream.of(dir.getChildren()).map(VirtualFile::getName).collect(Collectors.toList()); + return ContainerUtil.map(dir.getChildren(), VirtualFile::getName); } private static void assertPointers(VirtualFilePointer[] pointers, boolean valid) { assertThat(pointers).allMatch(p -> p.isValid() == valid); assertThat(pointers).allMatch(p -> p.getFile() == null || p.getFile().isValid()); } + + @Test + public void testJDKInstalledIntoDiskRootUnderWindowsDoesntCauseHorribleThings() { + IoTestUtil.assumeWindows(); + + IoTestUtil.performTestOnWindowsSubst(myJrtPath.toString(), substRoot -> { + VfsRootAccess.allowRootAccess(myDisposable, substRoot.getPath()); + + String substedUrl = "jrt://" + UriUtil.trimTrailingSlashes(FileUtil.toSystemIndependentName(substRoot.getPath())) + "/!/java.base"; + VirtualFilePointer pointer = VirtualFilePointerManager.getInstance().create(substedUrl, myDisposable, null); + assertTrue(pointer.isValid()); + VirtualFile file = pointer.getFile(); + assertNotNull(file); + assertTrue(file.getFileSystem() instanceof JrtFileSystem); + }); + } }
\ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/util/indexing/IndexDiagnosticTest.kt b/java/java-tests/testSrc/com/intellij/util/indexing/IndexDiagnosticTest.kt index 8bdfcbcbaceb..cbda9b713438 100644 --- a/java/java-tests/testSrc/com/intellij/util/indexing/IndexDiagnosticTest.kt +++ b/java/java-tests/testSrc/com/intellij/util/indexing/IndexDiagnosticTest.kt @@ -10,6 +10,7 @@ import com.intellij.openapi.project.getProjectCachePath import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase import com.intellij.util.SystemProperties import com.intellij.util.indexing.diagnostic.IndexDiagnosticDumper +import com.intellij.util.indexing.diagnostic.ScanningType import com.intellij.util.indexing.diagnostic.dto.* import com.intellij.util.indexing.diagnostic.dump.paths.PortableFilePath import org.junit.Assert @@ -73,7 +74,7 @@ class IndexDiagnosticTest : JavaCodeInsightFixtureTestCase() { projectName = "projectName", times = JsonProjectIndexingHistoryTimes( "reason", - false, + ScanningType.PARTIAL, JsonDuration(123), JsonDuration(456), JsonDuration(789), diff --git a/native/MacScreenMenu/src/Menu.m b/native/MacScreenMenu/src/Menu.m index 0e296a732204..f3f6ef3e02b6 100644 --- a/native/MacScreenMenu/src/Menu.m +++ b/native/MacScreenMenu/src/Menu.m @@ -410,11 +410,21 @@ Java_com_intellij_ui_mac_screenmenu_Menu_nativeFindItemByTitle(JNIEnv *env, jobj */ JNIEXPORT jlong JNICALL Java_com_intellij_ui_mac_screenmenu_Menu_nativeGetAppMenu(JNIEnv *env, jclass peerClass) { - NSMenu * mainMenu = [NSApplication sharedApplication].mainMenu; - id appMenu = [mainMenu numberOfItems] > 0 ? [mainMenu itemAtIndex:0] : nil; - if (appMenu != nil) { - appMenu = [appMenu submenu]; - [appMenu retain]; + JNI_COCOA_ENTER(); + __block id appMenu = nil; + dispatch_block_t block = ^{ + NSMenu * mainMenu = [NSApplication sharedApplication].mainMenu; + appMenu = [mainMenu numberOfItems] > 0 ? [mainMenu itemAtIndex:0] : nil; + if (appMenu != nil) { + appMenu = [appMenu submenu]; + [appMenu retain]; + } + }; + if ([NSThread isMainThread]) { + block(); + } else { + dispatch_async_and_wait(dispatch_get_main_queue(), block); } return (jlong)appMenu; + JNI_COCOA_EXIT(); }
\ No newline at end of file diff --git a/platform/analysis-api/src/com/intellij/lang/refactoring/NamesValidator.java b/platform/analysis-api/src/com/intellij/lang/refactoring/NamesValidator.java index 5e80ee5a541c..dad3050949da 100644 --- a/platform/analysis-api/src/com/intellij/lang/refactoring/NamesValidator.java +++ b/platform/analysis-api/src/com/intellij/lang/refactoring/NamesValidator.java @@ -1,43 +1,32 @@ -/* - * Copyright 2000-2017 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. - */ +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.lang.refactoring; import com.intellij.openapi.project.Project; import org.jetbrains.annotations.NotNull; /** - * Instances of NamesValidator are obtained from {@link com.intellij.lang.Language} instance. - * An instance encapsulates knowledge of identifier rules and keyword set of the language. + * Encapsulates the knowledge about identifier rules and keyword set of the assigned language. + * <p> + * Register in {@code com.intellij.lang.namesValidator} extension point. + * @see com.intellij.lang.LanguageNamesValidation + * @see <a href="https://plugins.jetbrains.com/docs/intellij/rename-refactoring.html">Rename Refactoring (IntelliJ Platform Docs)</a> */ public interface NamesValidator { /** * Checks if the specified string is a keyword in the custom language. * - * @param name the string to check. - * @param project the project in the context of which the check is done. - * @return true if the string is a keyword, false otherwise. + * @param name the string to check + * @param project the project in the context of which the check is done + * @return {@code true} if the string is a keyword, {@code false} otherwise */ boolean isKeyword(@NotNull String name, Project project); /** * Checks if the specified string is a valid identifier in the custom language. * - * @param name the string to check. - * @param project the project in the context of which the check is done. - * @return true if the string is a valid identifier, false otherwise. + * @param name the string to check + * @param project the project in the context of which the check is done + * @return {@code true} if the string is a valid identifier, {@code false} otherwise */ boolean isIdentifier(@NotNull String name, Project project); -}
\ No newline at end of file +} diff --git a/platform/core-api/src/com/intellij/openapi/components/AbstractProjectComponent.java b/platform/core-api/src/com/intellij/openapi/components/AbstractProjectComponent.java index b2a12dc2f448..389d1d5ffbd6 100644 --- a/platform/core-api/src/com/intellij/openapi/components/AbstractProjectComponent.java +++ b/platform/core-api/src/com/intellij/openapi/components/AbstractProjectComponent.java @@ -1,10 +1,10 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.openapi.components; import com.intellij.openapi.project.Project; /** - * @deprecated Use {@link ProjectComponent} directly if needed. + * @deprecated Components are deprecated, please see <a href="https://plugins.jetbrains.com/docs/intellij/plugin-components.html">SDK Docs</a> for guidelines on migrating to other APIs. */ @Deprecated public abstract class AbstractProjectComponent implements ProjectComponent { diff --git a/platform/core-api/src/com/intellij/openapi/components/ProjectComponent.java b/platform/core-api/src/com/intellij/openapi/components/ProjectComponent.java index 0c867e13af7e..4f7d43363a76 100644 --- a/platform/core-api/src/com/intellij/openapi/components/ProjectComponent.java +++ b/platform/core-api/src/com/intellij/openapi/components/ProjectComponent.java @@ -1,13 +1,13 @@ -// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.openapi.components; /** - * @deprecated components are deprecated. If you register a class as a project component it will be loaded, its instance will be created and + * @see com.intellij.openapi.project.ProjectManager#TOPIC + * @deprecated Components are deprecated, please see <a href="https://plugins.jetbrains.com/docs/intellij/plugin-components.html">SDK Docs</a> for guidelines on migrating to other APIs. + * <p> + * If you register a class as a project component it will be loaded, its instance will be created and * {@link #initComponent()} and {@link #projectOpened()} methods will be called for each project even if user doesn't use any feature of your - * plugin. Also plugins which declare project components don't support dynamic loading. Please see - * <a href="http://www.jetbrains.org/intellij/sdk/docs/basics/plugin_structure/plugin_components.html">guidelines</a> on migrating to other APIs. - * - * See {@link com.intellij.openapi.project.ProjectManager#TOPIC}. + * plugin. Also, plugins which declare project components don't support dynamic loading. */ @Deprecated public interface ProjectComponent extends BaseComponent { diff --git a/platform/diff-impl/src/com/intellij/diff/impl/DiffViewerWrapper.java b/platform/diff-impl/src/com/intellij/diff/impl/DiffViewerWrapper.java index f3e99bdc42e0..74019a35993f 100644 --- a/platform/diff-impl/src/com/intellij/diff/impl/DiffViewerWrapper.java +++ b/platform/diff-impl/src/com/intellij/diff/impl/DiffViewerWrapper.java @@ -19,6 +19,7 @@ import com.intellij.diff.DiffContext; import com.intellij.diff.FrameDiffTool.DiffViewer; import com.intellij.diff.requests.DiffRequest; import com.intellij.openapi.util.Key; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; /** @@ -27,6 +28,7 @@ import org.jetbrains.annotations.NotNull; * Please, consider using {@link com.intellij.diff.DiffTool}, {@link com.intellij.diff.DiffExtension} or introducing a better extension point into platform, * rather than adding second usage of this one. */ +@ApiStatus.Internal public interface DiffViewerWrapper { Key<DiffViewerWrapper> KEY = Key.create("Diff.DiffViewerWrapper"); diff --git a/platform/editor-ui-api/src/com/intellij/ui/breadcrumbs/BreadcrumbsProvider.java b/platform/editor-ui-api/src/com/intellij/ui/breadcrumbs/BreadcrumbsProvider.java index 92f23b1f0acb..85f4cdd1d365 100644 --- a/platform/editor-ui-api/src/com/intellij/ui/breadcrumbs/BreadcrumbsProvider.java +++ b/platform/editor-ui-api/src/com/intellij/ui/breadcrumbs/BreadcrumbsProvider.java @@ -14,33 +14,34 @@ import java.util.List; import static java.util.Collections.emptyList; /** - * Allows to provide a language-specific breadcrumbs, - * i.e. path to the file root from a selected PSI element. + * Allows providing language-specific breadcrumbs, i.e. path to the file root from a selected PSI element. */ public interface BreadcrumbsProvider { ExtensionPointName<BreadcrumbsProvider> EP_NAME = ExtensionPointName.create("com.intellij.breadcrumbsInfoProvider"); /** - * @return an array of languages supported by this provider + * @return array of languages supported by this provider */ Language[] getLanguages(); /** * @param element that represents a single crumb - * @return {@code true} if the specified element is supported by this provider + * @return {@code true} if this provider supports the specified element */ boolean acceptElement(@NotNull PsiElement element); /** + * Determines the text for a single crumb from the provided element. + * * @param element that represents a single crumb - * @return a text for the specified element + * @return text for the crumb */ @NotNull @NlsSafe String getElementInfo(@NotNull PsiElement element); /** * @param element that represents a single crumb - * @return an icon for the specified element + * @return icon for the crumb */ @Nullable default Icon getElementIcon(@NotNull PsiElement element) { @@ -49,7 +50,7 @@ public interface BreadcrumbsProvider { /** * @param element that represents a single crumb - * @return a description for the specified element + * @return description for the crumb */ @Nullable default @NlsSafe String getElementTooltip(@NotNull PsiElement element) { @@ -58,7 +59,7 @@ public interface BreadcrumbsProvider { /** * @param element that represents a single crumb - * @return an element that represents a parent crumb, or {@code null} + * @return element that represents a parent crumb, or {@code null} */ @Nullable default PsiElement getParent(@NotNull PsiElement element) { @@ -69,7 +70,7 @@ public interface BreadcrumbsProvider { * Reserved for future releases. Not supported yet. * * @param element that represents a single crumb - * @return a list of elements to navigate + * @return list of elements to navigate */ @NotNull default List<PsiElement> getChildren(@NotNull PsiElement element) { @@ -78,7 +79,7 @@ public interface BreadcrumbsProvider { /** * @param element that represents a single crumb - * @return a list of actions for context menu + * @return list of actions for the context menu */ @NotNull default List<? extends Action> getContextActions(@NotNull PsiElement element) { diff --git a/platform/extensions/src/com/intellij/openapi/components/BaseComponent.java b/platform/extensions/src/com/intellij/openapi/components/BaseComponent.java index 372cd609de44..da9fea00876e 100644 --- a/platform/extensions/src/com/intellij/openapi/components/BaseComponent.java +++ b/platform/extensions/src/com/intellij/openapi/components/BaseComponent.java @@ -1,10 +1,8 @@ -// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.openapi.components; /** - * The base interface class for all components. - * - * @deprecated Components are deprecated; please see <a href="http://www.jetbrains.org/intellij/sdk/docs/basics/plugin_structure/plugin_components.html">SDK Docs</a> for guidelines on migrating to other APIs. + * @deprecated Components are deprecated, please see <a href="https://plugins.jetbrains.com/docs/intellij/plugin-components.html">SDK Docs</a> for guidelines on migrating to other APIs. */ @Deprecated public interface BaseComponent extends NamedComponent { diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/util/ExternalSystemApiUtil.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/util/ExternalSystemApiUtil.java index eaeeaed79335..a6bc66e4ebcf 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/util/ExternalSystemApiUtil.java +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/util/ExternalSystemApiUtil.java @@ -713,6 +713,15 @@ public final class ExternalSystemApiUtil { return projectInfo.getExternalProjectStructure(); } + public static @Nullable DataNode<ProjectData> findProjectNode( + @NotNull Project project, + @NotNull ProjectSystemId systemId, + @NotNull String projectPath + ) { + ExternalProjectInfo projectInfo = findProjectInfo(project, systemId, projectPath); + if (projectInfo == null) return null; + return projectInfo.getExternalProjectStructure(); + } @ApiStatus.Experimental public static @Nullable ExternalProjectInfo findProjectInfo(@NotNull Project project, @NotNull ProjectSystemId systemId, diff --git a/platform/lang-api/src/com/intellij/codeInsight/completion/PlainTextSymbolCompletionContributor.java b/platform/lang-api/src/com/intellij/codeInsight/completion/PlainTextSymbolCompletionContributor.java index d8b5196057a0..74a017a0ce9a 100644 --- a/platform/lang-api/src/com/intellij/codeInsight/completion/PlainTextSymbolCompletionContributor.java +++ b/platform/lang-api/src/com/intellij/codeInsight/completion/PlainTextSymbolCompletionContributor.java @@ -8,21 +8,24 @@ import org.jetbrains.annotations.NotNull; import java.util.Collection; /** - * A language-specific completion contributor which provides reasonable amount of symbols declared in given file - * (likely only top-level declarations). Such contributor could be used in plain text editors like VCS commit message field - * to help users referring to code symbols from the text. + * A language-specific completion contributor that should provide a reasonable number of symbols declared in a given file, e.g., + * only top-level declarations. + * Such contributors can also help to complete words in plain text editors like VCS commit messages. + * + * Please see the <a href="https://plugins.jetbrains.com/docs/intellij/code-completion.html#contributor-based-completion">IntelliJ Platform Docs</a> + * for a high-level overview of contributor-based completion. * * @see PlainTextSymbolCompletionContributorEP */ public interface PlainTextSymbolCompletionContributor { /** - * Adds lookup elements from given file. + * Collects lookup elements from a given file. * - * @param file file to add elements from. + * @param file file for extracting lookup elements. * @param invocationCount number of times the completion was invoked (see {@link CompletionParameters#getInvocationCount()}). - * @param prefix a prefix string. It's not required to return only matches starting with prefix, but this may be used to improve - * performance. - * @return a collection of {@link LookupElement}'s to suggest. + * @param prefix prefix string that can be used to filter candidates. + * It's not required to return only matches starting with prefix, but it can improve performance. + * @return collection of suggestions used for completion. */ @NotNull Collection<LookupElement> getLookupElements(@NotNull PsiFile file, int invocationCount, @NotNull String prefix); diff --git a/platform/lang-api/src/com/intellij/lang/surroundWith/SurroundDescriptor.java b/platform/lang-api/src/com/intellij/lang/surroundWith/SurroundDescriptor.java index 6ac9411ca140..5339c65d5a0f 100644 --- a/platform/lang-api/src/com/intellij/lang/surroundWith/SurroundDescriptor.java +++ b/platform/lang-api/src/com/intellij/lang/surroundWith/SurroundDescriptor.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2015 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. - */ +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.lang.surroundWith; import com.intellij.psi.PsiElement; @@ -20,14 +6,14 @@ import com.intellij.psi.PsiFile; import org.jetbrains.annotations.NotNull; /** - * Defines a code fragment type on which the Surround With action can be used for files + * Defines a code fragment type on which the <em>Code | Surround With</em> action can be used for files * in a custom language. All surround descriptors registered for a language are queried * sequentially, and as soon as one is found that returns a non-empty list of elements * from {@link #getElementsToSurround(PsiFile, int, int)}, the user * is prompted to choose a specific surrounder for that surround descriptor. * - * @author ven * @see com.intellij.lang.LanguageSurrounders + * @see <a href="https://plugins.jetbrains.com/docs/intellij/surround-with.html">Surround With (IntelliJ Platform Docs)</a> */ public interface SurroundDescriptor { /** @@ -35,7 +21,7 @@ public interface SurroundDescriptor { * the specified selection in the specified file, or an empty array if no surrounders * from this surround descriptor are applicable to the specified selection. * - * @param file the file where elements are to be surrounded. + * @param file the file where elements are to be surrounded * @param startOffset the selection start offset, with whitespaces skipped * @param endOffset the selection end offset, with whitespaces skipped * @return the elements to be surrounded, or an empty array if cannot surround @@ -43,12 +29,13 @@ public interface SurroundDescriptor { PsiElement @NotNull [] getElementsToSurround(PsiFile file, int startOffset, int endOffset); /** - * Returns the list of surrounders (surround templates) which can be used for this - * code fragment type. - * - * @return the list of surrounders. + * @return the list of surrounders (surround templates) which can be used for this code fragment type */ Surrounder @NotNull [] getSurrounders(); + /** + * If {@code true} then only surrounders from all applicable exclusive surround descriptors + * will be included in the <em>Code | Surround With</em> action. + */ boolean isExclusive(); } diff --git a/platform/lang-api/src/com/intellij/lang/surroundWith/Surrounder.java b/platform/lang-api/src/com/intellij/lang/surroundWith/Surrounder.java index f75095e3f0cd..dcfa6b5a12b3 100644 --- a/platform/lang-api/src/com/intellij/lang/surroundWith/Surrounder.java +++ b/platform/lang-api/src/com/intellij/lang/surroundWith/Surrounder.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2009 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. - */ +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.lang.surroundWith; import com.intellij.openapi.editor.Editor; @@ -26,19 +12,17 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** - * Defines a single template which can be used in Surround With. + * Defines a single template which can be used in <em>Code | Surround With</em> action. * - * @author ven * @see SurroundDescriptor + * @see <a href="https://plugins.jetbrains.com/docs/intellij/surround-with.html">Surround With (IntelliJ Platform Docs)</a> */ public interface Surrounder { Surrounder[] EMPTY_ARRAY = new Surrounder[0]; ArrayFactory<Surrounder> myArrayFactory = count -> count == 0 ? EMPTY_ARRAY : new Surrounder[count]; /** - * Returns the user-visible name of the Surround With template. - * - * @return the template name + * @return the user-visible name of the <em>Surround With</em> template */ @NlsActions.ActionText String getTemplateDescription(); @@ -47,16 +31,14 @@ public interface Surrounder { * Checks if the template can be used to surround the specified range of elements. * * @param elements the elements to be surrounded - * @return true if the template is applicable to the elements, false otherwise. + * @return {@code true} if the template is applicable to the elements, {@code false} otherwise */ boolean isApplicable(PsiElement @NotNull [] elements); /** - * Performs the Surround With action on the specified range of elements. + * Performs the <em>Code | Surround With</em> action on the specified range of elements. * - * @param project the project containing the elements. - * @param editor the editor in which the action is invoked. - * @param elements the elements to be surrounded. + * @param elements the elements to be surrounded * @return range to select/to position the caret */ @Nullable diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/StatusItemMerger.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/StatusItemMerger.java index 718f5a4f9126..5ab75f2fc0c8 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/StatusItemMerger.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/StatusItemMerger.java @@ -8,6 +8,7 @@ import org.jetbrains.annotations.Nullable; import java.util.Objects; +/** An extension allowing to merge adjacent severity-based icons in the editor's code analysis indicator */ public abstract class StatusItemMerger { static final ExtensionPointName<StatusItemMerger> EP_NAME = ExtensionPointName.create("com.intellij.daemon.statusItemMerger"); diff --git a/platform/lang-impl/src/com/intellij/find/EditorSearchSession.java b/platform/lang-impl/src/com/intellij/find/EditorSearchSession.java index 28d7e6a05eb5..7c9dc4133681 100644 --- a/platform/lang-impl/src/com/intellij/find/EditorSearchSession.java +++ b/platform/lang-impl/src/com/intellij/find/EditorSearchSession.java @@ -1,4 +1,4 @@ -// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.find; @@ -750,7 +750,7 @@ public class EditorSearchSession implements SearchSession, private class ExcludeAction extends ButtonAction implements LightEditCompatible { ExcludeAction() { - super("", 'l'); + super(FindBundle.message("button.exclude"), 'l'); } @Override diff --git a/platform/lang-impl/src/com/intellij/ide/actions/searcheverywhere/ActionSearchEverywhereContributor.java b/platform/lang-impl/src/com/intellij/ide/actions/searcheverywhere/ActionSearchEverywhereContributor.java index 17b81e1d40d9..dff8210c7a7f 100644 --- a/platform/lang-impl/src/com/intellij/ide/actions/searcheverywhere/ActionSearchEverywhereContributor.java +++ b/platform/lang-impl/src/com/intellij/ide/actions/searcheverywhere/ActionSearchEverywhereContributor.java @@ -232,12 +232,12 @@ public class ActionSearchEverywhereContributor implements WeightedSearchEverywhe private FoundItemDescriptor<GotoActionModel.MatchedValue> getMLWeightedItemDescriptor(@NotNull SearchEverywhereMlService service, @NotNull GotoActionModel.MatchedValue element) { - if (element.getType() == GotoActionModel.MatchedValueType.ABBREVIATION) { - return new FoundItemDescriptor<>(element, element.getMatchingDegree(), 1.0); - } - double mlWeight = service.getMlWeight(this, element, element.getMatchingDegree()); if (mlWeight > 0) { + if (element.getType() == GotoActionModel.MatchedValueType.ABBREVIATION) { + return new FoundItemDescriptor<>(element, element.getMatchingDegree(), 1.0); + } + return new FoundItemDescriptor<>(element, element.getMatchingDegree(), mlWeight); } return new FoundItemDescriptor<>(element, element.getMatchingDegree()); diff --git a/platform/lang-impl/src/com/intellij/ide/actions/searcheverywhere/SEResultsEqualityProvider.kt b/platform/lang-impl/src/com/intellij/ide/actions/searcheverywhere/SEResultsEqualityProvider.kt index 1c2932f9f29b..126803303731 100644 --- a/platform/lang-impl/src/com/intellij/ide/actions/searcheverywhere/SEResultsEqualityProvider.kt +++ b/platform/lang-impl/src/com/intellij/ide/actions/searcheverywhere/SEResultsEqualityProvider.kt @@ -3,14 +3,43 @@ package com.intellij.ide.actions.searcheverywhere import com.intellij.openapi.extensions.ExtensionPointName +/** + * Equality provider can be used to compare items found by different (or sometimes same) instances of + * [SearchEverywhereContributor] and decide that those items are pointing to the same entity. + * + * For example, following items can be instances of different classes, but the same entities: + * - Same file returned by [FileSearchEverywhereContributor] and [RecentFilesSEContributor] + * - Java public class and .java file containing this class + * - Different links/wrappers to the same [com.intellij.psi.PsiElement] + * - etc. + */ interface SEResultsEqualityProvider { + + /** + * List of possible actions for [compareItems] method. + */ sealed class SEEqualElementsActionType { + + /** + * Nothing to do. + * Should be used when found item is not equal to any already found one. + */ object DoNothing : SEEqualElementsActionType() { override fun combine(another: SEEqualElementsActionType): SEEqualElementsActionType = another } + + /** + * New found item should be skipped. + * Should be used when better presentation of found entity already exists in results. + */ object Skip : SEEqualElementsActionType() { override fun combine(another: SEEqualElementsActionType): SEEqualElementsActionType = if (another is Replace) another else this } + + /** + * Already existing item `toBeReplaced` should be replaced with the new found item. + * Should be used when equal item already exists in results but new one represents corresponding entity better. + */ data class Replace(val toBeReplaced: List<SearchEverywhereFoundElementInfo>) : SEEqualElementsActionType() { constructor(toBeReplaced: SearchEverywhereFoundElementInfo) : this(listOf(toBeReplaced)) @@ -20,6 +49,14 @@ interface SEResultsEqualityProvider { abstract fun combine(another: SEEqualElementsActionType): SEEqualElementsActionType } + /** + * Compare just found [SearchEverywhereFoundElementInfo] with list of already found items and decide how this new item should be handled. + * + * See [SEEqualElementsActionType] for possible actions + * + * @param newItem new found item. This item is suggested to be added to results list. + * @param alreadyFoundItems list of already found results. Those items are already shown in results list. + */ fun compareItems(newItem: SearchEverywhereFoundElementInfo, alreadyFoundItems: List<SearchEverywhereFoundElementInfo>): SEEqualElementsActionType companion object { diff --git a/platform/lang-impl/src/com/intellij/ide/projectView/ProjectViewNodeDecorator.java b/platform/lang-impl/src/com/intellij/ide/projectView/ProjectViewNodeDecorator.java index 6f94396c0ab5..3080c011b7e3 100644 --- a/platform/lang-impl/src/com/intellij/ide/projectView/ProjectViewNodeDecorator.java +++ b/platform/lang-impl/src/com/intellij/ide/projectView/ProjectViewNodeDecorator.java @@ -9,6 +9,9 @@ import com.intellij.ui.ColoredTreeCellRenderer; /** * Allows modifying the presentation of project view and package dependencies view nodes. * + * Please see the <a href="https://plugins.jetbrains.com/docs/intellij/project-view.html#decorating-project-view-nodes">IntelliJ Platform Docs</a> + * for a high-level overview. + * * @see TreeStructureProvider */ public interface ProjectViewNodeDecorator { diff --git a/platform/lang-impl/src/com/intellij/psi/impl/cache/impl/IndexTodoCacheManagerImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/cache/impl/IndexTodoCacheManagerImpl.java index dc602d12626f..e28674fc52c1 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/cache/impl/IndexTodoCacheManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/cache/impl/IndexTodoCacheManagerImpl.java @@ -5,6 +5,7 @@ package com.intellij.psi.impl.cache.impl; import com.intellij.injected.editor.VirtualFileWindow; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; @@ -52,14 +53,25 @@ public class IndexTodoCacheManagerImpl implements TodoCacheManager { @Override public boolean processFilesWithTodoItems(@NotNull Processor<? super PsiFile> processor) { if (myProject.isDefault()) return true; - GlobalSearchScope scope = GlobalSearchScope.allScope(myProject); + GlobalSearchScope scope = new GlobalSearchScope(myProject) { + @Override + public boolean isSearchInModuleContent(@NotNull Module module) { return true; } + + @Override + public boolean isSearchInLibraries() { return false; } + + @Override + public boolean contains(@NotNull VirtualFile file) { + return TodoIndexers.belongsToProject(myProject, file); + } + }; ConcurrentBitSet idSet = ConcurrentBitSet.create(); ManagingFS fs = ManagingFS.getInstance(); PsiManager psiManager = PsiManager.getInstance(myProject); IntPredicate consumer = fileId -> { VirtualFile file = fs.findFileById(fileId); - if (file == null || !file.isValid() || !scope.contains(file) || !TodoIndexers.belongsToProject(myProject, file)) return true; + if (file == null || !file.isValid() || !scope.contains(file)) return true; PsiFile psiFile = psiManager.findFile(file); return psiFile == null || processor.process(psiFile); }; diff --git a/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexProjectHandler.java b/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexProjectHandler.java index 51e6618a34c9..b8878cd81cbc 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexProjectHandler.java +++ b/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexProjectHandler.java @@ -1,4 +1,4 @@ -// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.util.indexing; import com.intellij.diagnostic.PerformanceWatcher; @@ -15,6 +15,7 @@ import com.intellij.util.indexing.contentQueue.IndexUpdateRunner; import com.intellij.util.indexing.diagnostic.IndexDiagnosticDumper; import com.intellij.util.indexing.diagnostic.ProjectIndexingHistoryImpl; import com.intellij.util.indexing.diagnostic.ScanningStatistics; +import com.intellij.util.indexing.diagnostic.ScanningType; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -92,7 +93,7 @@ public final class FileBasedIndexProjectHandler { @NotNull Project project, long refreshedFilesCalcDuration) { ProjectIndexingHistoryImpl projectIndexingHistory = - new ProjectIndexingHistoryImpl(project, "On refresh of " + files.size() + " files", false); + new ProjectIndexingHistoryImpl(project, "On refresh of " + files.size() + " files", ScanningType.REFRESH); IndexDiagnosticDumper.getInstance().onIndexingStarted(projectIndexingHistory); ((FileBasedIndexImpl)FileBasedIndex.getInstance()).fireUpdateStarted(project); diff --git a/platform/lang-impl/src/com/intellij/util/indexing/ForceIndexRescanningAction.java b/platform/lang-impl/src/com/intellij/util/indexing/ForceIndexRescanningAction.java index a59c83922d21..e464c4142cf5 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/ForceIndexRescanningAction.java +++ b/platform/lang-impl/src/com/intellij/util/indexing/ForceIndexRescanningAction.java @@ -1,9 +1,10 @@ -// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.util.indexing; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; +import com.intellij.util.indexing.diagnostic.ScanningType; import org.jetbrains.annotations.NotNull; final class ForceIndexRescanningAction extends DumbAwareAction { @@ -15,7 +16,8 @@ final class ForceIndexRescanningAction extends DumbAwareAction { false, false, null, - "Force re-scanning"); + "Force re-scanning", + ScanningType.FULL_FORCED); task.queue(project); } } diff --git a/platform/lang-impl/src/com/intellij/util/indexing/RescanIndexesAction.kt b/platform/lang-impl/src/com/intellij/util/indexing/RescanIndexesAction.kt index d381ab9056d5..1f659a703777 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/RescanIndexesAction.kt +++ b/platform/lang-impl/src/com/intellij/util/indexing/RescanIndexesAction.kt @@ -1,4 +1,4 @@ -// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.util.indexing import com.intellij.ide.actions.cache.* @@ -7,13 +7,13 @@ import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.util.ProgressIndicatorUtils import com.intellij.openapi.project.DumbModeTask -import com.intellij.openapi.project.Project import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.VirtualFileWithId import com.intellij.psi.stubs.StubTreeBuilder import com.intellij.psi.stubs.StubUpdatingIndex import com.intellij.util.BooleanFunction import com.intellij.util.indexing.diagnostic.ProjectIndexingHistoryImpl +import com.intellij.util.indexing.diagnostic.ScanningType import com.intellij.util.indexing.roots.IndexableFilesIterator import com.intellij.util.indexing.roots.ProjectIndexableFilesIteratorImpl import org.jetbrains.annotations.ApiStatus @@ -39,7 +39,9 @@ class RescanIndexesAction : RecoveryAction { if (recoveryScope is FilesRecoveryScope) { predefinedIndexableFilesIterators = recoveryScope.files.map { ProjectIndexableFilesIteratorImpl(it) } } - object : UnindexedFilesUpdater(project, predefinedIndexableFilesIterators, "Rescanning indexes recovery action") { + object : UnindexedFilesUpdater(project, false, false, + predefinedIndexableFilesIterators, "Rescanning indexes recovery action", + if(predefinedIndexableFilesIterators == null) ScanningType.FULL_FORCED else ScanningType.PARTIAL_FORCED) { private val stubIndex = runCatching { (FileBasedIndex.getInstance() as FileBasedIndexImpl).getIndex(StubUpdatingIndex.INDEX_ID) } .onFailure { logger<RescanIndexesAction>().error(it) }.getOrNull() diff --git a/platform/lang-impl/src/com/intellij/util/indexing/UnindexedFilesUpdater.java b/platform/lang-impl/src/com/intellij/util/indexing/UnindexedFilesUpdater.java index 87597de332ba..53798fa938ca 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/UnindexedFilesUpdater.java +++ b/platform/lang-impl/src/com/intellij/util/indexing/UnindexedFilesUpdater.java @@ -1,4 +1,4 @@ -// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.util.indexing; import com.intellij.diagnostic.PerformanceWatcher; @@ -36,6 +36,7 @@ import com.intellij.util.indexing.contentQueue.IndexUpdateRunner; import com.intellij.util.indexing.diagnostic.IndexDiagnosticDumper; import com.intellij.util.indexing.diagnostic.ProjectIndexingHistoryImpl; import com.intellij.util.indexing.diagnostic.ScanningStatistics; +import com.intellij.util.indexing.diagnostic.ScanningType; import com.intellij.util.indexing.diagnostic.dto.JsonScanningStatistics; import com.intellij.util.indexing.roots.IndexableFileScanner; import com.intellij.util.indexing.roots.IndexableFilesDeduplicateFilter; @@ -85,6 +86,7 @@ public class UnindexedFilesUpdater extends DumbModeTask { private final boolean myStartSuspended; private final boolean myOnProjectOpen; private final @NonNls String myIndexingReason; + private final @NotNull ScanningType myScanningType; private final PushedFilePropertiesUpdater myPusher; private final @Nullable List<IndexableFilesIterator> myPredefinedIndexableFilesIterators; @@ -92,11 +94,13 @@ public class UnindexedFilesUpdater extends DumbModeTask { boolean startSuspended, boolean onProjectOpen, @Nullable List<IndexableFilesIterator> predefinedIndexableFilesIterators, - @Nullable @NonNls String indexingReason) { + @Nullable @NonNls String indexingReason, + @NotNull ScanningType scanningType) { myProject = project; myStartSuspended = startSuspended; myOnProjectOpen = onProjectOpen; myIndexingReason = indexingReason; + myScanningType = scanningType; myPusher = PushedFilePropertiesUpdater.getInstance(myProject); myPredefinedIndexableFilesIterators = predefinedIndexableFilesIterators; @@ -156,7 +160,8 @@ public class UnindexedFilesUpdater extends DumbModeTask { myStartSuspended, false, mergeIterators(myPredefinedIndexableFilesIterators, ((UnindexedFilesUpdater)taskFromQueue).myPredefinedIndexableFilesIterators), - reason + reason, + ScanningType.Companion.merge(oldTask.myScanningType, oldTask.myScanningType) ); } @@ -177,17 +182,18 @@ public class UnindexedFilesUpdater extends DumbModeTask { // If we haven't succeeded to fully scan the project content yet, then we must keep trying to run // file based index extensions for all project files until at least one of UnindexedFilesUpdater-s finishes without cancellation. // This is important, for example, for shared indexes: all files must be associated with their locally available shared index chunks. - this(project, false, false, null, null); + this(project, false, false, null, null, ScanningType.FULL); } public UnindexedFilesUpdater(@NotNull Project project, @Nullable @NonNls String indexingReason) { - this(project, false, false, null, indexingReason); + this(project, false, false, null, indexingReason, ScanningType.FULL); } public UnindexedFilesUpdater(@NotNull Project project, @Nullable List<IndexableFilesIterator> predefinedIndexableFilesIterators, @Nullable @NonNls String indexingReason) { - this(project, false, false, predefinedIndexableFilesIterators, indexingReason); + this(project, false, false, predefinedIndexableFilesIterators, indexingReason, + predefinedIndexableFilesIterators == null ? ScanningType.FULL : ScanningType.PARTIAL); } private void updateUnindexedFiles(@NotNull ProjectIndexingHistoryImpl projectIndexingHistory, @NotNull ProgressIndicator indicator) { @@ -573,7 +579,8 @@ public class UnindexedFilesUpdater extends DumbModeTask { } protected @NotNull ProjectIndexingHistoryImpl performScanningAndIndexing(@NotNull ProgressIndicator indicator) { - ProjectIndexingHistoryImpl projectIndexingHistory = new ProjectIndexingHistoryImpl(myProject, myIndexingReason, isFullIndexUpdate()); + ProjectIndexingHistoryImpl projectIndexingHistory = + new ProjectIndexingHistoryImpl(myProject, myIndexingReason, myScanningType); myIndex.loadIndexes(); myIndex.filesUpdateStarted(myProject, isFullIndexUpdate()); IndexDiagnosticDumper.getInstance().onIndexingStarted(projectIndexingHistory); @@ -643,17 +650,21 @@ public class UnindexedFilesUpdater extends DumbModeTask { return SystemProperties.getBooleanProperty("ij.indexes.skip.initial.refresh", false); } - public static void scanAndIndexProjectAfterOpen(@NotNull Project project, boolean startSuspended, @Nullable @NonNls String indexingReason) { + public static void scanAndIndexProjectAfterOpen(@NotNull Project project, + boolean startSuspended, + @Nullable @NonNls String indexingReason) { if (TestModeFlags.is(INDEX_PROJECT_WITH_MANY_UPDATERS_TEST_KEY)) { LOG.assertTrue(ApplicationManager.getApplication().isUnitTestMode()); List<IndexableFilesIterator> iterators = collectProviders(project, (FileBasedIndexImpl)FileBasedIndex.getInstance()); for (IndexableFilesIterator iterator : iterators) { - new UnindexedFilesUpdater(project, startSuspended, true, Collections.singletonList(iterator), indexingReason).queue(project); + new UnindexedFilesUpdater(project, startSuspended, true, Collections.singletonList(iterator), indexingReason, + ScanningType.FULL_ON_PROJECT_OPEN).queue(project); } project.putUserData(CONTENT_SCANNED, true); } else { - new UnindexedFilesUpdater(project, startSuspended, true, null, indexingReason).queue(project); + new UnindexedFilesUpdater(project, startSuspended, true, null, indexingReason, ScanningType.FULL_ON_PROJECT_OPEN). + queue(project); } } } diff --git a/platform/lang-impl/src/com/intellij/util/indexing/diagnostic/ProjectIndexingHistory.kt b/platform/lang-impl/src/com/intellij/util/indexing/diagnostic/ProjectIndexingHistory.kt index f7fa953ce974..495348b196db 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/diagnostic/ProjectIndexingHistory.kt +++ b/platform/lang-impl/src/com/intellij/util/indexing/diagnostic/ProjectIndexingHistory.kt @@ -33,6 +33,56 @@ interface ProjectIndexingHistory { val visibleTimeToAllThreadsTimeRatio: Double } +/** + * isFull - if the whole project was rescanned (instead of a part of it) + */ +enum class ScanningType(val isFull: Boolean) { + /** + * Full project rescan forced by user via Repair IDE action + */ + FULL_FORCED(true), + + /** + * It's mandatory full project rescan on project open + */ + FULL_ON_PROJECT_OPEN(true), + + /** + * Full project rescan requested by some code + */ + FULL(true), + + + /** + * Partial rescan forced by user via Repair IDE action on a limited scope (not full project) + */ + PARTIAL_FORCED(false), + + /** + * Partial project rescan requested by some code + */ + PARTIAL(false), + + /** + * Some files were considered changed and therefore rescanned + */ + REFRESH(false); + + companion object { + fun merge(first: ScanningType, second: ScanningType): ScanningType = returnFirstFound(first, second, + FULL_FORCED, FULL_ON_PROJECT_OPEN, FULL, + PARTIAL_FORCED, PARTIAL, + REFRESH) + + private fun returnFirstFound(first: ScanningType, second: ScanningType, vararg types: ScanningType): ScanningType { + for (type in types) { + if (first == type || second == type) return type + } + throw IllegalStateException("Unexpected ScanningType $first $second") + } + } +} + interface StatsPerFileType { val totalNumberOfFiles: Int val totalBytes: BytesNumber @@ -58,7 +108,7 @@ interface StatsPerIndexer { interface IndexingTimes { val indexingReason: String? - val wasFullIndexing: Boolean + val scanningType: ScanningType val updatingStart: ZonedDateTime val totalUpdatingTime: TimeNano val updatingEnd: ZonedDateTime diff --git a/platform/lang-impl/src/com/intellij/util/indexing/diagnostic/ProjectIndexingHistoryFusReporterListener.kt b/platform/lang-impl/src/com/intellij/util/indexing/diagnostic/ProjectIndexingHistoryFusReporterListener.kt index b3643550c5ce..b845875b42a6 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/diagnostic/ProjectIndexingHistoryFusReporterListener.kt +++ b/platform/lang-impl/src/com/intellij/util/indexing/diagnostic/ProjectIndexingHistoryFusReporterListener.kt @@ -11,7 +11,9 @@ import com.intellij.openapi.fileTypes.FileType import com.intellij.openapi.fileTypes.FileTypeManager import com.intellij.openapi.project.Project import com.intellij.util.indexing.diagnostic.dto.toMillis +import java.util.* import java.util.concurrent.TimeUnit +import kotlin.collections.HashMap import kotlin.math.roundToLong class ProjectIndexingHistoryFusReporterListener : ProjectIndexingHistoryListener { @@ -48,7 +50,7 @@ class ProjectIndexingHistoryFusReporterListener : ProjectIndexingHistoryListener ProjectIndexingHistoryFusReporter.reportIndexingFinished( projectIndexingHistory.project, projectIndexingHistory.indexingSessionId, - projectIndexingHistory.times.wasFullIndexing, + projectIndexingHistory.times.scanningType, projectIndexingHistory.times.totalUpdatingTime.toMillis(), projectIndexingHistory.times.indexingDuration.toMillis(), scanningTime, @@ -78,13 +80,14 @@ class ProjectIndexingHistoryFusReporterListener : ProjectIndexingHistoryListener } object ProjectIndexingHistoryFusReporter : CounterUsagesCollector() { - private val GROUP = EventLogGroup("indexing.statistics", 5) + private val GROUP = EventLogGroup("indexing.statistics", 6) override fun getGroup() = GROUP private val indexingSessionId = EventFields.Long("indexing_session_id") private val isFullIndexing = EventFields.Boolean("is_full") + private val scanningType = EventFields.Enum<ScanningType>("type") { type -> type.name.lowercase(Locale.ENGLISH) } private val totalTime = EventFields.Long("total_time") private val indexingTime = EventFields.Long("indexing_time") private val scanningTime = EventFields.Long("scanning_time") @@ -112,6 +115,7 @@ object ProjectIndexingHistoryFusReporter : CounterUsagesCollector() { "finished", indexingSessionId, isFullIndexing, + scanningType, totalTime, indexingTime, scanningTime, @@ -134,7 +138,7 @@ object ProjectIndexingHistoryFusReporter : CounterUsagesCollector() { fun reportIndexingFinished( project: Project, indexingSessionId: Long, - wasFullIndexing: Boolean, + scanningType: ScanningType, totalTime: Long, indexingTime: Long, scanningTime: Long, @@ -150,7 +154,8 @@ object ProjectIndexingHistoryFusReporter : CounterUsagesCollector() { indexingFinished.log( project, this.indexingSessionId.with(indexingSessionId), - this.isFullIndexing.with(wasFullIndexing), + this.isFullIndexing.with(scanningType.isFull), + this.scanningType.with(scanningType), this.totalTime.with(totalTime), this.indexingTime.with(indexingTime), this.scanningTime.with(scanningTime), diff --git a/platform/lang-impl/src/com/intellij/util/indexing/diagnostic/ProjectIndexingHistoryImpl.kt b/platform/lang-impl/src/com/intellij/util/indexing/diagnostic/ProjectIndexingHistoryImpl.kt index 198a2c2a3ce4..fa47fb1b29ec 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/diagnostic/ProjectIndexingHistoryImpl.kt +++ b/platform/lang-impl/src/com/intellij/util/indexing/diagnostic/ProjectIndexingHistoryImpl.kt @@ -19,7 +19,7 @@ import kotlin.reflect.KMutableProperty1 @ApiStatus.Internal data class ProjectIndexingHistoryImpl(override val project: Project, override val indexingReason: String?, - private val wasFullIndexing: Boolean) : ProjectIndexingHistory { + private val scanningType: ScanningType) : ProjectIndexingHistory { private companion object { val indexingSessionIdSequencer = AtomicLong() val log = thisLogger() @@ -31,7 +31,7 @@ data class ProjectIndexingHistoryImpl(override val project: Project, override val times: IndexingTimes by ::timesImpl - private val timesImpl = IndexingTimesImpl(indexingReason = indexingReason, wasFullIndexing = wasFullIndexing, + private val timesImpl = IndexingTimesImpl(indexingReason = indexingReason, scanningType = scanningType, updatingStart = ZonedDateTime.now(ZoneOffset.UTC), totalUpdatingTime = System.nanoTime()) override val scanningStatistics = arrayListOf<JsonScanningStatistics>() @@ -324,7 +324,7 @@ data class ProjectIndexingHistoryImpl(override val project: Project, data class IndexingTimesImpl( override val indexingReason: String?, - override val wasFullIndexing: Boolean, + override val scanningType: ScanningType, override val updatingStart: ZonedDateTime, override var totalUpdatingTime: TimeNano, override var updatingEnd: ZonedDateTime = updatingStart, diff --git a/platform/lang-impl/src/com/intellij/util/indexing/diagnostic/dto/JsonConverter.kt b/platform/lang-impl/src/com/intellij/util/indexing/diagnostic/dto/JsonConverter.kt index 4b8fa5a6874f..f3d2684389ff 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/diagnostic/dto/JsonConverter.kt +++ b/platform/lang-impl/src/com/intellij/util/indexing/diagnostic/dto/JsonConverter.kt @@ -85,7 +85,7 @@ fun IndexingFileSetStatistics.IndexedFile.toJson() = JsonFileProviderIndexStatis fun IndexingTimes.toJson() = JsonProjectIndexingHistoryTimes( indexingReason = indexingReason, - wasFullIndexing = wasFullIndexing, + scanningType = scanningType, totalUpdatingTime = JsonDuration(totalUpdatingTime), indexingTime = JsonDuration(indexingDuration.toNanos()), contentLoadingVisibleTime = JsonDuration(contentLoadingVisibleDuration.toNanos()), diff --git a/platform/lang-impl/src/com/intellij/util/indexing/diagnostic/dto/JsonProjectIndexingHistoryTimes.kt b/platform/lang-impl/src/com/intellij/util/indexing/diagnostic/dto/JsonProjectIndexingHistoryTimes.kt index b9e498f53150..d90b8ff2fc27 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/diagnostic/dto/JsonProjectIndexingHistoryTimes.kt +++ b/platform/lang-impl/src/com/intellij/util/indexing/diagnostic/dto/JsonProjectIndexingHistoryTimes.kt @@ -3,12 +3,13 @@ package com.intellij.util.indexing.diagnostic.dto import com.fasterxml.jackson.annotation.JsonIgnoreProperties import com.fasterxml.jackson.annotation.JsonInclude +import com.intellij.util.indexing.diagnostic.ScanningType @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) data class JsonProjectIndexingHistoryTimes( val indexingReason: String? = null, - val wasFullIndexing: Boolean = false, + val scanningType: ScanningType = ScanningType.FULL, val totalUpdatingTime: JsonDuration = JsonDuration(), val indexingTime: JsonDuration = JsonDuration(), val contentLoadingVisibleTime: JsonDuration = JsonDuration(), diff --git a/platform/lang-impl/src/com/intellij/util/indexing/diagnostic/presentation/jsonToHtmlConverter.kt b/platform/lang-impl/src/com/intellij/util/indexing/diagnostic/presentation/jsonToHtmlConverter.kt index 9a23f69063e2..ceaec19987d4 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/diagnostic/presentation/jsonToHtmlConverter.kt +++ b/platform/lang-impl/src/com/intellij/util/indexing/diagnostic/presentation/jsonToHtmlConverter.kt @@ -14,6 +14,7 @@ import com.intellij.util.indexing.diagnostic.JsonSharedIndexDiagnosticEvent import com.intellij.util.indexing.diagnostic.dto.* import org.intellij.lang.annotations.Language import org.jetbrains.annotations.Nls +import java.util.* fun createAggregateHtml( projectName: String, @@ -95,7 +96,7 @@ fun createAggregateHtml( td(diagnostic.appInfo.productCode + "-" + diagnostic.appInfo.build) //Indexing type section - td(if (diagnostic.indexingTimes.wasFullIndexing) "Full" else "Partial") + td(diagnostic.indexingTimes.scanningType.name.lowercase(Locale.ENGLISH).replace('_', ' ')) } } } @@ -378,7 +379,7 @@ fun JsonIndexDiagnostic.generateHtml(): String { if (times.indexingReason != null) { tr { td("Reason"); td(times.indexingReason) } } - tr { td("Full or partial"); td(if (times.wasFullIndexing) "full" else "partial") } + tr { td("Type"); td(times.scanningType.name.lowercase(Locale.ENGLISH).replace('_', ' ')) } tr { td("Finished at"); td(times.updatingEnd.presentableLocalDateTime()) } tr { td("Cancelled?"); td(times.wasInterrupted.toString()) } tr { td("Suspended time"); td(times.totalSuspendedTime.presentableDuration()) } diff --git a/platform/lang-impl/testSources/com/intellij/util/indexing/diagnostic/ProjectIndexingHistoryImplTest.kt b/platform/lang-impl/testSources/com/intellij/util/indexing/diagnostic/ProjectIndexingHistoryImplTest.kt index 12327dea2ad7..0e9e7a373ee0 100644 --- a/platform/lang-impl/testSources/com/intellij/util/indexing/diagnostic/ProjectIndexingHistoryImplTest.kt +++ b/platform/lang-impl/testSources/com/intellij/util/indexing/diagnostic/ProjectIndexingHistoryImplTest.kt @@ -1,4 +1,4 @@ -// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.util.indexing.diagnostic import com.intellij.openapi.command.impl.DummyProject @@ -12,7 +12,7 @@ class ProjectIndexingHistoryImplTest { @Test fun `test observation missed the start of suspension (IDEA-281514)`() { - val history = ProjectIndexingHistoryImpl(DummyProject.getInstance(), "test", true) + val history = ProjectIndexingHistoryImpl(DummyProject.getInstance(), "test", ScanningType.FULL) val time = Instant.now() history.stopSuspendingStages(time) history.startStage(ProjectIndexingHistoryImpl.Stage.Indexing, time.plusNanos(1)) @@ -27,7 +27,7 @@ class ProjectIndexingHistoryImplTest { @Test fun `test there may be actions after suspension`() { - val history = ProjectIndexingHistoryImpl(DummyProject.getInstance(), "test", true) + val history = ProjectIndexingHistoryImpl(DummyProject.getInstance(), "test", ScanningType.FULL) val time = Instant.now() history.startStage(ProjectIndexingHistoryImpl.Stage.Indexing, time) history.suspendStages(time.plusNanos(1)) @@ -42,7 +42,7 @@ class ProjectIndexingHistoryImplTest { @Test fun `test there may be actions after suspension 2`() { - val history = ProjectIndexingHistoryImpl(DummyProject.getInstance(), "test", true) + val history = ProjectIndexingHistoryImpl(DummyProject.getInstance(), "test", ScanningType.FULL) val time = Instant.now() history.startStage(ProjectIndexingHistoryImpl.Stage.Indexing, time) history.suspendStages(time.plusNanos(1)) @@ -58,7 +58,7 @@ class ProjectIndexingHistoryImplTest { @Test fun `test there may be actions after suspension 3`() { - val history = ProjectIndexingHistoryImpl(DummyProject.getInstance(), "test", true) + val history = ProjectIndexingHistoryImpl(DummyProject.getInstance(), "test", ScanningType.FULL) val time = Instant.now() history.suspendStages(time) history.startStage(ProjectIndexingHistoryImpl.Stage.Indexing, time.plusNanos(1)) @@ -74,7 +74,7 @@ class ProjectIndexingHistoryImplTest { @Test fun `test there may be actions after suspension 4`() { - val history = ProjectIndexingHistoryImpl(DummyProject.getInstance(), "test", true) + val history = ProjectIndexingHistoryImpl(DummyProject.getInstance(), "test", ScanningType.FULL) val time = Instant.now() history.startStage(ProjectIndexingHistoryImpl.Stage.Indexing, time) history.stopSuspendingStages(time.plusNanos(1)) @@ -89,7 +89,7 @@ class ProjectIndexingHistoryImplTest { @Test fun `test there may be actions after suspension 5`() { - val history = ProjectIndexingHistoryImpl(DummyProject.getInstance(), "test", true) + val history = ProjectIndexingHistoryImpl(DummyProject.getInstance(), "test", ScanningType.FULL) val time = Instant.now() history.startStage(ProjectIndexingHistoryImpl.Stage.Indexing, time.plusNanos(1)) history.stopSuspendingStages(time.plusNanos(2)) @@ -106,7 +106,7 @@ class ProjectIndexingHistoryImplTest { @Test fun `test basic workflow`() { - val history = ProjectIndexingHistoryImpl(DummyProject.getInstance(), "test", true) + val history = ProjectIndexingHistoryImpl(DummyProject.getInstance(), "test", ScanningType.FULL) val instant = Instant.now() history.startStage(ProjectIndexingHistoryImpl.Stage.PushProperties, instant) history.stopStage(ProjectIndexingHistoryImpl.Stage.PushProperties, instant.plusNanos(1)) @@ -122,7 +122,7 @@ class ProjectIndexingHistoryImplTest { @Test fun `test stage with suspension inside`() { - val history = ProjectIndexingHistoryImpl(DummyProject.getInstance(), "test", true) + val history = ProjectIndexingHistoryImpl(DummyProject.getInstance(), "test", ScanningType.FULL) val instant = Instant.now() history.startStage(ProjectIndexingHistoryImpl.Stage.PushProperties, instant) history.suspendStages(instant.plusNanos(1)) diff --git a/platform/lvcs-impl/src/com/intellij/history/core/Paths.java b/platform/lvcs-impl/src/com/intellij/history/core/Paths.java index 61586d8a3614..310c65326a71 100644 --- a/platform/lvcs-impl/src/com/intellij/history/core/Paths.java +++ b/platform/lvcs-impl/src/com/intellij/history/core/Paths.java @@ -58,7 +58,7 @@ public final class Paths { public static Iterable<String> split(String path) { String root = FileUtil.extractRootPath(path); if (root == null) return splitInner(path); - if (root.length() + 1 == path.length() && path.endsWith(":///")) { + if (root.length() + 1 == path.length() && path.endsWith("/")) { return Collections.singleton(root); } diff --git a/platform/platform-api/resources/messages/IdeBundle.properties b/platform/platform-api/resources/messages/IdeBundle.properties index ada86e389600..ad1db6aa1710 100644 --- a/platform/platform-api/resources/messages/IdeBundle.properties +++ b/platform/platform-api/resources/messages/IdeBundle.properties @@ -83,7 +83,7 @@ group.settings.process.tab.close=When closing a tool window with a running proce radio.process.close.terminate=Terminate process radio.process.close.disconnect=Disconnect radio.process.close.ask=Ask -treenode.loading= loading… +treenode.loading= loading\u2026 action.descriptor.action=Action: {0} action.descriptor.typing=Typing: "{0}" action.descriptor.keystroke=Keystroke: "{0}" @@ -150,8 +150,8 @@ prompt.input.favorites.list.new.name=Rename Favorites list ''{0}'' to: command.create.file.from.template=Create File From Template error.parsing.file.template=Error parsing file template: {0} title.velocity.error=Velocity Error -action.from.file.template=From File Template … -action.edit.file.templates=Edit File Templates… +action.from.file.template=From File Template \u2026 +action.edit.file.templates=Edit File Templates\u2026 error.unable.to.parse.template.message=Unable to parse template "{0}"\nError message: {1} error.invalid.template.file.name.or.extension=Invalid template file name or extension title.cannot.create.class=Cannot Create Class @@ -334,7 +334,7 @@ prompt.enter.new.file.name=Specify a new file name: title.new.file=New File progress.creating.file=Creating file {0}{1}{2} command.create.file=Create file -action.delete.ellipsis=_Delete… +action.delete.ellipsis=_Delete\u2026 action.delete=_Delete title.file.already.exists=File Already Exists title.error.writing.file=Error Writing File @@ -412,7 +412,7 @@ progress.downloading.list.of.plugins=Downloading list of plugins from {0} progress.downloading.plugins.meta=Downloading data for plugin {0} progress.downloading.available.plugins=Downloading list of available plugins progress.download.plugins=Downloading Plugins -button.http.proxy.settings=&HTTP Proxy Settings… +button.http.proxy.settings=&HTTP Proxy Settings\u2026 group.vendor=Vendor plugin.status.installed=Installed plugin.status.not.allowed=Blocked @@ -431,8 +431,8 @@ action.move.module.outside.any.group=Outside Any Group message.move.modules.to.group=Move {0} to the Group {1} message.module=module ''{0}'' message.modules=modules -action.move.module.new.top.level.group=New Top Level Group… -action.move.module.to.new.sub.group=To New Subgroup… +action.move.module.new.top.level.group=New Top Level Group\u2026 +action.move.module.to.new.sub.group=To New Subgroup\u2026 action.description.create.new.module.group=Create New Module Group prompt.specify.name.of.module.subgroup=Specify name of {0} subgroup the {1} will be shown under.\n\n title.module.sub.group=Module Subgroup @@ -558,15 +558,15 @@ title.choose.action.icon=Choose Action Icon action.choose.actions.to.add=Choose Actions To Add button.set.icon=&Set icon label.icon.path=&Icon: -button.edit.action.icon=Edit &Icon… -button.add.action=Add &Action… +button.edit.action.icon=Edit &Icon\u2026 +button.add.action=Add &Action\u2026 button.add.separator=Add &Separator title.customizations=Menus and Toolbars -group.customizations.add.action.group=Add Actions… -group.customizations.restore.action.group=Restore Actions… +group.customizations.add.action.group=Add Actions\u2026 +group.customizations.restore.action.group=Restore Actions\u2026 action.customizations.customize.action=Customize Toolbar... label.choosebyname.no.matches.found=No matches found -label.choosebyname.searching=Searching… +label.choosebyname.searching=Searching\u2026 prompt.gotoclass.enter.class.name=Enter {0} name: label.no.matches.found=No matches found prompt.gotofile.enter.file.name=Enter file name: @@ -613,9 +613,9 @@ scope.selected.files=Selected {0,choice,1#file|2#files} scope.selected.directories=Selected {0,choice,1#directory|2#directories} scope.selected.files.and.directories=Selected {0,choice,1#file|2#files} \\& {1,choice,1#directory|2#directories} scope.class.hierarchy=Class Hierarchy -progress.searching= searching… +progress.searching= searching\u2026 title.tip.of.the.day=Tip of the Day -action.open.tip=&Open… +action.open.tip=&Open\u2026 action.previous.tip=&Previous Tip action.next.tip=&Next Tip error.unable.to.read.tip.of.the.day=<html><body>Unable to read Tip Of The Day ({0}). Make sure {1} is installed properly.</body></html> @@ -644,7 +644,7 @@ checkbox.narrow.down.on.typing=&Narrow &&down on typing node.default.package=<Default Package> prompt.enter.a.new.package.name=Enter a new package name: command.create.new.package=Create new package -action.new.package=New Package… +action.new.package=New Package\u2026 action.description.create.new.package=Create new package tab.chooser.project=Project tab.chooser.search.by.name=Search by Name @@ -690,7 +690,7 @@ updates.paid.upgrade=You can evaluate the new version for {0} days, or buy it on updates.fallback.build=You have a perpetual fallback license for the new version. updates.interim.build=You can use the new version until your subscription expires on {0}. updates.new.build.notification.title={0} {1} available -updates.notification.update.action=Update… +updates.notification.update.action=Update\u2026 updates.ready.message={0} is ready to <a href=\"update\">update</a>. update.installed.notification.title=Plugin updates installed updates.external.progress=Fetching available updates for external components @@ -700,7 +700,7 @@ updates.plugin.ready.tooltip={0} {1,choice,1#plugin|2#plugins} {1,choice,1#updat updates.plugin.ready.title={0} plugin update available updates.plugins.ready.title=Plugin updates available updates.all.plugins.action={0,choice,1#Update|2#Update all} -updates.plugins.dialog.action=Details… +updates.plugins.dialog.action=Details\u2026 updates.no.updates.notification=No IDE or plugin updates available updates.no.updates.message=You already have the latest version of {0} and plugins installed. updates.no.updates.toolbox.message=<html>All plugins are up to date.<br> \ @@ -714,7 +714,7 @@ updates.no.updates.unknown.message=<html>All plugins are up to date.<br> \ updates.plugins.ready.header=<html><b>Plugins from configured hosts are ready to update.</b><br> \ Check plugins you want to update.</html> updates.configure.label=<b><a href=\"updates\">Configure</a></b> automatic updates. -updates.configure.updates.label=Configure updates… +updates.configure.updates.label=Configure updates\u2026 updates.incompatible.plugins.found={0,choice,1#Plugin|2#Plugins} incompatible with the new build found:{0,choice,1#' '|2#'<br/>'} {1} updates.download.and.restart.button=Up&date and Restart updates.apply.manually.button=Up&date Manually @@ -729,7 +729,7 @@ updates.from.to.size=Updating {0} to {1} ({2}). Patch size is {3} MB. updates.write.protected={0} does not have write access to {1}. Please run it by a privileged user to update. updates.settings.title=Updates updates.last.check.never=Never -updates.settings.check.now.button=&Check for Updates… +updates.settings.check.now.button=&Check for Updates\u2026 updates.settings.checkbox=Check for IDE updates updates.settings.checkbox.for=Check IDE updates for: updates.plugins.settings.checkbox=Check for plugin updates @@ -739,7 +739,7 @@ updates.settings.channel.locked=EAP builds can only be updated from the EAP chan updates.settings.last.check=Last checked: {0} updates.settings.current.version=Current version: updates.settings.build.number=Build number: -updates.settings.ignored=Manage ignored updates… +updates.settings.ignored=Manage ignored updates\u2026 updates.settings.ignored.title=Ignored Updates updates.settings.recommend.toolbox=We recommend the <a href=''{0}''>{1}</a> updates.settings.recommend.toolbox.multiline.description=Get updates automatically, open your projects with one click,\n\ @@ -794,14 +794,14 @@ plugin.manager.optional.dependencies.detected.title=Install Suggested Plugins plugin.manager.optional.dependencies.detected.message=It is suggested to install {1} with the ''{0}'' plugin. plugin.manager.obsolete.plugins.detected.title=Disable Conflicting Plugin plugin.manager.replace.plugin.0.by.plugin.1=<html>The ''{1}'' plugin is designed as a replacement for the installed ''{0}'' plugin.<br>\ - Disable the “{0}” plugin to avoid conflicts?</html> + Disable the \u201C{0}\u201D plugin to avoid conflicts?</html> error.message.unable.to.create.file=Unable to create file ''{0}'' button.facet.quickfix.text=&Fix -button.fix=Fix… +button.fix=Fix\u2026 file.chooser.show.path=Show path file.chooser.hide.path=Hide path file.chooser.hide.path.tooltip.text=Show/Hide path text field @@ -872,7 +872,7 @@ implicit.log.directory.path=It seems you''re using ''idea.system.path'' property bundled.jre.version.message=Please consider switching to the bundled Java runtime \ that is better suited for the IDE (your current Java runtime is {0} by {1} at ''{2}''). bundled.jre.m1.arch.message=Download {0} for Apple Silicon for better performance and stability. -bundled.jre.m1.arch.message.download=Download… +bundled.jre.m1.arch.message.download=Download\u2026 action.SwitchToJBR.text=Switch to bundled runtime cannot.delete.jre.config=Cannot delete JRE configuration file ''{0}'': {1} @@ -886,7 +886,7 @@ sys.health.acknowledge.action=Don't show again low.disk.space.message=Low disk space on a {0} system directory partition prompt.goto.inspection.enter.name=Enter inspection name: -goto.inspection.action.text=&Run Inspection by Name… +goto.inspection.action.text=&Run Inspection by Name\u2026 goto.inspection.action.choose.inherit.settings.from=Inspection Options goto.inspection.action.dialog.title=Run ''{0}'' goto.inspection.action.fix.all=Fix All @@ -901,12 +901,12 @@ whats.new.timeout.title=Couldn't load page whats.new.timeout.message=The content for this page cannot be loaded. Please check your internet connection. whats.new.timeout.action=You can <a href="{0}" class="link" target="_blank">open this page in browser</a> or try again later. # 0 - IDE name (e.g. "IntelliJ IDEA"), 1 - IDE version (e.g. "2020.3") -whats.new.notification.text={0} {1} est arrivée! +whats.new.notification.text={0} {1} est arriv\u00E9e! whats.new.notification.action=See what's new diff.dialog.title=Diff Between ''{0}'' and ''{1}'' -goto.custom.region.menu.item=Custom Folding… +goto.custom.region.menu.item=Custom Folding\u2026 goto.custom.region.command=Go to Custom Folding goto.custom.region.message.dumb.mode=Custom folding navigation is not available until indexes are built. goto.custom.region.message.unavailable=There are no custom foldings in the current file. @@ -1013,7 +1013,7 @@ run.anything.action.tooltip.text=Execute commands: open projects, launch run con double.ctrl.or.shift.shortcut=Double {0} run.anything.hint.initial.text=Press {0} or {1} to navigate through the suggestion list run.anything.indexing.mode.not.supported=Run anything is not available while indexes are updating -run.anything.context.browse.directory=Browse Directory… +run.anything.context.browse.directory=Browse Directory\u2026 run.anything.context.project=Project run.anything.context.project.undefined=undefined run.anything.context.title.working.directory=Execution Context @@ -1117,7 +1117,7 @@ dnd.with.alt.pressed.only=Drag-and-drop with Alt pressed only checkbox.full.paths.in.window.header=Always show full path in window header overridden.by.jvm.property=Overridden by JVM property ''{0}'' option.is.overridden.by.jvm.property=The option is overridden by the JVM property: "{0}" -background.image.button=Background Image… +background.image.button=Background Image\u2026 display.balloon.notifications=Display balloon notifications small.labels.in.editor.tabs=Small labels in editor tabs show.hidden.tabs=Show Hidden Tabs @@ -1174,11 +1174,11 @@ file.encoding.option.warning.windows.only=<html> {0} will add <a>UTF-8 BOM</a> t plugin.manager.tab.marketplace=Marketplace plugin.manager.tab.installed=Installed plugin.manager.options.command=Type / to see options -plugin.manager.repositories=Manage Plugin Repositories… +plugin.manager.repositories=Manage Plugin Repositories\u2026 plugin.manager.custom.certificates=Manage Plugin Certificates plugin.manager.update.all=Update all plugin.manager.tooltip=Manage Repositories, Configure Proxy or Install Plugin from Disk -plugin.settings.link.title=Plugin Settings… +plugin.settings.link.title=Plugin Settings\u2026 settings.required.plugins.title=Specify a list of plugins required for your project. {0} will notify you if a required plugin is missing or needs an update. settings.certificate.accept.non.trusted.certificates.automatically=Accept non-trusted certificates &automatically settings.certificate.no.certificate.selected=No certificate selected @@ -1221,7 +1221,7 @@ settings.file.colors.dialog.warning.replace=<html>Do you want to update \ settings.file.colors.enable.file.colors=Enable &file colors settings.file.colors.use.in.editor.tabs=Use in editor &tabs settings.file.colors.use.in.project.view=Use in &project view -settings.file.colors.manage.scopes=&Manage scopes… +settings.file.colors.manage.scopes=&Manage scopes\u2026 settings.file.colors.description=<html>\ Files can belong to several scopes. If there are two colors for one scope, the color of the first scope in the list is used. @@ -1251,11 +1251,11 @@ action.Anonymous.text.reset.shortcuts=Reset Shortcuts action.Anonymous.text.delete.hook=Delete Hook action.Anonymous.text.live.templates=Live Templates action.Anonymous.text.live.template=Live Template -action.Anonymous.text.template.group=Template Group… +action.Anonymous.text.template.group=Template Group\u2026 action.Anonymous.text.rename=Rename -action.Anonymous.text.new.group=New group… -action.Anonymous.text.change.context=Change context… -action.Anonymous.text.edit.scopes.order=Edit Scopes Order… +action.Anonymous.text.new.group=New group\u2026 +action.Anonymous.text.change.context=Change context\u2026 +action.Anonymous.text.edit.scopes.order=Edit Scopes Order\u2026 action.Anonymous.text.done=Done action.Anonymous.text.remove.leading.directory=Remove Leading Directory action.Anonymous.text.restore.leading.directory=Restore Leading Directory @@ -1335,9 +1335,9 @@ action.AnAction.description.run.on.frame.activation=Run On Frame Activation action.AnAction.text.search.history=Search History action.AnAction.description.search.history=Search History action.add.to.new.favorites.list.description=Add To New Favorites List -action.DataSharingOptionsAction.text=Data Sharing Options… +action.DataSharingOptionsAction.text=Data Sharing Options\u2026 action.DataSharingOptionsAction.description=Data Sharing Options -action.InstallFromDiskAction.text=Install Plugin from Disk… +action.InstallFromDiskAction.text=Install Plugin from Disk\u2026 action.InstallFromDiskAction.not.allowed.description=Installing plugins from disk is not allowed for your organization failed.to.unload.modified.plugins=Failed to unload modified plugins: {0} plugins.reloaded.successfully={0} reloaded successfully @@ -1438,18 +1438,18 @@ label.category.unknown=UNKNOWN chooser.description.jar.and.zip.archives.are.accepted=JAR and ZIP archives are accepted chooser.title.plugin.file=Choose Plugin File dialog.title.install.plugin=Install Plugin -progress.title.installing.plugin=Installing plugin "{0}"… +progress.title.installing.plugin=Installing plugin "{0}"\u2026 message.link.refresh=refresh message.check.the.internet.connection.and=Check the internet connection and -progress.text.downloading=Downloading… -progress.text.loading=Loading… +progress.text.downloading=Downloading\u2026 +progress.text.loading=Loading\u2026 label.install.a.limited.functionality.for.free=Install a version with limited functionality for free or label.use.the.trial.for.up.to.30.days.or=Use the 30-day trial or label.category.n.a=n/a dialog.title.dependent.plugins.found=Dependent Plugins Found button.enable.updated.plugins=Enable Updated {0,Choice,1#Plugin|2#Plugins} button.enable.all=Enable All -progress.title.checking.plugins.repository=Checking Plugins Repository… +progress.title.checking.plugins.repository=Checking Plugins Repository\u2026 title.search.results=Search Results empty.text.nothing.found=Nothing found label.plugin.change.notes=Change Notes @@ -1496,7 +1496,7 @@ plugins.configurable.buy.the.plugin=buy the plugin action.presentation.AbstractSchemesPanel.text=Show Scheme Actions action.presentation.EditorTabbedContainer.text=Close. Alt-Click to Close Others. action.presentation.EncodingPanel.text=File Encoding -action.presentation.ExtractIncludeAction.text=Include File… +action.presentation.ExtractIncludeAction.text=Include File\u2026 action.presentation.LightEditTabs.text=Close. Alt-Click to Close Others. action.presentation.OpenSelectedProjectsAction.text.open.all.projects.in.group=Open All Projects in Group action.presentation.OpenSelectedProjectsAction.text.open.selected=Open Selected @@ -1558,7 +1558,7 @@ action.text.hide.file.name=Hide File Name rename.0=Rename {0} dialog.title.select.0=Select {0} quickfix.text.insert.0=Insert {0} -progress.text.reopening.files=Reopening files… +progress.text.reopening.files=Reopening files\u2026 button.reject=Reject tooltip.text.update.is.in.progress.click.to.cancel=Update is in progress. Click to cancel @@ -1627,7 +1627,7 @@ terminal.cursor.shape.vertical.name=Vertical notification.title.desktop.entry.creation.failed=Desktop entry creation failed notification.title.desktop.entry.created=Desktop entry created -label.text.desktop.entry.can.be.created.later.in.tools.create.desktop.entry=Desktop entry can be created later in Tools | Create Desktop Entry… +label.text.desktop.entry.can.be.created.later.in.tools.create.desktop.entry=Desktop entry can be created later in Tools | Create Desktop Entry\u2026 step.title.desktop.entry=Desktop Entry button.installed=Installed label.new.plugins.can.also.be.downloaded.in.0.plugins=New plugins can also be downloaded in {0} | Plugins @@ -1689,7 +1689,7 @@ dialog.title.invalid.input=Invalid Input popup.title.import.scheme=Import Scheme dialog.title.import=Import message.there.are.no.available.schemes.to.import=There are no available schemes to import -label.macro.recording.started=Macro recording started… +label.macro.recording.started=Macro recording started\u2026 tooltip.macro.is.being.recorded.now=Macro is being recorded now dialog.title.required.plugin=Required Plugin @@ -1698,7 +1698,7 @@ label.minimum.version=Minimum version: label.plugin=Plugin: label.version.any=<any> -link.more=More… +link.more=More\u2026 message.the.path.0.does.not.exist.maybe.on.remote=The path {0} does not exist.\n\ If it is on a removable or network drive, please make sure that the drive is connected. @@ -1728,7 +1728,7 @@ message.this.functionality.is.not.available.during.indexing=This functionality i progress.title.constructing.tooltip=Constructing tooltip wrong.number.of.arguments.usage.ide.executable.save=Wrong number of arguments. Usage: <ide executable> save wrong.number.of.arguments.usage.ide.executable.exit=Wrong number of arguments. Usage: <ide executable> exit [--restart] -menu.item.loading=loading… +menu.item.loading=loading\u2026 command.finish=finish dialog.title.add.actions.to.quick.list=Add Actions to Quick List this.file.does.not.belong.to.the.project={0, choice, 1#This file does|2#These files do} not belong to the project: @@ -1757,7 +1757,7 @@ large.file.editor.name=Large File Editor large.file.preview.notification=The file is too large ({0}). Showing a read-only preview of the first {1}. dialog.message.could.not.erase.files.or.folders.0.1=Could not erase files or folders:\n{0}{1} command.deleting.files=Deleting Files -progress.title.deleting.files=Deleting Files… +progress.title.deleting.files=Deleting Files\u2026 dialog.title.choose.plugins.to.install.or.enable=Choose Plugins to Install or Enable action.text.merge.tabs.to.group=Merge Tabs to ''{0}'' Group action.text.split.group=Split ''{0}'' Group @@ -1773,15 +1773,15 @@ notification.group.project.settings=Could not save project action.text.copy=&Copy dialog.title.collect.troubleshooting.information=Collect Troubleshooting Information link.reset.icon=Reset -link.change.icon=Change… +link.change.icon=Change\u2026 dialog.title.can.t.create.0.sdk=Cannot Create {0} SDK label.continue.editing=Continue editing button.continue.editing=Continue Editing button.save.anyway=Save Anyway change.encoding.command.name=Change Encoding for ''{0}'' -progress.text.reloading.file=Reloading file… +progress.text.reloading.file=Reloading file\u2026 progress.title.reload.files=Reload files -progress.text.reloading.files=Reloading files… +progress.text.reloading.files=Reloading files\u2026 action.text.more=More dialog.title.change.group.name=Change Group Name label.enter.group.name=Enter group name: @@ -1847,7 +1847,7 @@ notification.group.password.safe=Password storage not available action.text.install.keymap=Install {0} Keymap action.text.search.for.keymap=Search for {0} Keymap plugin action.separator.file.templates=File templates -startup.indicator.text.running.startup.activities=Running startup activities… +startup.indicator.text.running.startup.activities=Running startup activities\u2026 notification.configurable.display.name.notifications=Notifications dumb.balloon.this.action=This action dumb.balloon.none.of.the.following.actions=None of the following actions @@ -1859,7 +1859,7 @@ checkbox.use.custom.user.data.directory.for.chrome=&Use custom user data directo label.ssl.certificate.details=Certificate details dialog.title.http.proxy.exceptions=Proxy exceptions border.title.problem.files=Problem files -link.change.project.icon=Choose SVG file… +link.change.project.icon=Choose SVG file\u2026 link.change.project.icon.description=The SVG file will be saved to the .idea folder.<br/>To share the project icon, add it to the repository. label.project.icon.for.darcula.theme=Dark theme: label.project.icon.for.default.theme=Light theme: @@ -1972,7 +1972,7 @@ welcome.screen.learnIde.help.and.resources.text=Help and Resources welcome.screen.color.theme.header=Color theme welcome.screen.ide.font.size.label=IDE font: welcome.screen.editor.font.size.label=Editor font: -welcome.screen.all.settings.link=All settings… +welcome.screen.all.settings.link=All settings\u2026 # End of the sentence that is started by link.check hyperlink welcome.screen.check.for.updates.comment=\ for updates now. welcome.screen.logo.version.label=Version {0} @@ -1992,8 +1992,8 @@ dialog.title.proxy.authentication=Proxy Authentication: {0} dialog.message.please.enter.credentials.for=Please enter credentials for: {0} dialog.title.untrusted.server.s.certificate=Untrusted Server's Certificate text.server.s.certificate.trusted=Server's certificate is not trusted -action.DescriptionAwareSchemeActions.add.description.text=Add Description… -action.DescriptionAwareSchemeActions.edit.description.text=Edit Description… +action.DescriptionAwareSchemeActions.add.description.text=Add Description\u2026 +action.DescriptionAwareSchemeActions.edit.description.text=Edit Description\u2026 button.fix.it=Fix it popup.title.recent.projects=Recent Projects dialog.title.reopen.project=Reopen Project @@ -2314,7 +2314,7 @@ notification.action.unknown.macros.error.fix=Fix it dialog.title.configuration.changed=Configuration Changed dialog.message.component.could.not.be.reloaded=The component cannot be reloaded. Reload project? dialog.title.migrating.plugins=Migrating Plugins -progress.text.migrating.plugins=Migrating plugins… +progress.text.migrating.plugins=Migrating plugins\u2026 dialog.message.unknown.error=Unknown error http.velocity={0}<hr> <font face="verdana" size="-1">\ <a href=''http://velocity.apache.org/engine/devel/user-guide.html#Velocity_Template_Language_VTL:_An_Introduction''>\nApache Velocity</a> \ @@ -2347,9 +2347,9 @@ required.plugin.between.versions=, version between ''{0}'' and ''{1}'' required.plugin.at.least.versions=, version at least ''{0}'' required.plugin.at.most.versions=, version at most ''{0}'' window.title.switcher=Switcher -progress.title.cds.optimize.startup=Optimizing startup performance… -progress.text.collecting.classes=Collecting classes list… -progress.text.generate.classes.archive=Generating classes archive… +progress.title.cds.optimize.startup=Optimizing startup performance\u2026 +progress.text.collecting.classes=Collecting classes list\u2026 +progress.text.generate.classes.archive=Generating classes archive\u2026 only.at.line.start=\\&Only at line start dialog.message.internal.error=Internal error label.recommended.only.if.you.are.br.familiar.with.vim=Recommended only if you are<br> familiar with Vim. @@ -2357,7 +2357,7 @@ label.from.your.jetbrains.account=From your JetBrains account label.text.plugin.dependencies=With {0, choice, 1#dependency|2#dependencies}: {1} label.no.description.available=No description available label.plugin.descriptor.category.unknown=Unknown -link.label.wizard.step.plugin.customize=Customize… +link.label.wizard.step.plugin.customize=Customize\u2026 link.label.choice.disable.enable.choice.all={0, choice, 0#Disable|1#Enable}{1, choice, 0# All|1#} colorpicker.label.red=R: @@ -2380,7 +2380,7 @@ colorpanel.label.alpha.percent=A% colorpanel.label.red=R colorpanel.label.green=G colorpanel.label.blue=B -colorpanel.label.hue=H° +colorpanel.label.hue=H\u00B0 colorpanel.label.saturation=S% colorpanel.label.brightness=B% @@ -2392,7 +2392,7 @@ no.charset.set.reason.charset.auto.detected.from.content=charset is auto-detecte no.charset.set.reason.disabled.for.file.type=disabled for {0} checkbox.widescreen.tool.window.layout.description=Maximize the height of vertical tool windows by limiting the width of horizontal tool windows error.tree.view.cell.error=Error: {0} -error.tree.view.fixing=fixing… +error.tree.view.fixing=fixing\u2026 error.tree.view.fix.description=Fix: {0} gdpr.exit.button=Exit gdpr.continue.button=Continue @@ -2456,25 +2456,25 @@ wsl.target.introspection.step.completed.successfully=Introspection completed suc wsl.target.introspection.step.completed.with.errors=Introspection completed with errors. wsl.target.language.step.description={0} runtime configuration for {1} wsl.target.tool.step.description=WSL configuration -wsl.opening_wsl=Opening WSL… +wsl.opening_wsl=Opening WSL\u2026 wsl.no_path=Cannot find the Windows-specific part of this distribution, cannot browse it wsl.executing.process=Executing WSL Process name.variable=File name entered in the dialog -action.plugins.text=Plugins… +action.plugins.text=Plugins\u2026 settings.entry.point.tooltip=IDE and Project Settings settings.entry.point.with.updates.tooltip=Updates available. IDE and Project Settings settings.entry.point.widget.name=IDE and Project Settings settings.entry.point.update.ide.action=Download {0} {1} -settings.entry.point.update.plugin.action=Update {0} Plugin… -settings.entry.point.update.plugins.action=Update {0} Plugins… +settings.entry.point.update.plugin.action=Update {0} Plugin\u2026 +settings.entry.point.update.plugins.action=Update {0} Plugins\u2026 template.file.name=Template to generate file name and path template.file.name.optional=Template to generate file name and path (optional) updates.settings.show.editor=Show What's New in the editor after an IDE update plugin.version.bundled=bundled -laf.action.install.theme=Install Theme… -keymap.action.configure.keymap=Configure Keymap… -keymap.action.install.keymap=Install Keymap… +laf.action.install.theme=Install Theme\u2026 +keymap.action.configure.keymap=Configure Keymap\u2026 +keymap.action.install.keymap=Install Keymap\u2026 settings.entry.point.got.it.popup=Quickly access the main IDE and project settings, and execute commands. dialog.title.custom.debug.log.configuration=Custom Debug Log Configuration web.preview.file.editor.name=Preview for {0} @@ -2488,7 +2488,7 @@ command.name.choice.exclude.include={0, choice, 0#Exclude|1#Include} {1} detach.directory.dialog.message.detach.0=Detach {0}?\nNo files will be removed on disk. detach.directory.dialog.title.detach=Detach Directories from Project detach.directory.dialog.button.detach=Detach -detach.directory.action.text.detach.0=Detach {0,choice,1#Directory|2#Directories} from Project… +detach.directory.action.text.detach.0=Detach {0,choice,1#Directory|2#Directories} from Project\u2026 attachable.project.pane.name=Files label.empty.text.attach.directories.with.right.click=Attach directories with right click label.text.html.center.drop.here.to.attach.br.as.a.root.directory.center.html=<html><center>Drop here to attach<br/>as a root directory</center></html> @@ -2526,11 +2526,11 @@ untrusted.project.open.dialog.title=Trust and Open Project ''{0}''? # {0} stands for the IDE name, e.g. "IntelliJ IDEA" untrusted.project.open.dialog.text=\ {0} provides features that may execute potentially malicious code from this folder.\n\n\ - If you don’t trust the source, preview the project in the safe mode to only browse its code. + If you don\u2019t trust the source, preview the project in the safe mode to only browse its code. # Editor notification banner when the project is opened in the safe mode untrusted.project.notification.description=Safe mode, limited functionality. Trust the project to access full IDE functionality. -untrusted.project.notification.trust.link=Trust project… +untrusted.project.notification.trust.link=Trust project\u2026 untrusted.project.notification.read.more.link=Read more # Trust Project modal confirmation when opening an existing project, or when clicking to "Trust Project" link in the editor banner @@ -2538,7 +2538,7 @@ untrusted.project.dialog.distrust.button=Stay in Safe Mode untrusted.project.dialog.title=Trust {0} {1,choice,1#Project|1<Projects}? untrusted.project.general.dialog.title=Trust Project? untrusted.project.dialog.text=\ - If you don’t trust the source, stay in the safe mode.\n\n\ + If you don\u2019t trust the source, stay in the safe mode.\n\n\ Loading, running, or building a {0} {1,choice,1#project|1<projects} may execute potentially malicious code from its build scripts. # {0} stands for something like github.com/JetBrains @@ -2557,9 +2557,9 @@ trusted.hosts.settings.new.trusted.folder.file.chooser.title=Select Trusted Loca # 'Actions on Save' page in Settings (Preferences) actions.on.save.page.title=Actions on Save -actions.on.save.link.configure.autosave.options=Configure autosave options… -actions.on.save.link.configure=Configure… -actions.on.save.link.all.actions.on.save=All actions on save… +actions.on.save.link.configure.autosave.options=Configure autosave options\u2026 +actions.on.save.link.configure=Configure\u2026 +actions.on.save.link.all.actions.on.save=All actions on save\u2026 actions.on.save.table.column.name.action=Action actions.on.save.table.column.name.activated.on=Activated On # This is a label in the 'Activated On' column. Should be short. @@ -2582,7 +2582,7 @@ toolbox.updates.download.update.action.text=Download {2} {1} ({0}) toolbox.updates.download.update.action.description=Toolbox App will download {2} {1} ({0}) and let you \ know when the update is ready to switch -toolbox.updates.download.ready.action.text=Switch to {2} {1} ({0})… +toolbox.updates.download.ready.action.text=Switch to {2} {1} ({0})\u2026 toolbox.updates.download.ready.action.description=Toolbox App has installed {2} {1} ({0}). Restart to the new version? popup.title.open.project=Open Project @@ -2591,7 +2591,7 @@ notification.title.input.method.disabler=Input methods disabler notification.content.input.method.disabler=The IDE is running with enabled input methods that can cause freezes. \ Please consider to disable input methods if you don't really use them. action.text.disable.input.methods=Disable input methods -log.in.link.text=Log in… +log.in.link.text=Log in\u2026 login.dialog.jb.login=Log in to JetBrains Account login.dialog.separator.text=or log in with @@ -2601,14 +2601,14 @@ login.dialog.simple.login=Log in to JetBrains Account login.dialog.you.are.logged=You are logged in login.dialog.subscribe.to.survey=Subscribe to {0} survey login.dialog.start.using=Get Started -login.dialog.waiting.for.login=Waiting for login in browser… -login.dialog.waiting.for.login.short=Waiting for login… +login.dialog.waiting.for.login=Waiting for login in browser\u2026 +login.dialog.waiting.for.login.short=Waiting for login\u2026 login.dialog.new.user.label=New user? login.dialog.register.link=Create an account login.dialog.troubles.link=Troubles? login.dialog.eap.info.link=About EAP login.dialog.title={0} -login.dialog.log.out=Log Out… +login.dialog.log.out=Log Out\u2026 login.dialog.empty.username=Logged in login.dialog.trouble.default.message=Unable to complete authorization process. login.dialog.trouble.no.link={0}<br> \ @@ -2617,16 +2617,16 @@ login.dialog.trouble.no.link={0}<br> \ login.dialog.trouble.with.redirect=Sign in to your account and copy IDE authorization token.<br/>If the browser does not open, <a href="{0}">copy the link</a> and open it manually. login.dialog.trouble.unknown=<a href="{0}">Copy the link</a> and open it in browser to get IDE authorization token. login.dialog.authorization.token.field=IDE authorization token -login.dialog.back.link=← Back +login.dialog.back.link=\u2190 Back login.dialog.check.token=Check Token login.dialog.wrong.token=Wrong authorization token login.dialog.link.copied=Link copied login.dialog.session.expired=Authorization session has expired. Please start it again # {0} is a product name (e.g. "IntelliJ IDEA") -old.dirs.action.progress=Looking for leftover IDE storage directories… +old.dirs.action.progress=Looking for leftover IDE storage directories\u2026 old.dirs.notification.text=Leftover IDE storage directories found. -old.dirs.notification.action=View and delete… +old.dirs.notification.action=View and delete\u2026 old.dirs.not.found.notification.text=No leftover IDE storage directories were found. old.dirs.dialog.title=Delete Leftover IDE Storage Directories old.dirs.dialog.text=<html>\ @@ -2638,7 +2638,7 @@ old.dirs.column.name=Directory old.dirs.column.updated=Last Used old.dirs.column.size=Size old.dirs.dialog.delete.button=Delete {0} {0,choice,0#Directories|1#Directory|2#Directories} -old.dirs.delete.progress=Deleting directories… +old.dirs.delete.progress=Deleting directories\u2026 old.dirs.delete.error=Cannot delete directories plugin.group.editor=Editor @@ -2679,8 +2679,8 @@ notifications.toolwindow.suggestions=Suggestions notifications.toolwindow.timeline=Timeline notifications.toolwindow.timeline.clear.all=Clear all notifications.toolwindow.remind.tomorrow=Remind me tomorrow -notifications.toolwindow.dont.show.again.for.this.project=Don’t show again for this project -notifications.toolwindow.dont.show.again=Don’t show again +notifications.toolwindow.dont.show.again.for.this.project=Don\u2019t show again for this project +notifications.toolwindow.dont.show.again=Don\u2019t show again notifications.toolwindow.suggestion.gotit.title=Use suggestions below to optimize user\nexperience and improve performance notifications.toolwindow.suggestion.gotit.link=Got it notifications.collapse.balloon.title={0} more {1} @@ -2728,3 +2728,8 @@ external.link.confirmation.title=Open Link external.link.confirmation.message.0=Are you sure you want to open the link in a browser or in an associated application?<br><br>{0} external.link.confirmation.yes.label=Open external.link.confirmation.trust.label=Trust Project and Open + +jcef.local.cache.invalidate.action.description=Delete embedded browser engine cache and cookies. This action may affect components that use an embedded browser to render HTML-based content and web pages. +jcef.local.cache.invalidate.checkbox.description=Delete embedded browser engine cache and cookies +jcef.local.cache.invalidate.failed.title=Failed to clean up browser engine cache +jcef.local.cache.invalidate.failed.message=The Invalidate Caches operation requested embedded browser engine cache cleanup. The operation failed due to I/O error: {0} diff --git a/platform/platform-api/src/com/intellij/openapi/GitSilentFileAdder.java b/platform/platform-api/src/com/intellij/openapi/GitSilentFileAdder.java index 3a01ec4979fe..3904e1e56282 100644 --- a/platform/platform-api/src/com/intellij/openapi/GitSilentFileAdder.java +++ b/platform/platform-api/src/com/intellij/openapi/GitSilentFileAdder.java @@ -9,14 +9,6 @@ import org.jetbrains.annotations.SystemIndependent; import java.io.File; import java.nio.file.Path; -/** - * To be used in async Project initialization tasks to add files silently, - * preventing triggering of 'add new files to vcs' notifications/dialogs - * by {@link com.intellij.openapi.vcs.VcsVFSListener}. - * - * @see GitRepositoryInitializer - * @see com.intellij.openapi.vcs.VcsFileListenerContextHelper - */ @ApiStatus.Internal public interface GitSilentFileAdder { /** @@ -34,9 +26,9 @@ public interface GitSilentFileAdder { void markFileForAdding(@NotNull Path path, boolean isDirectory); /** - * Notify that marked files can be added on pooled thread. + * Notify that marked files can be scheduled for addition to VCS. * <p> - * Should be called after explicit VFS refresh if files are added externally (ex: using NIO). + * Method should be called after an explicit synchronous VFS refresh if files are added externally (ex: using NIO, by an external command, etc). */ void finish(); diff --git a/platform/platform-api/src/com/intellij/openapi/GitSilentFileAdderProvider.java b/platform/platform-api/src/com/intellij/openapi/GitSilentFileAdderProvider.java index 85fe0d3a4093..273cf59b7179 100644 --- a/platform/platform-api/src/com/intellij/openapi/GitSilentFileAdderProvider.java +++ b/platform/platform-api/src/com/intellij/openapi/GitSilentFileAdderProvider.java @@ -3,8 +3,18 @@ package com.intellij.openapi; import com.intellij.openapi.extensions.ProjectExtensionPointName; import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +/** + * To be used in async Project initialization tasks to add files silently, + * preventing triggering of 'add new files to vcs' notifications/dialogs + * by {@link com.intellij.openapi.vcs.VcsVFSListener}. + * + * @see GitRepositoryInitializer + * @see com.intellij.openapi.vcs.VcsFileListenerContextHelper + */ +@ApiStatus.Internal public interface GitSilentFileAdderProvider { ProjectExtensionPointName<GitSilentFileAdderProvider> EP_NAME = new ProjectExtensionPointName<>("com.intellij.gitSilentFileAdder"); diff --git a/platform/platform-api/src/com/intellij/ui/jcef/JBCefApp.java b/platform/platform-api/src/com/intellij/ui/jcef/JBCefApp.java index 71c1a27a2a27..3504b016bde5 100644 --- a/platform/platform-api/src/com/intellij/ui/jcef/JBCefApp.java +++ b/platform/platform-api/src/com/intellij/ui/jcef/JBCefApp.java @@ -13,6 +13,7 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.PathManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.NotNullLazyValue; @@ -30,6 +31,7 @@ import org.cef.CefSettings.LogSeverity; import org.cef.callback.CefSchemeHandlerFactory; import org.cef.callback.CefSchemeRegistrar; import org.cef.handler.CefAppHandlerAdapter; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -68,6 +70,8 @@ public final class JBCefApp { @NotNull private final CefApp myCefApp; + @NotNull private final CefSettings myCefSettings; + @NotNull private final Disposable myDisposable = new Disposable() { @Override public void dispose() { @@ -147,6 +151,8 @@ public final class JBCefApp { settings.remote_debugging_port = port; } + settings.cache_path = ApplicationManager.getApplication().getService(JBCefAppCache.class).getPath().toString(); + String[] argsFromProviders = JBCefAppRequiredArgumentsProvider .getProviders() .stream() @@ -194,6 +200,7 @@ public final class JBCefApp { } CefApp.addAppHandler(new MyCefAppHandler(args, trackGPUCrashes)); + myCefSettings = settings; myCefApp = CefApp.getInstance(settings); Disposer.register(ApplicationManager.getApplication(), myDisposable); } @@ -356,6 +363,11 @@ public final class JBCefApp { return getInstance() != null; } + @Contract(pure = true) + @NotNull String getCachePath() { + return myCefSettings.cache_path; + } + @NotNull public JBCefClient createClient() { return createClient(false); @@ -426,7 +438,7 @@ public final class JBCefApp { private int myGPUCrashCounter = 0; private boolean myNotificationShown = false; - MyCefAppHandler(String @Nullable[] args, boolean trackGPUCrashes) { + MyCefAppHandler(String @Nullable [] args, boolean trackGPUCrashes) { super(args); myGPUCrashLimit = trackGPUCrashes ? Integer.getInteger("ide.browser.jcef.gpu.infinitecrash.internallimit", 10) : -1; } diff --git a/platform/platform-api/src/com/intellij/ui/jcef/JBCefAppCache.kt b/platform/platform-api/src/com/intellij/ui/jcef/JBCefAppCache.kt new file mode 100644 index 000000000000..c9bddbb01f76 --- /dev/null +++ b/platform/platform-api/src/com/intellij/ui/jcef/JBCefAppCache.kt @@ -0,0 +1,61 @@ +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.ui.jcef + +import com.intellij.execution.Platform +import com.intellij.ide.IdeBundle +import com.intellij.notification.Notification +import com.intellij.notification.NotificationType +import com.intellij.notification.Notifications +import com.intellij.openapi.application.PathManager +import com.intellij.openapi.components.Service +import com.intellij.openapi.diagnostic.thisLogger +import com.intellij.openapi.util.io.FileUtil +import org.jetbrains.annotations.NotNull +import java.io.IOException +import java.nio.file.Path +import java.nio.file.Paths + +private const val invalidationMarkerFileName = "invalidation.marker" + +@Service +class JBCefAppCache { + @get:NotNull + val path by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { + prepareCachePath() + } + + fun markInvalidated() { + FileUtil.createIfDoesntExist(path.resolve(invalidationMarkerFileName).toFile()) + } + + private fun prepareCachePath(): Path { + val fileSeparator = Platform.current().fileSeparator + val defaultCachePath = "${PathManager.getSystemPath()}${fileSeparator}jcef_cache${fileSeparator}" + val suggestedPath: Path = Paths.get(System.getProperty("ide.browser.jcef.cache.path", defaultCachePath)) + + val invalidationMarkerFilePath = suggestedPath.resolve(invalidationMarkerFileName) + val logger = thisLogger() + + if (FileUtil.exists(invalidationMarkerFilePath.toString())) { + try { + FileUtil.delete(suggestedPath) + logger.info("Successfully deleted JCEF browser engine cache at \"$suggestedPath\"") + } + catch (exception: IOException) { + Notifications.Bus.notify( + Notification( + "IDE Caches", + IdeBundle.message("jcef.local.cache.invalidate.failed.title"), + IdeBundle.message("jcef.local.cache.invalidate.failed.message", exception.message), + NotificationType.ERROR + ) + ) + + logger.error("Failed to cleanup JCEF browser engine cache due to I/O error", exception) + } + } + + logger.debug("JCEF cache path: \"$suggestedPath\"") + return suggestedPath + } +} diff --git a/platform/platform-api/src/com/intellij/ui/jcef/JBCefAppCacheInvalidator.kt b/platform/platform-api/src/com/intellij/ui/jcef/JBCefAppCacheInvalidator.kt new file mode 100644 index 000000000000..2e062e2c9d06 --- /dev/null +++ b/platform/platform-api/src/com/intellij/ui/jcef/JBCefAppCacheInvalidator.kt @@ -0,0 +1,19 @@ +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.ui.jcef + +import com.intellij.ide.IdeBundle +import com.intellij.ide.caches.CachesInvalidator +import com.intellij.openapi.application.ApplicationManager + +class JBCefAppCacheInvalidator : CachesInvalidator() { + + override fun getComment(): String = IdeBundle.message("jcef.local.cache.invalidate.action.description") + + override fun getDescription(): String = IdeBundle.message("jcef.local.cache.invalidate.checkbox.description") + + override fun optionalCheckboxDefaultValue(): Boolean = false + + override fun invalidateCaches() { + ApplicationManager.getApplication().getService(JBCefAppCache::class.java).markInvalidated() + } +} diff --git a/platform/platform-impl/src/com/intellij/application/options/editor/EditorTabsConfigurableEP.java b/platform/platform-impl/src/com/intellij/application/options/editor/EditorTabsConfigurableEP.java index 629fa82e7788..9748b456bd59 100644 --- a/platform/platform-impl/src/com/intellij/application/options/editor/EditorTabsConfigurableEP.java +++ b/platform/platform-impl/src/com/intellij/application/options/editor/EditorTabsConfigurableEP.java @@ -7,17 +7,19 @@ import com.intellij.openapi.options.SearchableConfigurable; import com.intellij.openapi.options.UnnamedConfigurable; /** - * To provide additional options in Editor | Editor Tabs section register implementation of - * {@link UnnamedConfigurable} in the plugin.xml: - * <p/> - * <extensions defaultExtensionNs="com.intellij"><br> - * <editorTabsConfigurable instance="class-name"/><br> + * Provides additional options in <em>Editor | General | Editor Tabs</em> settings. + * <p> + * Register implementation of {@link UnnamedConfigurable} in {@code plugin.xml}: + * <pre> + * <extensions defaultExtensionNs="com.intellij"> + * <editorTabsConfigurable instance="class-name"/> * </extensions> + * </pre> + * <p> + * A new instance of the specified class will be created each time when the Settings dialog is opened. * <p> - * A new instance of the specified class will be created each time then the Settings dialog is opened - * - * If you need to add a section of editor tabs options, your UnnamedConfigurable should implement - * {@link EditorTabsOptionsCustomSection} + * If you need to add a section of editor tabs options, your {@code UnnamedConfigurable} should implement + * {@link EditorTabsOptionsCustomSection}. */ public final class EditorTabsConfigurableEP extends ConfigurableEP<SearchableConfigurable> { static final ExtensionPointName<EditorTabsConfigurableEP> EP_NAME = new ExtensionPointName<>("com.intellij.editorTabsConfigurable"); diff --git a/platform/platform-impl/src/com/intellij/application/options/editor/EditorTabsOptionsCustomSection.java b/platform/platform-impl/src/com/intellij/application/options/editor/EditorTabsOptionsCustomSection.java index 752800cb827d..1ae050d928e6 100644 --- a/platform/platform-impl/src/com/intellij/application/options/editor/EditorTabsOptionsCustomSection.java +++ b/platform/platform-impl/src/com/intellij/application/options/editor/EditorTabsOptionsCustomSection.java @@ -4,7 +4,9 @@ package com.intellij.application.options.editor; import com.intellij.openapi.options.UnnamedConfigurable; /** - * A marker interface for custom editor tabs option sections + * A marker interface for custom editor tabs option sections. + * + * @see EditorTabsConfigurableEP */ public interface EditorTabsOptionsCustomSection extends UnnamedConfigurable { } diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionToolbarImpl.java b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionToolbarImpl.java index 82f80162117e..d265bd404bb2 100644 --- a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionToolbarImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionToolbarImpl.java @@ -1344,6 +1344,7 @@ public class ActionToolbarImpl extends JPanel implements ActionToolbar, QuickAct Dimension pSize = p.getSize(); if (myOrientation == SwingConstants.HORIZONTAL && pSize.height - availSize.height > 8 || myOrientation == SwingConstants.VERTICAL && pSize.width - availSize.width > 8) { + if (availSize.width == 0 && availSize.height == 0) result = p; break; } result = p; diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/PopupMenuPreloader.java b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/PopupMenuPreloader.java index af3676b4ba6e..18c21d50307b 100644 --- a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/PopupMenuPreloader.java +++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/PopupMenuPreloader.java @@ -7,6 +7,7 @@ import com.intellij.openapi.actionSystem.ActionGroup; import com.intellij.openapi.actionSystem.ActionPlaces; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.ex.util.EditorUtil; import com.intellij.openapi.editor.impl.EditorComponentImpl; @@ -49,6 +50,7 @@ public final class PopupMenuPreloader implements Runnable, HierarchyListener { @NotNull String actionPlace, @Nullable PopupHandler popupHandler, @NotNull Supplier<? extends ActionGroup> groupSupplier) { + if (ApplicationManager.getApplication().isUnitTestMode()) return; if (component instanceof EditorComponentImpl && ourEditorContextMenuPreloadCount > 4 || component instanceof IdeMenuBar && SwingUtilities.getWindowAncestor(component) instanceof IdeFrame.Child) { return; diff --git a/platform/platform-impl/src/com/intellij/openapi/command/impl/DefaultUndoReportHandler.java b/platform/platform-impl/src/com/intellij/openapi/command/impl/DefaultUndoReportHandler.java index 235471be7ba2..ef24baa838b6 100644 --- a/platform/platform-impl/src/com/intellij/openapi/command/impl/DefaultUndoReportHandler.java +++ b/platform/platform-impl/src/com/intellij/openapi/command/impl/DefaultUndoReportHandler.java @@ -15,7 +15,7 @@ import org.jetbrains.annotations.Nullable; import java.util.Collection; -public class DefaultUndoReportHandler implements UndoReportHandler { +class DefaultUndoReportHandler implements UndoReportHandler { private static final Logger LOG = Logger.getInstance(DefaultUndoReportHandler.class); @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/command/impl/UndoReportHandler.java b/platform/platform-impl/src/com/intellij/openapi/command/impl/UndoReportHandler.java index d83808b4285c..f7bda96542be 100644 --- a/platform/platform-impl/src/com/intellij/openapi/command/impl/UndoReportHandler.java +++ b/platform/platform-impl/src/com/intellij/openapi/command/impl/UndoReportHandler.java @@ -6,11 +6,13 @@ import com.intellij.openapi.command.undo.UnexpectedUndoException; import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.NlsContexts; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; +@ApiStatus.Internal public interface UndoReportHandler { ExtensionPointName<UndoReportHandler> EP_NAME = ExtensionPointName.create("com.intellij.undoReportHandler"); diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/headertoolbar/MainToolbarAppWidgetFactory.kt b/platform/platform-impl/src/com/intellij/openapi/wm/impl/headertoolbar/MainToolbarAppWidgetFactory.kt index 64852ca5957b..ed0c07b742cd 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/headertoolbar/MainToolbarAppWidgetFactory.kt +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/headertoolbar/MainToolbarAppWidgetFactory.kt @@ -2,12 +2,23 @@ package com.intellij.openapi.wm.impl.headertoolbar import com.intellij.openapi.extensions.ExtensionPointName +import org.jetbrains.annotations.ApiStatus import javax.swing.JComponent +/** + * Factory for Application-level widgets placed in main toolbar. + * + * @see [MainToolbarWidgetFactory] + */ +@ApiStatus.Experimental +@ApiStatus.Internal interface MainToolbarAppWidgetFactory : MainToolbarWidgetFactory { companion object { val EP_NAME = ExtensionPointName<MainToolbarAppWidgetFactory>("com.intellij.appToolbarWidget") } + /** + * Factory method to create widget + */ fun createWidget(): JComponent }
\ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/headertoolbar/MainToolbarProjectWidgetFactory.kt b/platform/platform-impl/src/com/intellij/openapi/wm/impl/headertoolbar/MainToolbarProjectWidgetFactory.kt index 81533aeb2a2d..8b7ebe52561d 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/headertoolbar/MainToolbarProjectWidgetFactory.kt +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/headertoolbar/MainToolbarProjectWidgetFactory.kt @@ -2,11 +2,24 @@ package com.intellij.openapi.wm.impl.headertoolbar import com.intellij.openapi.project.Project +import org.jetbrains.annotations.ApiStatus import javax.swing.JComponent /** - * Extension point: com.intellij.projectToolbarWidget + * Factory for Project-level widgets placed in main toolbar. + * + * Extension point: `com.intellij.projectToolbarWidget` + * + * @see [MainToolbarWidgetFactory] */ +@ApiStatus.Experimental +@ApiStatus.Internal interface MainToolbarProjectWidgetFactory : MainToolbarWidgetFactory { + + /** + * Factory method to create widget + * + * @param project current project + */ fun createWidget(project: Project): JComponent }
\ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/headertoolbar/MainToolbarWidgetFactory.kt b/platform/platform-impl/src/com/intellij/openapi/wm/impl/headertoolbar/MainToolbarWidgetFactory.kt index 70fada1da44b..0fb770a2412a 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/headertoolbar/MainToolbarWidgetFactory.kt +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/headertoolbar/MainToolbarWidgetFactory.kt @@ -1,10 +1,25 @@ // Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.openapi.wm.impl.headertoolbar +import org.jetbrains.annotations.ApiStatus + +/** + * Factory for widgets placed in main toolbar. + * This is root interface which is not supposed to be implemented. Please implement [MainToolbarProjectWidgetFactory] for + * Application level widgets or [MainToolbarProjectWidgetFactory] for Project level widgets + */ +@ApiStatus.Experimental +@ApiStatus.Internal interface MainToolbarWidgetFactory { + /** + * Defines part of toolbar (described in [Position]) when widget should be shown. + */ fun getPosition(): Position + /** + * List of allowed positions for toolbar widgets + */ enum class Position { Left, Right, Center } diff --git a/platform/platform-impl/src/com/intellij/ui/GotItTooltip.kt b/platform/platform-impl/src/com/intellij/ui/GotItTooltip.kt index ac62470efa38..267ca5743ac0 100644 --- a/platform/platform-impl/src/com/intellij/ui/GotItTooltip.kt +++ b/platform/platform-impl/src/com/intellij/ui/GotItTooltip.kt @@ -70,11 +70,13 @@ class GotItTooltipService { } /** - * id is a unique id for the tooltip that will be used to store the tooltip state in <code>PropertiesComponent</code> - * id has the following format: place.where.used - lowercase words separated with dots. - * GotIt tooltip usage statistics can be properly gathered if its id prefix is registered in plugin.xml (PlatformExtensions.xml) - * with gotItTooltipAllowlist extension point. Prefix can cover a whole class of different gotit tooltips. - * If prefix is shorter than the whole ID then all different tooltip usages will be reported in one category described by the prefix. + * The `id` is a unique identifier for the tooltip that will be used to store the tooltip state in [PropertiesComponent]. + * Identifier has the following format: `place.where.used` (lowercase words separated with dots). + * + * Got It tooltip usage statistics can be properly gathered if its identifier prefix is registered in + * `plugin.xml` (`PlatformExtensions.xml`) with `com.intellij.statistics.gotItTooltipAllowlist` extension point. + * Prefix can cover a whole class of different Got It tooltips. If the prefix is shorter than the whole ID, then all different + * tooltip usages will be reported in one category described by the prefix. */ class GotItTooltip(@NonNls val id: String, @Nls val text: String, @@ -127,7 +129,7 @@ class GotItTooltip(@NonNls val id: String, } /** - * Add optional header to the tooltip. + * Add an optional header to the tooltip. */ fun withHeader(@Nls header: String): GotItTooltip { this.header = header @@ -135,7 +137,7 @@ class GotItTooltip(@NonNls val id: String, } /** - * Set preferred tooltip position relatively to the owner component + * Set preferred tooltip position relatively to the owner component. */ fun withPosition(position: Balloon.Position): GotItTooltip { this.position = position @@ -167,7 +169,7 @@ class GotItTooltip(@NonNls val id: String, } /** - * Set close timeout. If set then tooltip appears without "Got It" button. + * Set the close timeout. If set, then the tooltip appears without the "Got It" button. */ @JvmOverloads fun withTimeout(timeout: Int = DEFAULT_TIMEOUT): GotItTooltip { @@ -178,7 +180,7 @@ class GotItTooltip(@NonNls val id: String, } /** - * Limit tooltip body width to the given value. By default it's limited to <code>MAX_WIDTH</code> pixels. + * Limit tooltip body width to the given value. By default, it's limited to `MAX_WIDTH` pixels. */ fun withMaxWidth(width: Int): GotItTooltip { maxWidth = width @@ -186,7 +188,7 @@ class GotItTooltip(@NonNls val id: String, } /** - * Add optional link to the tooltip. + * Add an optional link to the tooltip. */ fun withLink(@Nls linkLabel: String, action: () -> Unit): GotItTooltip { link = object : LinkLabel<Unit>(linkLabel, null) { @@ -197,14 +199,14 @@ class GotItTooltip(@NonNls val id: String, } /** - * Add optional link to the tooltip. Java version. + * Add an optional link to the tooltip. Java version. */ fun withLink(@Nls linkLabel: String, action: Runnable): GotItTooltip { return withLink(linkLabel) { action.run() } } /** - * Add optional browser link to the tooltip. Link is rendered with arrow icon. + * Add an optional browser link to the tooltip. Link is rendered with arrow icon. */ fun withBrowserLink(@Nls linkLabel: String, url: URL): GotItTooltip { link = object : LinkLabel<Unit>(linkLabel, AllIcons.Ide.External_link_arrow) { @@ -231,7 +233,7 @@ class GotItTooltip(@NonNls val id: String, } /** - * Optionally show close shortcut next to Got It button + * Show close shortcut next to the "Got It" button. */ fun andShowCloseShortcut(): GotItTooltip { showCloseShortcut = true @@ -239,7 +241,7 @@ class GotItTooltip(@NonNls val id: String, } /** - * Set notification method that's called when actual <code>Balloon</code> is created. + * Set the notification method that's called when actual [Balloon] is created. */ fun setOnBalloonCreated(callback: (Balloon) -> Unit): GotItTooltip { onBalloonCreated = callback @@ -247,20 +249,21 @@ class GotItTooltip(@NonNls val id: String, } /** - * Returns <code>true</code> if this tooltip can be shown at the given properties settings. + * Returns `true` if this tooltip can be shown at the given properties settings. */ override fun canShow(): Boolean = showCondition("$PROPERTY_PREFIX.$id") /** * Show tooltip for the given component and point to the component. - * If the component is showing (see <code>Component.isShowing</code>) and has not empty bounds then - * the tooltip is shown right away. - * If the component is showing but has empty bounds (technically not visible) then tooltip is shown asynchronously - * when component gets resized to not empty bounds. - * If the component is not showing then tooltip is shown asynchronously when component is added to the hierarchy and - * gets not empty bounds. * - * not for actionButton + * If the component is showing (see [Component.isShowing]) and has not empty bounds, + * then the tooltip is shown right away. + * + * If the component is showing but has empty bounds (technically not visible), + * then tooltip is shown asynchronously when the component gets resized to not empty bounds. + * + * If the component is not showing, then tooltip is shown asynchronously when component is added to the hierarchy + * and gets not empty bounds. */ override fun show(component: JComponent, pointProvider: (Component, Balloon) -> Point) { if (canShow()) { diff --git a/platform/platform-impl/src/com/intellij/ui/Splash.java b/platform/platform-impl/src/com/intellij/ui/Splash.java index 7f075826fb33..6d213b10e173 100644 --- a/platform/platform-impl/src/com/intellij/ui/Splash.java +++ b/platform/platform-impl/src/com/intellij/ui/Splash.java @@ -53,7 +53,7 @@ public final class Splash extends Dialog { private final Image image; public Splash(@NotNull ApplicationInfoEx info) { - super((Frame)null); + super((Frame)null, "splash"); setUndecorated(true); progressSlidePainter = info.getProgressSlides().isEmpty() ? null : new ProgressSlidePainter(info); diff --git a/platform/platform-impl/src/com/intellij/ui/popup/list/ListPopupImpl.java b/platform/platform-impl/src/com/intellij/ui/popup/list/ListPopupImpl.java index 2b6f6e2a7214..82d2b04ed355 100644 --- a/platform/platform-impl/src/com/intellij/ui/popup/list/ListPopupImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/popup/list/ListPopupImpl.java @@ -611,7 +611,7 @@ public class ListPopupImpl extends WizardPopup implements ListPopup, NextStepHan @Override public void mouseReleased(MouseEvent e) { - if (e.isConsumed() || !isActionClick(e) || isMultiSelectionEnabled() && UIUtil.isSelectionButtonDown(e)) return; + if (!isActionClick(e) || isMultiSelectionEnabled() && UIUtil.isSelectionButtonDown(e)) return; IdeEventQueue.getInstance().blockNextEvents(e); // sometimes, after popup close, MOUSE_RELEASE event delivers to other components Object selectedValue = myList.getSelectedValue(); ListPopupStep<Object> listStep = getListStep(); diff --git a/platform/platform-resources/src/META-INF/PlatformExtensions.xml b/platform/platform-resources/src/META-INF/PlatformExtensions.xml index de8c36d4da28..0fbb8bf0b8f6 100644 --- a/platform/platform-resources/src/META-INF/PlatformExtensions.xml +++ b/platform/platform-resources/src/META-INF/PlatformExtensions.xml @@ -147,6 +147,7 @@ <applicationService serviceImplementation="com.intellij.ide.GeneralSettings" preload="notHeadless"/> <applicationService serviceImplementation="com.intellij.ui.jcef.JBCefStartup" preload="true" os="mac"/> + <cachesInvalidator implementation="com.intellij.ui.jcef.JBCefAppCacheInvalidator" order="last" /> <applicationService serviceInterface="com.intellij.ide.ui.IdeUiService" serviceImplementation="com.intellij.openapi.fileEditor.impl.IdeUiServiceImpl"/> diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/project/DumbServiceImplTest.groovy b/platform/platform-tests/testSrc/com/intellij/openapi/project/DumbServiceImplTest.groovy index f7a672bd966f..29304c37706f 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/project/DumbServiceImplTest.groovy +++ b/platform/platform-tests/testSrc/com/intellij/openapi/project/DumbServiceImplTest.groovy @@ -1,4 +1,4 @@ -// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.openapi.project import com.intellij.openapi.application.ApplicationManager @@ -21,6 +21,7 @@ import com.intellij.util.indexing.FileBasedIndex import com.intellij.util.indexing.FileBasedIndexImpl import com.intellij.util.indexing.contentQueue.IndexUpdateRunner import com.intellij.util.indexing.diagnostic.ProjectIndexingHistoryImpl +import com.intellij.util.indexing.diagnostic.ScanningType import com.intellij.util.ui.UIUtil import org.jetbrains.annotations.NotNull import org.junit.Assert @@ -171,7 +172,7 @@ class DumbServiceImplTest extends BasePlatformTestCase { def index = FileBasedIndex.getInstance() as FileBasedIndexImpl new IndexUpdateRunner(index, ConcurrencyUtil.newSameThreadExecutorService(), 1) .indexFiles(project, Collections.singletonList(new IndexUpdateRunner.FileSet(project, "child", [child])), - indicator, new ProjectIndexingHistoryImpl(getProject(), "Testing", false)) + indicator, new ProjectIndexingHistoryImpl(getProject(), "Testing", ScanningType.PARTIAL)) } } catch (ProcessCanceledException e) { diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/impl/VirtualFilePointerTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/impl/VirtualFilePointerTest.java index eed27521d73d..34f6d27e30d8 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/impl/VirtualFilePointerTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/impl/VirtualFilePointerTest.java @@ -11,11 +11,13 @@ import com.intellij.openapi.application.WriteAction; import com.intellij.openapi.application.ex.PathManagerEx; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileAttributes; import com.intellij.openapi.util.io.FileSystemUtil; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.io.IoTestUtil; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.*; import com.intellij.openapi.vfs.ex.temp.TempFileSystem; import com.intellij.openapi.vfs.impl.jar.JarFileSystemImpl; @@ -29,16 +31,14 @@ import com.intellij.openapi.vfs.pointers.VirtualFilePointerManager; import com.intellij.testFramework.*; import com.intellij.testFramework.fixtures.BareTestFixtureTestCase; import com.intellij.testFramework.rules.TempDirectory; -import com.intellij.util.ExceptionUtil; -import com.intellij.util.IncorrectOperationException; -import com.intellij.util.TestTimeOut; -import com.intellij.util.TimeoutUtil; +import com.intellij.util.*; import com.intellij.util.concurrency.Semaphore; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.io.SuperUserStatus; import com.intellij.util.messages.MessageBusConnection; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -50,6 +50,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.attribute.FileTime; +import java.security.SecureRandom; import java.time.Instant; import java.util.*; import java.util.concurrent.CountDownLatch; @@ -213,11 +214,6 @@ public class VirtualFilePointerTest extends BareTestFixtureTestCase { } @Test - public void testCreateVFPFromStrangeJarUrlMustCrash() throws IOException { - UsefulTestCase.assertThrows(IllegalArgumentException.class, ()->myVirtualFilePointerManager.create("jar://C:/!/java.base", disposable, null)); - } - - @Test public void testPointerForFileSystemRoot1() { File rootDir = new File("/"); assertTrue(rootDir.exists()); @@ -1281,4 +1277,46 @@ public class VirtualFilePointerTest extends BareTestFixtureTestCase { assertTrue(pointer.getUrl(), pointer.getUrl().endsWith(path)); assertEquals("allopen-compiler-plugin.jar", pointer.getFileName()); } + @Test + public void testJarUrlContainingInvalidExclamationInTheMiddleMustNotCrashAnything() { + File root = tempDir.getRoot(); + while (root.getParentFile() != null) root = root.getParentFile(); + String diskRoot = UriUtil.trimTrailingSlashes(FileUtil.toSystemIndependentName(root.getPath())); + + assertJarSeparatorParsedCorrectlyForFileInsideJar("/", "!/", null, "_"); + assertJarSeparatorParsedCorrectlyForFileInsideJar("!/", "!/", null, "_"); + assertJarSeparatorParsedCorrectlyForFileInsideJar("!/xxx", "!/xxx", "xxx", "xxx"); + assertJarSeparatorParsedCorrectlyForFileInsideJar("!/xxx/!/yyy", "!/xxx/!/yyy", "yyy", "xxx/!/yyy"); + if (SystemInfo.isWindows) { + assertJarSeparatorParsedCorrectly("jar://" + diskRoot + "/!/abc", "jar://" + diskRoot + "!/abc", "abc"); + assertJarSeparatorParsedCorrectly("jar://" + diskRoot + "!/abc", "jar://" + diskRoot + "!/abc", "abc"); + } + } + + private void assertJarSeparatorParsedCorrectlyForFileInsideJar(@NotNull String relativePathInsideJar, @NotNull String expectedPointerRelativeUrl, @Nullable String expectedPointerFileName, @NotNull String expectedPathInsideJar) { + String abc = "abc" + new SecureRandom().nextLong()+".jar"; + String tempRoot = UriUtil.trimTrailingSlashes(FileUtil.toSystemIndependentName(tempDir.getRoot().getPath())); + VirtualFilePointer pointer = VirtualFilePointerManager.getInstance().create("jar://" + tempRoot + "/" + abc + relativePathInsideJar, disposable, null); + assertEquals(expectedPointerRelativeUrl, StringUtil.trimStart(pointer.getUrl(), "jar://" + tempRoot + "/" + abc)); + String expectedPointerFileNameToCheck = expectedPointerFileName == null ? abc : expectedPointerFileName; + assertEquals(expectedPointerFileNameToCheck, pointer.getFileName()); + assertEquals(JarFileSystem.getInstance(), ((VirtualFilePointerImpl)pointer).myNode.myFS); + assertFalse(pointer.isValid()); + + File jar = IoTestUtil.createTestJar(new File(tempRoot+"/"+abc), List.of(Pair.create(expectedPathInsideJar, new byte[]{' ', ' '}))); + assertNotNull(LocalFileSystem.getInstance().refreshAndFindFileByIoFile(jar)); + + assertTrue(pointer.isValid()); + VirtualFile virtualFile = pointer.getFile(); + assertNotNull(virtualFile); + assertEquals(expectedPointerFileNameToCheck, virtualFile.getName()); + } + + private void assertJarSeparatorParsedCorrectly(@NotNull String sourceUrl, @NotNull String expectedPointerUrl, @NotNull String expectedPointerFileName) { + VirtualFilePointer pointer = VirtualFilePointerManager.getInstance().create(sourceUrl, disposable, null); + assertEquals(expectedPointerUrl, pointer.getUrl()); + assertEquals(expectedPointerFileName, pointer.getFileName()); + assertEquals(JarFileSystem.getInstance(), ((VirtualFilePointerImpl)pointer).myNode.myFS); + assertFalse(pointer.isValid()); + } }
\ No newline at end of file diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/FileWatcherTest.kt b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/FileWatcherTest.kt index 075026c81749..d8e5561a56b0 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/FileWatcherTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/FileWatcherTest.kt @@ -459,30 +459,30 @@ class FileWatcherTest : BareTestFixtureTestCase() { val target = tempDir.newDirectory("top") val file = tempDir.newFile("top/sub/test.txt") - val substRoot = IoTestUtil.createSubst(target.path) - VfsRootAccess.allowRootAccess(testRootDisposable, substRoot.path) - val vfsRoot = fs.findFileByIoFile(substRoot)!! - watchedPaths += substRoot.path + IoTestUtil.performTestOnWindowsSubst(target.path) { substRoot -> + VfsRootAccess.allowRootAccess(testRootDisposable, substRoot.path) + val vfsRoot = fs.findFileByIoFile(substRoot)!! + watchedPaths += substRoot.path - val substFile = File(substRoot, "sub/test.txt") - refresh(target) - refresh(substRoot) + val substFile = File(substRoot, "sub/test.txt") + refresh(target) + refresh(substRoot) - try { - watch(substRoot) - assertEvents({ file.writeText("new content") }, mapOf(substFile to 'U')) + try { + watch(substRoot) + assertEvents({ file.writeText("new content") }, mapOf(substFile to 'U')) - val request = watch(target) - assertEvents({ file.writeText("updated content") }, mapOf(file to 'U', substFile to 'U')) - assertEvents({ assertTrue(file.delete()) }, mapOf(file to 'D', substFile to 'D')) - unwatch(request) + val request = watch(target) + assertEvents({ file.writeText("updated content") }, mapOf(file to 'U', substFile to 'U')) + assertEvents({ assertTrue(file.delete()) }, mapOf(file to 'D', substFile to 'D')) + unwatch(request) - assertEvents({ file.writeText("re-creation") }, mapOf(substFile to 'C')) - } - finally { - IoTestUtil.deleteSubst(substRoot.path) - (vfsRoot as NewVirtualFile).markDirty() - fs.refresh(false) + assertEvents({ file.writeText("re-creation") }, mapOf(substFile to 'C')) + } + finally { + (vfsRoot as NewVirtualFile).markDirty() + fs.refresh(false) + } } } diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/WslFileWatcherTest.kt b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/WslFileWatcherTest.kt index 43333ea3cffa..83738beb9836 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/WslFileWatcherTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/WslFileWatcherTest.kt @@ -7,7 +7,6 @@ import com.intellij.openapi.application.runWriteAction import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.util.SystemInfo import com.intellij.openapi.util.io.FileUtil -import com.intellij.openapi.util.io.IoTestUtil import com.intellij.openapi.util.io.IoTestUtil.* import com.intellij.openapi.vfs.LocalFileSystem import com.intellij.openapi.vfs.VfsUtilCore @@ -455,30 +454,30 @@ class WslFileWatcherTest : BareTestFixtureTestCase() { val target = tempDir.newDirectory("top") val file = tempDir.newFile("top/sub/test.txt") - val substRoot = createSubst(target.path) - VfsRootAccess.allowRootAccess(testRootDisposable, substRoot.path) - val vfsRoot = fs.findFileByIoFile(substRoot)!! - watchedPaths += substRoot.path + performTestOnWindowsSubst(target.path) { substRoot -> + VfsRootAccess.allowRootAccess(testRootDisposable, substRoot.path) + val vfsRoot = fs.findFileByIoFile(substRoot)!! + watchedPaths += substRoot.path - val substFile = File(substRoot, "sub/test.txt") - refresh(target) - refresh(substRoot) + val substFile = File(substRoot, "sub/test.txt") + refresh(target) + refresh(substRoot) - try { - watch(substRoot) - assertEvents({ file.writeText("new content") }, mapOf(substFile to 'U')) + try { + watch(substRoot) + assertEvents({ file.writeText("new content") }, mapOf(substFile to 'U')) - val request = watch(target) - assertEvents({ file.writeText("updated content") }, mapOf(file to 'U', substFile to 'U')) - assertEvents({ assertTrue(file.delete()) }, mapOf(file to 'D', substFile to 'D')) - unwatch(request) + val request = watch(target) + assertEvents({ file.writeText("updated content") }, mapOf(file to 'U', substFile to 'U')) + assertEvents({ assertTrue(file.delete()) }, mapOf(file to 'D', substFile to 'D')) + unwatch(request) - assertEvents({ file.writeText("re-creation") }, mapOf(substFile to 'C')) - } - finally { - deleteSubst(substRoot.path) - (vfsRoot as NewVirtualFile).markDirty() - fs.refresh(false) + assertEvents({ file.writeText("re-creation") }, mapOf(substFile to 'C')) + } + finally { + (vfsRoot as NewVirtualFile).markDirty() + fs.refresh(false) + } } } diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/newvfs/persistent/PersistentFsTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/newvfs/persistent/PersistentFsTest.java index 986584903066..02cca13d4052 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/newvfs/persistent/PersistentFsTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/newvfs/persistent/PersistentFsTest.java @@ -15,6 +15,7 @@ import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.roots.ContentEntry; import com.intellij.openapi.roots.ModuleRootModificationUtil; import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileAttributes; import com.intellij.openapi.util.io.FileUtil; @@ -158,21 +159,19 @@ public class PersistentFsTest extends BareTestFixtureTestCase { @Test public void testDeleteSubstRoots() { IoTestUtil.assumeWindows(); + Ref<VirtualFile> subst = new Ref<>(); + Ref<String> substPath = new Ref<>(); - File substRoot = IoTestUtil.createSubst(tempDirectory.getRoot().getPath()); - VirtualFile subst; - try { - subst = refreshAndFind(substRoot); + IoTestUtil.performTestOnWindowsSubst(tempDirectory.getRoot().getPath(), substRoot -> { + substPath.set(substRoot.getPath()); + subst.set(refreshAndFind(substRoot)); assertNotNull(substRoot.listFiles()); - } - finally { - IoTestUtil.deleteSubst(substRoot.getPath()); - } - subst.refresh(false, true); + }); + subst.get().refresh(false, true); VirtualFile[] roots = PersistentFS.getInstance().getRoots(LocalFileSystem.getInstance()); for (VirtualFile root : roots) { - String prefix = StringUtil.commonPrefix(root.getPath(), substRoot.getPath()); + String prefix = StringUtil.commonPrefix(root.getPath(), substPath.get()); assertTrue(prefix, prefix.isEmpty()); } } diff --git a/platform/platform-tests/testSrc/com/intellij/util/io/PersistencePerformanceTest.java b/platform/platform-tests/testSrc/com/intellij/util/io/PersistencePerformanceTest.java index f78a77c29cdf..7af410828c16 100644 --- a/platform/platform-tests/testSrc/com/intellij/util/io/PersistencePerformanceTest.java +++ b/platform/platform-tests/testSrc/com/intellij/util/io/PersistencePerformanceTest.java @@ -1,4 +1,4 @@ -// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.util.io; import com.intellij.openapi.application.ApplicationManager; @@ -15,6 +15,7 @@ import com.intellij.util.indexing.FileBasedIndexImpl; import com.intellij.util.indexing.UnindexedFilesUpdater; import com.intellij.util.indexing.contentQueue.IndexUpdateRunner; import com.intellij.util.indexing.diagnostic.ProjectIndexingHistoryImpl; +import com.intellij.util.indexing.diagnostic.ScanningType; import org.jetbrains.annotations.NotNull; import java.io.DataInput; @@ -108,7 +109,7 @@ public class PersistencePerformanceTest extends BasePlatformTestCase { Thread.sleep(100); new IndexUpdateRunner(index, UnindexedFilesUpdater.GLOBAL_INDEXING_EXECUTOR, UnindexedFilesUpdater.getNumberOfIndexingThreads()) .indexFiles(getProject(), Collections.singletonList(new IndexUpdateRunner.FileSet(getProject(), "test files", files)), - new EmptyProgressIndicator(), new ProjectIndexingHistoryImpl(getProject(), "Testing", false)); + new EmptyProgressIndicator(), new ProjectIndexingHistoryImpl(getProject(), "Testing", ScanningType.PARTIAL)); } for (Future<Boolean> future : futures) { assertTrue(future.get()); diff --git a/platform/projectModel-api/src/com/intellij/openapi/module/ModuleComponent.java b/platform/projectModel-api/src/com/intellij/openapi/module/ModuleComponent.java index 9a5ab1a3edf8..bb1ba90f42e5 100644 --- a/platform/projectModel-api/src/com/intellij/openapi/module/ModuleComponent.java +++ b/platform/projectModel-api/src/com/intellij/openapi/module/ModuleComponent.java @@ -1,15 +1,15 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.openapi.module; import com.intellij.openapi.components.BaseComponent; import com.intellij.openapi.project.Project; /** - * @deprecated Components are deprecated. If you register a class as a module component it will be loaded, its instance will be created and + * @deprecated Components are deprecated, please see <a href="https://plugins.jetbrains.com/docs/intellij/plugin-components.html">SDK Docs</a> for guidelines on migrating to other APIs. + * <p> + * If you register a class as a module component it will be loaded, its instance will be created and * {@link #initComponent()}, {@link #moduleAdded()} methods will be called for each module even if user doesn't use * any feature of your plugin. Also, plugins declaring module components do not support dynamic loading. - * <p/> - * Please see <a href="http://www.jetbrains.org/intellij/sdk/docs/basics/plugin_structure/plugin_components.html">SDK Docs</a> for guidelines on migrating to other APIs. */ @Deprecated public interface ModuleComponent extends BaseComponent { @@ -38,7 +38,7 @@ public interface ModuleComponent extends BaseComponent { /** * Invoked when the module corresponding to this component instance has been completely * loaded and added to the project. - * <p> + * * @deprecated Consider to use {@link com.intellij.ProjectTopics#MODULES} ({@link com.intellij.openapi.project.ModuleListener#moduleAdded(Project, Module)}) */ @Deprecated diff --git a/platform/refactoring/src/com/intellij/refactoring/extractMethod/ExtractMethodHelper.java b/platform/refactoring/src/com/intellij/refactoring/extractMethod/ExtractMethodHelper.java index 320a13398a32..c6f3bcf3ec12 100644 --- a/platform/refactoring/src/com/intellij/refactoring/extractMethod/ExtractMethodHelper.java +++ b/platform/refactoring/src/com/intellij/refactoring/extractMethod/ExtractMethodHelper.java @@ -108,9 +108,8 @@ public final class ExtractMethodHelper { boolean isPerformanceScript = System.getProperty("testscript.filename") != null; final Project project = callElement.getProject(); final boolean exitCode = isUnittest || isPerformanceScript || - MessageDialogBuilder.yesNo(message, - RefactoringBundle.message( - "refactoring.extract.method.dialog.title"), + MessageDialogBuilder.yesNo(RefactoringBundle.message("refactoring.extract.method.dialog.title"), + message, UIUtil.getInformationIcon()).ask(project); if (exitCode) { boolean replaceAll = false; diff --git a/platform/refactoring/src/com/intellij/refactoring/rename/RenameInputValidator.java b/platform/refactoring/src/com/intellij/refactoring/rename/RenameInputValidator.java index a094d6404265..ee381ce1bdc2 100644 --- a/platform/refactoring/src/com/intellij/refactoring/rename/RenameInputValidator.java +++ b/platform/refactoring/src/com/intellij/refactoring/rename/RenameInputValidator.java @@ -9,10 +9,11 @@ import org.jetbrains.annotations.NotNull; /** * Validates input for new chosen name of the element to be renamed. + * Extend {@link RenameInputValidatorEx} to provide custom error messages. * <p> - * Extend {@link RenameInputValidatorEx} to provide custom error message. - * - * @author Gregory.Shrago + * Register in {@code com.intellij.renameInputValidator} extension point. + * @see RenameInputValidatorRegistry + * @see <a href="https://plugins.jetbrains.com/docs/intellij/rename-refactoring.html">Rename Refactoring (IntelliJ Platform Docs)</a> */ public interface RenameInputValidator { ExtensionPointName<RenameInputValidator> EP_NAME = ExtensionPointName.create("com.intellij.renameInputValidator"); @@ -21,10 +22,10 @@ public interface RenameInputValidator { ElementPattern<? extends PsiElement> getPattern(); /** - * Invoked for elements accepted by pattern {@link #getPattern()}. + * Invoked for elements accepted by pattern returned from {@link #getPattern()}. * <p> - * Return {@code true} if {@link RenameInputValidatorEx} should return custom error message, + * Return {@code true} if {@link RenameInputValidatorEx} should return a custom error message, * otherwise default message "'[newName]' is not a valid identifier" will be shown. */ boolean isInputValid(@NotNull final String newName, @NotNull final PsiElement element, @NotNull final ProcessingContext context); -}
\ No newline at end of file +} diff --git a/platform/refactoring/src/com/intellij/refactoring/rename/RenameInputValidatorEx.java b/platform/refactoring/src/com/intellij/refactoring/rename/RenameInputValidatorEx.java index 013c485d6153..5bf9f98edde3 100644 --- a/platform/refactoring/src/com/intellij/refactoring/rename/RenameInputValidatorEx.java +++ b/platform/refactoring/src/com/intellij/refactoring/rename/RenameInputValidatorEx.java @@ -9,17 +9,17 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** - * Adds ability to provide custom error message. + * Rename input validator with the ability to provide custom error messages. */ public interface RenameInputValidatorEx extends RenameInputValidator { /** - * Called only if all input validators ({@link RenameInputValidator}) accept + * Called only if all {@link RenameInputValidator}s accept * the new name in {@link #isInputValid(String, PsiElement, ProcessingContext)} * and name is a valid identifier for the language of the element. * - * @return {@code null} if newName is a valid name, custom error message otherwise + * @return {@code null} if {@code newName} is a valid name, custom error message otherwise */ @Nullable @DialogMessage String getErrorMessage(@NotNull String newName, @NotNull Project project); -}
\ No newline at end of file +} diff --git a/platform/remoteDev-util/src/com/intellij/remoteDev/downloader/CodeWithMeClientDownloader.kt b/platform/remoteDev-util/src/com/intellij/remoteDev/downloader/CodeWithMeClientDownloader.kt index edc1f8622f18..873aa12c9a08 100644 --- a/platform/remoteDev-util/src/com/intellij/remoteDev/downloader/CodeWithMeClientDownloader.kt +++ b/platform/remoteDev-util/src/com/intellij/remoteDev/downloader/CodeWithMeClientDownloader.kt @@ -8,6 +8,7 @@ import com.intellij.internal.statistic.StructuredIdeActivity import com.intellij.openapi.application.ModalityState import com.intellij.openapi.components.service import com.intellij.openapi.diagnostic.ControlFlowException +import com.intellij.openapi.diagnostic.debug import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.progress.EmptyProgressIndicator import com.intellij.openapi.progress.ProcessCanceledException @@ -515,7 +516,7 @@ object CodeWithMeClientDownloader { val (executable, fullLauncherCmd) = findLauncherUnderCwmGuestRoot(guestRoot) val guestHome = findCwmGuestHome(guestRoot) - val linkTarget = if (SystemInfo.isMac) jdkRoot / "jbr" else detectTrueJdkRoot(jdkRoot) + val linkTarget = if (SystemInfo.isMac) detectMacOsJbrDirectory(jdkRoot) else detectTrueJdkRoot(jdkRoot) createSymlink(guestHome / "jbr", linkTarget) // Update mtime on JRE & CWM Guest roots. The cleanup process will use it later. @@ -630,8 +631,15 @@ object CodeWithMeClientDownloader { return processLifetimeDef.lifetime } + private fun detectMacOsJbrDirectory(root: Path): Path { + val jbrDirectory = root.listDirectoryEntries().find { it.nameWithoutExtension.startsWith("jbr") } + + LOG.debug { "JBR directory: $jbrDirectory" } + return jbrDirectory ?: error("Unable to find target content directory starts with 'jbr' inside MacOS package: '$root'") + } + fun createSymlinkToJdkFromGuest(guestRoot: Path, jdkRoot: Path) { - val linkTarget = if (SystemInfo.isMac) jdkRoot / "jbr" else detectTrueJdkRoot(jdkRoot) + val linkTarget = if (SystemInfo.isMac) detectMacOsJbrDirectory(jdkRoot) else detectTrueJdkRoot(jdkRoot) val guestHome = findCwmGuestHome(guestRoot) createSymlink(guestHome / "jbr", linkTarget) } diff --git a/platform/remoteDev-util/src/com/intellij/remoteDev/downloader/CodeWithMeGuestLauncher.kt b/platform/remoteDev-util/src/com/intellij/remoteDev/downloader/CodeWithMeGuestLauncher.kt index 66ccb80e16c1..b2d2ca3298c9 100644 --- a/platform/remoteDev-util/src/com/intellij/remoteDev/downloader/CodeWithMeGuestLauncher.kt +++ b/platform/remoteDev-util/src/com/intellij/remoteDev/downloader/CodeWithMeGuestLauncher.kt @@ -65,7 +65,14 @@ object CodeWithMeGuestLauncher { val pair = CodeWithMeClientDownloader.downloadClientAndJdk(sessionInfo, progressIndicator) if (pair == null) return - clientLifetime = runDownloadedClient(project?.createLifetime() ?: Lifetime.Eternal, pair.first, pair.second, url, product, progressIndicator) + clientLifetime = runDownloadedClient( + lifetime = project?.createLifetime() ?: Lifetime.Eternal, + pathToClient = pair.first, + pathToJre = pair.second, + urlForThinClient = url, + product = product, + progressIndicator = progressIndicator + ) } catch (t: Throwable) { LOG.warn(t) diff --git a/platform/testFramework/src/com/intellij/openapi/util/io/IoTestUtil.java b/platform/testFramework/src/com/intellij/openapi/util/io/IoTestUtil.java index a6df3c241f72..0ead19a709a7 100644 --- a/platform/testFramework/src/com/intellij/openapi/util/io/IoTestUtil.java +++ b/platform/testFramework/src/com/intellij/openapi/util/io/IoTestUtil.java @@ -26,6 +26,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.*; +import java.util.function.Consumer; import java.util.function.Predicate; import java.util.jar.JarFile; import java.util.stream.Collectors; @@ -57,7 +58,7 @@ public final class IoTestUtil { return filterParts(Charset.forName(forEncoding).newEncoder()::canEncode); } - private static String filterParts(Predicate<String> predicate) { + private static String filterParts(@NotNull Predicate<? super String> predicate) { return StringUtil.nullize(Stream.of(UNICODE_PARTS).filter(predicate).collect(Collectors.joining("_"))); } @@ -174,7 +175,11 @@ public final class IoTestUtil { assertTrue(new File(junction).delete()); } - public static @NotNull File createSubst(@NotNull String target) { + /** + * (Windows-only) + * creates "subst" drive for target, perform some tests on it and deletes it + */ + public static void performTestOnWindowsSubst(@NotNull String target, @NotNull Consumer<? super @NotNull File> createdSubstTester) { assertTrue(SystemInfo.isWindows); File targetFile = new File(target); assertTrue(targetFile.getPath(), targetFile.isDirectory()); @@ -182,11 +187,13 @@ public final class IoTestUtil { runCommand("subst", substRoot, targetFile.getPath()); File rootFile = new File(substRoot + "\\"); assertTrue("target=" + targetFile + ", subst=" + rootFile, rootFile.isDirectory()); - return rootFile; - } - public static void deleteSubst(@NotNull String substRoot) { - runCommand("subst", StringUtil.trimEnd(substRoot, '\\'), "/d"); + try { + createdSubstTester.accept(rootFile); + } + finally { + runCommand("subst", StringUtil.trimEnd(substRoot, '\\'), "/d"); + } } public static void createFifo(@NotNull String path) { @@ -266,7 +273,7 @@ public final class IoTestUtil { } } - public static @NotNull File createTestJar(@NotNull File jarFile, @NotNull Collection<Pair<String, byte[]>> namesAndContents) { + public static @NotNull File createTestJar(@NotNull File jarFile, @NotNull Collection<? extends Pair<String, byte[]>> namesAndContents) { try (ZipOutputStream stream = new ZipOutputStream(new FileOutputStream(jarFile))) { for (Pair<String, byte[]> p : namesAndContents) { String name = p.first; diff --git a/platform/util/src/com/intellij/openapi/util/io/OSAgnosticPathUtil.java b/platform/util/src/com/intellij/openapi/util/io/OSAgnosticPathUtil.java index 189dc7a8bbd4..5b204af5829b 100644 --- a/platform/util/src/com/intellij/openapi/util/io/OSAgnosticPathUtil.java +++ b/platform/util/src/com/intellij/openapi/util/io/OSAgnosticPathUtil.java @@ -65,7 +65,14 @@ public final class OSAgnosticPathUtil { } public static boolean isAbsoluteDosPath(@NotNull String path) { - return path.length() > 2 && path.charAt(1) == ':' && isSlash(path.charAt(2)) && isDriveLetter(path.charAt(0)); + return path.length() > 2 && startsWithWindowsDrive(path) && isSlash(path.charAt(2)); + } + + /** + * @return true when the path starts with a drive letter followed by colon, e.g., "C:" + */ + public static boolean startsWithWindowsDrive(@NotNull String path) { + return path.length() >= 2 && path.charAt(1) == ':' && isDriveLetter(path.charAt(0)); } public static boolean isUncPath(@NotNull String path) { diff --git a/platform/util/testSrc/com/intellij/openapi/util/io/FileAttributesReadingTest.java b/platform/util/testSrc/com/intellij/openapi/util/io/FileAttributesReadingTest.java index 5d478baaaccf..f790be64de75 100644 --- a/platform/util/testSrc/com/intellij/openapi/util/io/FileAttributesReadingTest.java +++ b/platform/util/testSrc/com/intellij/openapi/util/io/FileAttributesReadingTest.java @@ -432,8 +432,7 @@ public abstract class FileAttributesReadingTest { assumeWindows(); tempDir.newFile("file.txt"); // just to populate a directory - File substRoot = createSubst(tempDir.getRoot().getPath()); - try { + performTestOnWindowsSubst(tempDir.getRoot().getPath(), substRoot ->{ FileAttributes attributes = getAttributes(substRoot); assertEquals(substRoot + " " + attributes, FileAttributes.Type.DIRECTORY, attributes.getType()); assertFalse(substRoot + " " + attributes, attributes.isSymLink()); @@ -445,10 +444,7 @@ public abstract class FileAttributesReadingTest { File file = children[0]; String target = resolveSymLink(file); assertEquals(file.getPath(), target); - } - finally { - deleteSubst(substRoot.getPath()); - } + }); } @Test diff --git a/platform/vcs-impl/src/com/intellij/codeInsight/hints/VcsCodeVisionProvider.kt b/platform/vcs-impl/src/com/intellij/codeInsight/hints/VcsCodeVisionProvider.kt index ecb2e77cad74..e332218bed7c 100644 --- a/platform/vcs-impl/src/com/intellij/codeInsight/hints/VcsCodeVisionProvider.kt +++ b/platform/vcs-impl/src/com/intellij/codeInsight/hints/VcsCodeVisionProvider.kt @@ -74,29 +74,23 @@ class VcsCodeVisionProvider : CodeVisionProvider<Unit> { val lenses = ArrayList<Pair<TextRange, CodeVisionEntry>>() - try { - val visionLanguageContext = VcsCodeVisionLanguageContext.providersExtensionPoint.forLanguage(language) - ?: return@runReadAction READY_EMPTY - val traverser = SyntaxTraverser.psiTraverser(file) - for (element in traverser.preOrderDfsTraversal()) { - if (visionLanguageContext.isAccepted(element)) { - val textRange = InlayHintsUtils.getTextRangeWithoutLeadingCommentsAndWhitespaces(element) - val length = editor.document.textLength - val adjustedRange = TextRange(min(textRange.startOffset, length), min(textRange.endOffset, length)) - val codeAuthorInfo = PREVIEW_INFO_KEY.get(editor) ?: getCodeAuthorInfo(element.project, adjustedRange, editor, aspect) - val text = codeAuthorInfo.getText() - val icon = if (codeAuthorInfo.mainAuthor != null) AllIcons.Vcs.Author else null - val clickHandler = CodeAuthorClickHandler(element, language) - val entry = ClickableTextCodeVisionEntry(text, id, onClick = clickHandler, icon, text, text, emptyList()) - entry.showInMorePopup = false - lenses.add(adjustedRange to entry) - } + val visionLanguageContext = VcsCodeVisionLanguageContext.providersExtensionPoint.forLanguage(language) + ?: return@runReadAction READY_EMPTY + val traverser = SyntaxTraverser.psiTraverser(file) + for (element in traverser.preOrderDfsTraversal()) { + if (visionLanguageContext.isAccepted(element)) { + val textRange = InlayHintsUtils.getTextRangeWithoutLeadingCommentsAndWhitespaces(element) + val length = editor.document.textLength + val adjustedRange = TextRange(min(textRange.startOffset, length), min(textRange.endOffset, length)) + val codeAuthorInfo = PREVIEW_INFO_KEY.get(editor) ?: getCodeAuthorInfo(element.project, adjustedRange, editor, aspect) + val text = codeAuthorInfo.getText() + val icon = if (codeAuthorInfo.mainAuthor != null) AllIcons.Vcs.Author else null + val clickHandler = CodeAuthorClickHandler(element, language) + val entry = ClickableTextCodeVisionEntry(text, id, onClick = clickHandler, icon, text, text, emptyList()) + entry.showInMorePopup = false + lenses.add(adjustedRange to entry) } } - catch (e: Exception) { - e.printStackTrace() - throw e - } return@runReadAction CodeVisionState.Ready(lenses) } } @@ -135,7 +129,7 @@ class VcsCodeVisionProvider : CodeVisionProvider<Unit> { val visionLanguageContext = VcsCodeVisionLanguageContext.providersExtensionPoint.forLanguage(language) ?: return null val vcs = ProjectLevelVcsManager.getInstance(project).getVcsFor(psiFile.virtualFile) ?: return null if ("Git" != vcs.name) { - return null + return null } if (vcs.annotationProvider !is CacheableAnnotationProvider) return null return object: BypassBasedPlaceholderCollector { diff --git a/platform/vfs-impl/src/com/intellij/openapi/vfs/impl/FilePartNodeRoot.java b/platform/vfs-impl/src/com/intellij/openapi/vfs/impl/FilePartNodeRoot.java index 059768d0492f..9dd4671ed360 100644 --- a/platform/vfs-impl/src/com/intellij/openapi/vfs/impl/FilePartNodeRoot.java +++ b/platform/vfs-impl/src/com/intellij/openapi/vfs/impl/FilePartNodeRoot.java @@ -2,6 +2,7 @@ package com.intellij.openapi.vfs.impl; import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.util.text.StringUtilRt; import com.intellij.openapi.vfs.*; @@ -138,27 +139,27 @@ final class FilePartNodeRoot extends FilePartNode { @NotNull NodeToUpdate findOrCreateByPath(@NotNull String path, @NotNull NewVirtualFileSystem fs) { NewVirtualFileSystem currentFS; - boolean jarSuffix; + String relativePathInsideJar; if (fs instanceof ArchiveFileSystem) { currentFS = LocalFileSystem.getInstance(); - // strip trailing "!/" because LocalVirtualFileSystem.normalize() are afraid of them and strip them out - if (path.endsWith(JarFileSystem.JAR_SEPARATOR)) { - path = path.substring(0, path.length() - JarFileSystem.JAR_SEPARATOR.length()); - jarSuffix = true; + int i = path.lastIndexOf(JarFileSystem.JAR_SEPARATOR); + // strip everything after "!/" and after extractRootFromPath() append it back, + // because LocalVirtualFileSystem.normalize() is afraid of these jar separators and tries to absolutize them incorrectly (e.g. "C:!/foo" -> "C:idea/bin/!/foo") + if (i == -1) { + relativePathInsideJar = JarFileSystem.JAR_SEPARATOR; } else { - jarSuffix = false; + relativePathInsideJar = path.substring(i); + path = path.substring(0, i); } } else { currentFS = fs; - jarSuffix = false; + relativePathInsideJar = ""; } Pair<NewVirtualFile, String> pair = VfsImplUtil.extractRootFromPath(currentFS, path); String pathFromRoot = pair == null ? path : pair.second; - if (jarSuffix) { - pathFromRoot += JarFileSystem.JAR_SEPARATOR; - } + pathFromRoot += relativePathInsideJar; List<String> names = splitNames(pathFromRoot); NewVirtualFile fsRoot = pair == null ? null : pair.first; @@ -283,16 +284,16 @@ final class FilePartNodeRoot extends FilePartNode { if (end == 0) return Collections.emptyList(); List<String> names = new ArrayList<>(Math.max(20, end/4)); // path length -> path height approximation while (true) { - boolean isJarSeparator = StringUtil.endsWith(path, 0, end, JarFileSystem.JAR_SEPARATOR) && end > 2 && path.charAt(end - 3) != '/'; + boolean isJarSeparator = StringUtil.endsWith(path, 0, end, JarFileSystem.JAR_SEPARATOR) + && (end == 2 && SystemInfo.isWindows || end > 2 && path.charAt(end - 3) != '/'); if (isJarSeparator) { names.add(JarFileSystem.JAR_SEPARATOR); end -= 2; - continue; } - if (path.charAt(end-1) == '/') { + if (end != 0 && path.charAt(end-1) == '/') { end--; } - if (end == 0 && path.charAt(0) == '/') { + if (end == 0) { break; // here's separator between non-empty root (e.g. on Windows) and path's tail } int startIndex = extractName(path, end); @@ -321,8 +322,8 @@ final class FilePartNodeRoot extends FilePartNode { } // returns start index of the name (i.e. path[return..length) is considered a name) - private static int extractName(@NotNull CharSequence path, int length) { - int i = StringUtil.lastIndexOf(path, '/', 0, length); + private static int extractName(@NotNull CharSequence path, int endOffset) { + int i = StringUtil.lastIndexOf(path, '/', 0, endOffset); if (i != -1 && PathUtilRt.isWindowsUNCRoot(path, i)) { // UNC return 0; diff --git a/platform/vfs-impl/src/com/intellij/openapi/vfs/impl/VirtualFilePointerManagerImpl.java b/platform/vfs-impl/src/com/intellij/openapi/vfs/impl/VirtualFilePointerManagerImpl.java index 70efb62acc40..0149cbfb7a03 100644 --- a/platform/vfs-impl/src/com/intellij/openapi/vfs/impl/VirtualFilePointerManagerImpl.java +++ b/platform/vfs-impl/src/com/intellij/openapi/vfs/impl/VirtualFilePointerManagerImpl.java @@ -223,8 +223,7 @@ public final class VirtualFilePointerManagerImpl extends VirtualFilePointerManag // if newly created path is the same as the one extracted from url then the url did not change, we can reuse it //noinspection StringEquality if (cleanPath != path) { - String fsSpecificPath = cleanPath + (fileSystem instanceof ArchiveFileSystem && !cleanPath.contains(JarFileSystem.JAR_SEPARATOR) ? JarFileSystem.JAR_SEPARATOR : ""); - url = VirtualFileManager.constructUrl(protocol, fsSpecificPath); + url = VirtualFileManager.constructUrl(protocol, cleanPath); path = cleanPath; } if (url.contains("..")) { @@ -237,7 +236,7 @@ public final class VirtualFilePointerManagerImpl extends VirtualFilePointerManag else { // when someone is crazy enough to create VFP for not-yet existing file from the path with "..", ignore all symlinks path = FileUtil.toCanonicalPath(path); - url = VirtualFileManager.constructUrl(protocol, path + (fileSystem instanceof ArchiveFileSystem ? JarFileSystem.JAR_SEPARATOR : "")); + url = VirtualFileManager.constructUrl(protocol, path); } } if (file == null && StringUtil.isEmptyOrSpaces(path)) { @@ -283,7 +282,7 @@ public final class VirtualFilePointerManagerImpl extends VirtualFilePointerManag if (next == '/' && i != 0 || next == '/' && !SystemInfo.isWindows ||// additional condition for Windows UNC - next == '/' && slash == 2 && path.charAt(1) == ':' && OSAgnosticPathUtil.isDriveLetter(path.charAt(0)) ||// Z://foo -> Z:/foo + next == '/' && slash == 2 && OSAgnosticPathUtil.startsWithWindowsDrive(path) || // Z://foo -> Z:/foo next == '.' && (slash == path.length()-2 || path.charAt(slash+2) == '/')) { return cleanupTail(path, slash); } @@ -342,19 +341,34 @@ public final class VirtualFilePointerManagerImpl extends VirtualFilePointerManag FilePartNodeRoot root = getRoot(fs); NodeToUpdate toUpdate; if (file == null) { - String normPath = fs instanceof ArchiveFileSystem && !path.contains(JarFileSystem.JAR_SEPARATOR) ? path + JarFileSystem.JAR_SEPARATOR : path; + String normPath = path; + if (fs instanceof ArchiveFileSystem) { + // check that "!/" separator is placed correctly in the url: + // "xx!/yyy" has jar separator; but "xx/!/yyy" doesn't: directory separator only + int index = -1; + do { + index = path.indexOf(JarFileSystem.JAR_SEPARATOR, index + 1); + } + while (index > 0 && path.charAt(index-1) == '/'); + if (index == -1 && !isArchiveInTheWindowsDiskRoot(path)) { + // treat url "jar://xx/x.jar" as "jar://xx/x.jar!/" + normPath = path + JarFileSystem.JAR_SEPARATOR; + } + } toUpdate = root.findOrCreateByPath(normPath, fs); } else { toUpdate = root.findOrCreateByFile(file); } FilePartNode node = toUpdate.node; - if (fs != node.myFS && url != null && (IS_UNDER_UNIT_TEST || IS_INTERNAL)) { - throw new IllegalArgumentException("Invalid url: '" + url + "'. " + - "Its protocol '" + VirtualFileManager.extractProtocol(url) + "' is from " + fsFromFile + - " but the path part points to " + node.myFS); + if (fs != node.myFS) { + if (url != null && (IS_UNDER_UNIT_TEST || IS_INTERNAL)) { + throw new IllegalArgumentException("Invalid url: '" + url + "'. " + + "Its protocol '" + VirtualFileManager.extractProtocol(url) + "' is from " + fsFromFile + + " but the path part points to " + node.myFS); + } + LOG.error("fs=" + fs + "; node.myFS=" + node.myFS+"; url="+url+"; file="+file+"; node="+node); } - assert fs == node.myFS : "fs=" + fs + "; node.myFS=" + node.myFS+"; url="+url+"; file="+file; VirtualFilePointerImpl pointer = node.getPointer(listener); if (pointer == null) { @@ -372,6 +386,21 @@ public final class VirtualFilePointerManagerImpl extends VirtualFilePointerManag return pointer; } + private static boolean isArchiveInTheWindowsDiskRoot(@NotNull String path) { + // special case: "C:!/foo" means the archive is in the disk root - we shouldn't treat it as relative path starting with "!" + // other special case: "C:/!/foo" means the archive is in the disk root - we shouldn't treat it as under the (local) directory "!" in the disk root + return OSAgnosticPathUtil.startsWithWindowsDrive(path) + && path.length() >= 4 + && (path.charAt(2) == '!' + && path.charAt(3) == '/' + || + path.length() >= 5 + && path.charAt(2) == '/' + && path.charAt(3) == '!' + && path.charAt(4) == '/' + ); + } + @Override public @NotNull VirtualFilePointer duplicate(@NotNull VirtualFilePointer pointer, @NotNull Disposable parent, @@ -530,7 +559,7 @@ public final class VirtualFilePointerManagerImpl extends VirtualFilePointerManag if (VirtualFile.PROP_NAME.equals(change.getPropertyName()) && !Comparing.equal(change.getOldValue(), change.getNewValue())) { VirtualFileSystemEntry eventFile = (VirtualFileSystemEntry)change.getFile(); VirtualFileSystemEntry parent = (VirtualFileSystemEntry)FilePartNode.getParentThroughJar(eventFile, eventFile.getFileSystem()); - // e.g. for LightVirtualFiles + // e.g., for LightVirtualFiles if (parent != null) { int newNameId = toNameId(change.getNewValue().toString()); addRelevantPointers(eventFile, parent, newNameId, toFirePointers, toUpdateNodes, true, fs, event); diff --git a/platform/vfs-impl/src/com/intellij/openapi/vfs/newvfs/VfsImplUtil.java b/platform/vfs-impl/src/com/intellij/openapi/vfs/newvfs/VfsImplUtil.java index 80bf679e13ac..b3be0fabdf91 100644 --- a/platform/vfs-impl/src/com/intellij/openapi/vfs/newvfs/VfsImplUtil.java +++ b/platform/vfs-impl/src/com/intellij/openapi/vfs/newvfs/VfsImplUtil.java @@ -152,7 +152,12 @@ public final class VfsImplUtil { return null; } - return pair(root, normalizedPath.substring(rootPath.length())); + int i = rootPath.length(); + if (i < normalizedPath.length() && normalizedPath.charAt(i) == '/') { + i++; + } + String relativePath = normalizedPath.substring(i); + return pair(root, relativePath); } public static void refresh(@NotNull NewVirtualFileSystem vfs, boolean asynchronous) { diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/actions/XNewWatchAction.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/actions/XNewWatchAction.java index 7f4e9fdd9cc3..43dfe1a1808d 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/actions/XNewWatchAction.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/actions/XNewWatchAction.java @@ -1,34 +1,36 @@ -/* - * Copyright 2000-2009 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. - */ +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.xdebugger.impl.frame.actions; import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.project.DumbAwareAction; +import com.intellij.xdebugger.XDebugSession; +import com.intellij.xdebugger.impl.XDebugSessionImpl; +import com.intellij.xdebugger.impl.frame.XVariablesViewBase; import com.intellij.xdebugger.impl.frame.XWatchesView; -import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree; +import com.intellij.xdebugger.impl.ui.DebuggerUIUtil; +import com.intellij.xdebugger.impl.ui.XDebugSessionTab; import com.intellij.xdebugger.impl.ui.tree.nodes.WatchesRootNode; import com.intellij.xdebugger.impl.ui.tree.nodes.XDebuggerTreeNode; import org.jetbrains.annotations.NotNull; -public class XNewWatchAction extends XWatchesTreeActionBase { +public class XNewWatchAction extends DumbAwareAction { @Override - protected void perform(@NotNull AnActionEvent e, @NotNull XDebuggerTree tree, @NotNull XWatchesView watchesView) { - XDebuggerTreeNode root = tree.getRoot(); - if (root instanceof WatchesRootNode) { - final WatchesRootNode watchesRoot = (WatchesRootNode)root; - watchesRoot.addNewWatch(); + public void actionPerformed(@NotNull AnActionEvent e) { + XWatchesView view = DebuggerUIUtil.getWatchesView(e); + if (view instanceof XVariablesViewBase) { + XDebuggerTreeNode root = ((XVariablesViewBase)view).getTree().getRoot(); + if (root instanceof WatchesRootNode) { + XDebugSession session = DebuggerUIUtil.getSession(e); + if (session instanceof XDebugSessionImpl) { + XDebugSessionTab.showWatchesView((XDebugSessionImpl)session); + } + ((WatchesRootNode)root).addNewWatch(); + } } } + + @Override + public void update(@NotNull AnActionEvent e) { + e.getPresentation().setEnabled(DebuggerUIUtil.getSession(e) != null && DebuggerUIUtil.getWatchesView(e) != null); + } } diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/DebuggerUIUtil.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/DebuggerUIUtil.java index e20b55765dc7..163deca2ead9 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/DebuggerUIUtil.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/DebuggerUIUtil.java @@ -4,7 +4,9 @@ package com.intellij.xdebugger.impl.ui; import com.intellij.codeInsight.hint.HintUtil; import com.intellij.ide.nls.NlsMessages; import com.intellij.openapi.Disposable; -import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.actionSystem.ActionManager; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.LogicalPosition; @@ -21,15 +23,14 @@ import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.openapi.wm.WindowManager; -import com.intellij.ui.*; +import com.intellij.ui.AppUIUtil; +import com.intellij.ui.ComponentUtil; +import com.intellij.ui.EditorTextField; +import com.intellij.ui.ScreenUtil; import com.intellij.ui.awt.RelativePoint; -import com.intellij.ui.components.AnActionLink; -import com.intellij.ui.components.JBLabel; import com.intellij.ui.popup.list.ListPopupImpl; import com.intellij.util.Consumer; -import com.intellij.util.ui.GridBag; import com.intellij.util.ui.JBUI; -import com.intellij.util.ui.UIUtil; import com.intellij.xdebugger.*; import com.intellij.xdebugger.breakpoints.XBreakpoint; import com.intellij.xdebugger.breakpoints.XBreakpointListener; @@ -464,6 +465,22 @@ public final class DebuggerUIUtil { }); } + @Nullable + public static XWatchesView getWatchesView(@NotNull AnActionEvent e) { + XWatchesView view = e.getData(XWatchesView.DATA_KEY); + Project project = e.getProject(); + if (view == null && project != null) { + XDebugSession session = getSession(e); + if (session != null) { + XDebugSessionTab tab = ((XDebugSessionImpl)session).getSessionTab(); + if (tab != null) { + return tab.getWatchesView(); + } + } + } + return view; + } + public static void registerActionOnComponent(String name, JComponent component, Disposable parentDisposable) { AnAction action = ActionManager.getInstance().getAction(name); action.registerCustomShortcutSet(action.getShortcutSet(), component, parentDisposable); diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/actions/XAddToWatchesTreeAction.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/actions/XAddToWatchesTreeAction.java index 2cfce6f629e2..acc6e0b322c0 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/actions/XAddToWatchesTreeAction.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/actions/XAddToWatchesTreeAction.java @@ -1,13 +1,9 @@ -// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.xdebugger.impl.ui.tree.actions; import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.project.Project; -import com.intellij.xdebugger.XDebugSession; -import com.intellij.xdebugger.impl.XDebugSessionImpl; import com.intellij.xdebugger.impl.frame.XWatchesView; import com.intellij.xdebugger.impl.ui.DebuggerUIUtil; -import com.intellij.xdebugger.impl.ui.XDebugSessionTab; import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; import org.jetbrains.annotations.NotNull; @@ -18,7 +14,7 @@ import org.jetbrains.annotations.NotNull; public class XAddToWatchesTreeAction extends XDebuggerTreeActionBase { @Override protected boolean isEnabled(@NotNull final XValueNodeImpl node, @NotNull AnActionEvent e) { - return super.isEnabled(node, e) && getWatchesView(e) != null; + return super.isEnabled(node, e) && DebuggerUIUtil.getWatchesView(e) != null; } @Override @@ -35,24 +31,9 @@ public class XAddToWatchesTreeAction extends XDebuggerTreeActionBase { @Override protected void perform(final XValueNodeImpl node, @NotNull final String nodeName, final AnActionEvent e) { - final XWatchesView watchesView = getWatchesView(e); + final XWatchesView watchesView = DebuggerUIUtil.getWatchesView(e); if (watchesView != null) { DebuggerUIUtil.addToWatches(watchesView, node); } } - - private static XWatchesView getWatchesView(@NotNull AnActionEvent e) { - XWatchesView view = e.getData(XWatchesView.DATA_KEY); - Project project = e.getProject(); - if (view == null && project != null) { - XDebugSession session = DebuggerUIUtil.getSession(e); - if (session != null) { - XDebugSessionTab tab = ((XDebugSessionImpl)session).getSessionTab(); - if (tab != null) { - return tab.getWatchesView(); - } - } - } - return view; - } }
\ No newline at end of file diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/resources/AutoCloseableResourceInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/resources/AutoCloseableResourceInspection.java index 31a9cd9414a5..2b733fc191c3 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/resources/AutoCloseableResourceInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/resources/AutoCloseableResourceInspection.java @@ -62,7 +62,9 @@ public class AutoCloseableResourceInspection extends ResourceInspection { "java.io.StringReader", "java.util.Formatter", "java.util.Scanner", - "org.springframework.context.ConfigurableApplicationContext"); + "org.springframework.context.ConfigurableApplicationContext", + "io.micronaut.context.ApplicationContext"); + protected final MethodMatcher myMethodMatcher; final List<String> ignoredTypes = new ArrayList<>(DEFAULT_IGNORED_TYPES); @SuppressWarnings("PublicField") diff --git a/plugins/coverage/src/com/intellij/coverage/JavaCoverageEngine.java b/plugins/coverage/src/com/intellij/coverage/JavaCoverageEngine.java index 2aebf9b181db..a7bf3c8a006f 100644 --- a/plugins/coverage/src/com/intellij/coverage/JavaCoverageEngine.java +++ b/plugins/coverage/src/com/intellij/coverage/JavaCoverageEngine.java @@ -53,11 +53,13 @@ import com.intellij.psi.util.PsiTreeUtil; import com.intellij.rt.coverage.data.JumpData; import com.intellij.rt.coverage.data.LineData; import com.intellij.rt.coverage.data.SwitchData; +import com.intellij.task.ProjectTaskManager; import com.intellij.testIntegration.TestFramework; import jetbrains.coverage.report.ReportGenerationFailedException; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.concurrency.Promise; import org.jetbrains.jps.model.java.JavaSourceRootType; import java.io.DataInputStream; @@ -326,8 +328,13 @@ public class JavaCoverageEngine extends CoverageEngine { @Override public boolean recompileProjectAndRerunAction(@NotNull final Module module, @NotNull final CoverageSuitesBundle suite, @NotNull final Runnable chooseSuiteAction) { - final VirtualFile outputpath = CompilerModuleExtension.getInstance(module).getCompilerOutputPath(); - final VirtualFile testOutputpath = CompilerModuleExtension.getInstance(module).getCompilerOutputPathForTests(); + CompilerModuleExtension compilerModuleExtension = CompilerModuleExtension.getInstance(module); + if (compilerModuleExtension == null) { + return false; + } + + final VirtualFile outputpath = compilerModuleExtension.getCompilerOutputPath(); + final VirtualFile testOutputpath = compilerModuleExtension.getCompilerOutputPathForTests(); if (outputpath == null && isModuleOutputNeeded(module, JavaSourceRootType.SOURCE) || suite.isTrackTestFolders() && testOutputpath == null && isModuleOutputNeeded(module, JavaSourceRootType.TEST_SOURCE)) { @@ -342,14 +349,12 @@ public class JavaCoverageEngine extends CoverageEngine { JavaCoverageBundle.message("coverage.hide.report"), Messages.getWarningIcon()); if (choice == Messages.OK) { - final CompilerManager compilerManager = CompilerManager.getInstance(project); - compilerManager.make(compilerManager.createProjectCompileScope(project), (aborted, errors, warnings, compileContext) -> { - if (aborted || errors != 0) return; - ApplicationManager.getApplication().invokeLater(() -> { - if (project.isDisposed()) return; - CoverageDataManager.getInstance(project).chooseSuitesBundle(suite); - }); - }); + ProjectTaskManager taskManager = ProjectTaskManager.getInstance(project); + Promise<ProjectTaskManager.Result> promise = taskManager.buildAllModules(); + promise.onSuccess(result -> ApplicationManager.getApplication().invokeLater(() -> { + CoverageDataManager.getInstance(project).chooseSuitesBundle(suite); + }, o -> project.isDisposed()) + ); } else if (!project.isDisposed()) { CoverageDataManager.getInstance(project).chooseSuitesBundle(null); } diff --git a/plugins/editorconfig/src/org/editorconfig/configmanagement/EditorConfigEncodingCache.java b/plugins/editorconfig/src/org/editorconfig/configmanagement/EditorConfigEncodingCache.java index 82b3bb84e6e7..161b7fa6aee8 100644 --- a/plugins/editorconfig/src/org/editorconfig/configmanagement/EditorConfigEncodingCache.java +++ b/plugins/editorconfig/src/org/editorconfig/configmanagement/EditorConfigEncodingCache.java @@ -138,7 +138,8 @@ public class EditorConfigEncodingCache implements PersistentStateComponent<Eleme @Nullable public Charset getCachedEncoding(@NotNull VirtualFile virtualFile) { - return ObjectUtils.doIfNotNull(getCachedCharsetData(virtualFile), CharsetData::getCharset); + CharsetData charsetData = getCachedCharsetData(virtualFile); + return charsetData != null && !charsetData.isIgnored ? charsetData.getCharset() : null; } @Nullable diff --git a/plugins/gradle/java/resources/META-INF/plugin.xml b/plugins/gradle/java/resources/META-INF/plugin.xml index 7e8c36e8761c..2eea7553b1c3 100644 --- a/plugins/gradle/java/resources/META-INF/plugin.xml +++ b/plugins/gradle/java/resources/META-INF/plugin.xml @@ -40,6 +40,7 @@ <frameworkSupport implementation="org.jetbrains.plugins.gradle.frameworkSupport.GradleJavaFrameworkSupportProvider"/> <kotlinDslFrameworkSupport implementation="org.jetbrains.plugins.gradle.frameworkSupport.KotlinDslGradleJavaFrameworkSupportProvider" /> <targetEnvironmentAware implementation="org.jetbrains.plugins.gradle.execution.target.GradleServerDebugAware" /> + <taskResultListener implementation="org.jetbrains.plugins.gradle.service.GradleProjectOutputsUpdater"/> </extensions> <extensions defaultExtensionNs="com.intellij"> @@ -95,5 +96,7 @@ <statistics.counterUsagesCollector implementationClass="com.intellij.openapi.externalSystem.statistics.ExternalSystemSyncActionsCollector"/> <registryKey key="gradle.execution.target.server.debug.port" defaultValue="-1" description="Specifies port at which Gradle target server process will wait for debugger connections. -1 means disabled feature."/> + <registryKey key="gradle.refresh.project.outputs" defaultValue="true" + description="After a Gradle task, do a shallow refresh of modules outputs in VFS"/> </extensions> </idea-plugin>
\ No newline at end of file diff --git a/plugins/gradle/java/src/config/GradlePositionManager.java b/plugins/gradle/java/src/config/GradlePositionManager.java index a1182a8b7307..efbee8117583 100644 --- a/plugins/gradle/java/src/config/GradlePositionManager.java +++ b/plugins/gradle/java/src/config/GradlePositionManager.java @@ -4,6 +4,7 @@ package org.jetbrains.plugins.gradle.config; import com.intellij.execution.wsl.WSLDistribution; import com.intellij.execution.wsl.WslDistributionManager; import com.intellij.execution.wsl.WslPath; +import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.project.Project; @@ -38,6 +39,8 @@ import org.jetbrains.plugins.groovy.runner.GroovyScriptUtil; import java.io.File; import java.net.URI; import java.net.URISyntaxException; +import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.regex.Pattern; @@ -99,6 +102,11 @@ public class GradlePositionManager extends ScriptPositionManagerHelper { return PsiManager.getInstance(project).findFile(virtualFile); } + @Override + public Collection<? extends FileType> getAcceptedFileTypes() { + return Collections.singleton(GradleFileType.INSTANCE); + } + private static String getLocalFilePath(@NotNull Project project, @NotNull String sourceFilePath) { // TODO add the support for other run targets mappings String projectBasePath = project.getBasePath(); diff --git a/plugins/gradle/java/src/service/GradleProjectOutputsUpdater.kt b/plugins/gradle/java/src/service/GradleProjectOutputsUpdater.kt new file mode 100644 index 000000000000..6353f13fd28f --- /dev/null +++ b/plugins/gradle/java/src/service/GradleProjectOutputsUpdater.kt @@ -0,0 +1,44 @@ +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package org.jetbrains.plugins.gradle.service + +import com.intellij.compiler.impl.CompilerUtil +import com.intellij.openapi.compiler.CompilerPaths +import com.intellij.openapi.diagnostic.logger +import com.intellij.openapi.externalSystem.model.ProjectKeys +import com.intellij.openapi.externalSystem.model.project.ModuleData +import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId +import com.intellij.openapi.externalSystem.service.project.IdeModelsProviderImpl +import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil +import com.intellij.openapi.module.Module +import com.intellij.openapi.util.registry.Registry +import com.intellij.util.containers.ContainerUtil +import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData +import org.jetbrains.plugins.gradle.service.task.GradleTaskResultListener +import org.jetbrains.plugins.gradle.util.GradleConstants + +class GradleProjectOutputsUpdater: GradleTaskResultListener { + override fun onSuccess(id: ExternalSystemTaskId, projectPath: String) { + if (!Registry.`is`("gradle.refresh.project.outputs")) return + + val ideaProject = id.findProject() + if (ideaProject == null) { + LOG.warn("Project path [$projectPath] does not belong to any open Gradle projects") + return + } + + val modelsProvider = IdeModelsProviderImpl(ideaProject) + val projectNode = ExternalSystemApiUtil.findProjectNode(ideaProject, GradleConstants.SYSTEM_ID, projectPath) ?: return + val moduleNodes = ExternalSystemApiUtil.findAll(projectNode, ProjectKeys.MODULE) + val affectedModules: List<Module> = moduleNodes.flatMap { ExternalSystemApiUtil.findAll(it, GradleSourceSetData.KEY) + it } + .map { it.data } + .filterIsInstance(ModuleData::class.java) + .mapNotNull { modelsProvider.findIdeModule(it) } + + val affectedRoots = ContainerUtil.newHashSet(*CompilerPaths.getOutputPaths(affectedModules.toTypedArray())) + CompilerUtil.refreshOutputRoots(affectedRoots) + } + + companion object { + private val LOG = logger<GradleProjectOutputsUpdater>() + } +}
\ No newline at end of file diff --git a/plugins/gradle/plugin-resources/META-INF/plugin.xml b/plugins/gradle/plugin-resources/META-INF/plugin.xml index b201d882de66..5d6437168fe9 100644 --- a/plugins/gradle/plugin-resources/META-INF/plugin.xml +++ b/plugins/gradle/plugin-resources/META-INF/plugin.xml @@ -42,6 +42,8 @@ dynamic="true"/> <extensionPoint qualifiedName="org.jetbrains.plugins.gradle.targetEnvironmentAware" interface="org.jetbrains.plugins.gradle.execution.target.GradleTargetEnvironmentAware" dynamic="true"/> + <extensionPoint qualifiedName= "org.jetbrains.plugins.gradle.taskResultListener" interface="org.jetbrains.plugins.gradle.service.task.GradleTaskResultListener" + dynamic="true"/> </extensionPoints> <extensions defaultExtensionNs="org.jetbrains.plugins.gradle"> diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/CommonGradleProjectResolverExtension.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/CommonGradleProjectResolverExtension.java index 2680635554b7..d24588261504 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/CommonGradleProjectResolverExtension.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/CommonGradleProjectResolverExtension.java @@ -5,6 +5,7 @@ import com.intellij.build.events.MessageEvent; import com.intellij.build.issue.BuildIssue; import com.intellij.openapi.application.ApplicationNamesInfo; import com.intellij.openapi.application.PathManager; +import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.externalSystem.debugger.DebuggerBackendExtension; import com.intellij.openapi.externalSystem.model.ConfigurationDataImpl; @@ -34,6 +35,7 @@ import com.intellij.util.Consumer; import com.intellij.util.LazyInitializer; import com.intellij.util.LazyInitializer.LazyValue; import com.intellij.util.ReflectionUtil; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.FileCollectionFactory; import com.intellij.util.containers.MultiMap; import com.intellij.util.execution.ParametersListUtil; @@ -586,7 +588,21 @@ public final class CommonGradleProjectResolverExtension extends AbstractProjectR @Nullable private static File getGradleOutputDir(@Nullable ExternalSourceDirectorySet sourceDirectorySet) { if (sourceDirectorySet == null) return null; - return sourceDirectorySet.getGradleOutputDirs().stream().findFirst().orElse(null); + String firstExistingLang = sourceDirectorySet.getSrcDirs().stream() + .filter(File::exists) + .findFirst() + .map(File::getName) + .orElse(null); + + if (firstExistingLang == null) { + return ContainerUtil.getFirstItem(sourceDirectorySet.getGradleOutputDirs()); + } + + return sourceDirectorySet.getGradleOutputDirs().stream() + .filter(f -> f.getPath().contains(firstExistingLang)) + .findFirst() + .orElse(ContainerUtil.getFirstItem(sourceDirectorySet.getGradleOutputDirs())); + } private static void excludeOutDir(@NotNull DataNode<ModuleData> ideModule, File ideaOutDir) { diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/task/GradleTaskManager.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/task/GradleTaskManager.java index d9faf3979d4b..bae1b9a7f784 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/task/GradleTaskManager.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/task/GradleTaskManager.java @@ -157,6 +157,7 @@ public class GradleTaskManager implements ExternalSystemTaskManager<GradleExecut launcher.withCancellationToken(cancellationTokenSource.token()); launcher.run(); } + GradleTaskResultListener.EP_NAME.forEachExtensionSafe(ext -> ext.onSuccess(id, projectPath)); } catch (RuntimeException e) { LOG.debug("Gradle build launcher error", e); diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/task/GradleTaskResultListener.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/task/GradleTaskResultListener.java new file mode 100644 index 000000000000..20b26d197f20 --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/task/GradleTaskResultListener.java @@ -0,0 +1,26 @@ +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package org.jetbrains.plugins.gradle.service.task; + +import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +/** + * Listens for Gradle tasks execution. + * <br> + * Internal extension point to support internal functionality. E.g., vfs updates for java-related projects + */ +@ApiStatus.Internal +public interface GradleTaskResultListener { + + ExtensionPointName<GradleTaskResultListener> EP_NAME = ExtensionPointName.create("org.jetbrains.plugins.gradle.taskResultListener"); + + /** + * Called right after successful execution of Gradle task, before returning to External System API + * + * @param id + * @param projectPath + */ + void onSuccess(@NotNull ExternalSystemTaskId id, @NotNull String projectPath); +} diff --git a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleDependenciesImportingTest.java b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleDependenciesImportingTest.java index dd3b4d7363c9..0fc5daac4f7f 100644 --- a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleDependenciesImportingTest.java +++ b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleDependenciesImportingTest.java @@ -1118,11 +1118,14 @@ public class GradleDependenciesImportingTest extends GradleImportingTestCase { "project.project1", "project.project1.main", "project.project1.test", "project.project2", "project.project2.main", "project.project2.test"); - assertModuleOutput("project.project1.main", getProjectPath() + "/project1/buildIdea/main", ""); - assertModuleOutput("project.project1.test", "", getProjectPath() + "/project1/buildIdea/test"); + String mainClassesOutputPath = isGradleNewerOrSameAs("4.0") ? "/build/classes/java/main" : "/build/classes/main"; + String testClassesOutputPath = isGradleNewerOrSameAs("4.0") ? "/build/classes/java/test" : "/build/classes/test"; - assertModuleOutput("project.project2.main", getProjectPath() + "/project2/buildIdea/main", ""); - assertModuleOutput("project.project2.test", "", getProjectPath() + "/project2/buildIdea/test"); + assertModuleOutput("project.project1.main", getProjectPath() + "/project1" + mainClassesOutputPath, ""); + assertModuleOutput("project.project1.test", "", getProjectPath() + "/project1" + testClassesOutputPath); + + assertModuleOutput("project.project2.main", getProjectPath() + "/project2" + mainClassesOutputPath, ""); + assertModuleOutput("project.project2.test", "", getProjectPath() + "/project2" + testClassesOutputPath); assertModuleModuleDeps("project.project2.main", ArrayUtilRt.EMPTY_STRING_ARRAY); assertModuleModuleDeps("project.project2.test", "project.project2.main", "project.project1.test"); diff --git a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleFoldersImportingTest.java b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleFoldersImportingTest.java index e920116bfcca..8701d1300db0 100644 --- a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleFoldersImportingTest.java +++ b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleFoldersImportingTest.java @@ -194,10 +194,18 @@ public class GradleFoldersImportingTest extends GradleImportingTestCase { assertDefaultGradleJavaProjectFolders("project"); - assertModuleOutput("project.main", getProjectPath() + "/build", ""); + String mainClassesOutputPath = isGradleNewerOrSameAs("4.0") ? "/build/classes/java/main" : "/build/classes/main"; + assertModuleOutput("project.main", getProjectPath() + mainClassesOutputPath, ""); String testClassesOutputPath = isGradleNewerOrSameAs("4.0") ? "/build/classes/java/test" : "/build/classes/test"; assertModuleOutput("project.test", "", getProjectPath() + testClassesOutputPath); + getCurrentExternalProjectSettings().setDelegatedBuild(false); + GradleSettings.getInstance(myProject).getPublisher().onBuildDelegationChange(false, getProjectPath()); + assertModuleOutput("project.main", getProjectPath() + "/build", ""); + assertModuleOutput("project.test", "", getProjectPath() + "/out/test/classes"); + + getCurrentExternalProjectSettings().setDelegatedBuild(true); + importProjectUsingSingeModulePerGradleProject(); assertModules("project"); assertContentRoots("project", getProjectPath()); diff --git a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/ExternalProjectBuilderImpl.groovy b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/ExternalProjectBuilderImpl.groovy index 72b2bca5d1a7..8d50211efe27 100644 --- a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/ExternalProjectBuilderImpl.groovy +++ b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/ExternalProjectBuilderImpl.groovy @@ -298,12 +298,6 @@ class ExternalProjectBuilderImpl extends AbstractModelBuilderService { ExternalSourceDirectorySet resourcesDirectorySet = new DefaultExternalSourceDirectorySet() resourcesDirectorySet.name = sourceSet.resources.name resourcesDirectorySet.srcDirs = sourceSet.resources.srcDirs - if (ideaPluginOutDir && SourceSet.MAIN_SOURCE_SET_NAME == sourceSet.name) { - resourcesDirectorySet.addGradleOutputDir(ideaPluginOutDir) - } - if (ideaPluginTestOutDir && SourceSet.TEST_SOURCE_SET_NAME == sourceSet.name) { - resourcesDirectorySet.addGradleOutputDir(ideaPluginTestOutDir) - } if (is4OrBetter) { if (sourceSet.output.resourcesDir) { resourcesDirectorySet.addGradleOutputDir(sourceSet.output.resourcesDir) @@ -331,12 +325,6 @@ class ExternalProjectBuilderImpl extends AbstractModelBuilderService { ExternalSourceDirectorySet javaDirectorySet = new DefaultExternalSourceDirectorySet() javaDirectorySet.name = sourceSet.allJava.name javaDirectorySet.srcDirs = sourceSet.allJava.srcDirs - if (ideaPluginOutDir && SourceSet.MAIN_SOURCE_SET_NAME == sourceSet.name) { - javaDirectorySet.addGradleOutputDir(ideaPluginOutDir) - } - if (ideaPluginTestOutDir && SourceSet.TEST_SOURCE_SET_NAME == sourceSet.name) { - javaDirectorySet.addGradleOutputDir(ideaPluginTestOutDir) - } if (is4OrBetter) { for (File outDir : sourceSet.output.classesDirs.files) { javaDirectorySet.addGradleOutputDir(outDir) diff --git a/plugins/grazie/src/test/kotlin/com/intellij/grazie/text/TextExtractionTest.java b/plugins/grazie/src/test/kotlin/com/intellij/grazie/text/TextExtractionTest.java index 242e48583228..f0facee7550b 100644 --- a/plugins/grazie/src/test/kotlin/com/intellij/grazie/text/TextExtractionTest.java +++ b/plugins/grazie/src/test/kotlin/com/intellij/grazie/text/TextExtractionTest.java @@ -213,7 +213,25 @@ public class TextExtractionTest extends BasePlatformTestCase { myFixture.type(' '); PsiDocumentManager.getInstance(getProject()).commitAllDocuments(); // drop file caches }) - .usesAllCPUCores() + .assertTiming(); + } + + public void testLargeXmlWithUnclosedDoctypePerformance() { + String text = "<!DOCTYPE rules [\n<!ENTITY some \"x\">\n<rules> " + + IntStreamEx.range(0, 10_000).mapToObj(i -> "<tag> content" + i + "</tag>\n").joining() + + " </rules>"; + PsiFile file = myFixture.configureByText("a.xml", text); + + PlatformTestUtil + .startPerformanceTest("text extraction", 1_500, () -> { + for (PsiElement element : SyntaxTraverser.psiTraverser(file)) { + TextExtractor.findTextsAt(element, TextContent.TextDomain.ALL); + } + }) + .setup(() -> { + myFixture.type(' '); + PsiDocumentManager.getInstance(getProject()).commitAllDocuments(); // drop file caches + }) .assertTiming(); } diff --git a/plugins/grazie/src/test/testData/ide/language/properties/Example.properties b/plugins/grazie/src/test/testData/ide/language/properties/Example.properties index b0b1151f01c7..18204c77a6ce 100644 --- a/plugins/grazie/src/test/testData/ide/language/properties/Example.properties +++ b/plugins/grazie/src/test/testData/ide/language/properties/Example.properties @@ -12,6 +12,7 @@ ru.text.err3=Это случилось <warning descr="INVALID_DATE">31 нояб ru.text.err4=За весь вечер она <warning descr="ne_proronila_ni">не проронила и слово</warning>. ru.text.err5=Собрание состоится в <warning descr="RU_COMPOUNDS">конференц зале</warning>. ru.text.err6=<warning descr="WORD_REPEAT_RULE">Он он</warning> здесь ошибка в тексте. +ru.with.newline=Не удалось авторизоваться.\nПопробуйте ещё раз. de.text.err1=Er überprüfte die Rechnungen noch <TYPO descr="Typo: In word 'einal'">einal</TYPO>, um ganz <warning descr="COMPOUND_INFINITIV_RULE">sicher zu gehen</warning>. de.text.err2=das ist <warning descr="FUEHR_FUER">führ</warning> Dich! diff --git a/plugins/grazie/src/test/testData/ide/language/xml/Example.xml b/plugins/grazie/src/test/testData/ide/language/xml/Example.xml index 7b490a394c16..080f030fc091 100644 --- a/plugins/grazie/src/test/testData/ide/language/xml/Example.xml +++ b/plugins/grazie/src/test/testData/ide/language/xml/Example.xml @@ -6,4 +6,5 @@ <name>First sentence. <warning descr="PRP_THE">it</warning> <warning descr="EN_A_VS_AN">an</warning> friend. Last sentence.</name> <address>Some street 23</address> </shipto> + <russian_with_newline>Не удалось авторизоваться.\nПопробуйте ещё раз. И <warning descr="Sklonenije_NUM_NN">пять карандаша</warning>.</russian_with_newline> </shiporder> diff --git a/plugins/grazie/xml/main/kotlin/com/intellij/grazie/ide/language/xml/XmlProblemFilter.java b/plugins/grazie/xml/main/kotlin/com/intellij/grazie/ide/language/xml/XmlProblemFilter.java index e3ca374cf784..a9eb8ebbc734 100644 --- a/plugins/grazie/xml/main/kotlin/com/intellij/grazie/ide/language/xml/XmlProblemFilter.java +++ b/plugins/grazie/xml/main/kotlin/com/intellij/grazie/ide/language/xml/XmlProblemFilter.java @@ -1,15 +1,25 @@ package com.intellij.grazie.ide.language.xml; import com.intellij.grazie.text.ProblemFilter; +import com.intellij.grazie.text.TextContent; import com.intellij.grazie.text.TextProblem; +import com.intellij.openapi.util.text.Strings; import org.jetbrains.annotations.NotNull; class XmlProblemFilter extends ProblemFilter { @Override public boolean shouldIgnore(@NotNull TextProblem problem) { + if (isAfterPossibleEscape(problem)) return true; + // This HTML (<b>text</b>) is currently split into 3 parts, and the bracket-pairing rule produces false positives. // This can be removed when we implement smarter tag content merging. String id = problem.getRule().getGlobalId(); return id.startsWith("LanguageTool.") && id.endsWith("UNPAIRED_BRACKETS"); } + + private static boolean isAfterPossibleEscape(@NotNull TextProblem problem) { + TextContent text = problem.getText(); + return problem.getHighlightRanges().stream() + .anyMatch(r -> Strings.contains(text, Math.max(0, r.getStartOffset() - 1), r.getEndOffset(), '\\')); + } } diff --git a/plugins/grazie/xml/main/kotlin/com/intellij/grazie/ide/language/xml/XmlTextExtractor.java b/plugins/grazie/xml/main/kotlin/com/intellij/grazie/ide/language/xml/XmlTextExtractor.java index 20312118d8d0..78e3552fc3de 100644 --- a/plugins/grazie/xml/main/kotlin/com/intellij/grazie/ide/language/xml/XmlTextExtractor.java +++ b/plugins/grazie/xml/main/kotlin/com/intellij/grazie/ide/language/xml/XmlTextExtractor.java @@ -24,9 +24,7 @@ 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.psi.xml.XmlDocument; -import com.intellij.psi.xml.XmlTag; -import com.intellij.psi.xml.XmlTokenType; +import com.intellij.psi.xml.*; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -51,8 +49,7 @@ public class XmlTextExtractor extends TextExtractor { @Override protected @Nullable TextContent buildTextContent(@NotNull PsiElement element, @NotNull Set<TextContent.TextDomain> allowedDomains) { - IElementType type = PsiUtilCore.getElementType(element); - if (isText(type) && hasSuitableDialect(element)) { + if (isText(element) && hasSuitableDialect(element)) { var classifier = tagClassifier(element); PsiElement container = SyntaxTraverser.psiApi().parents(element) .find(e -> e instanceof XmlDocument || e instanceof XmlTag && classifier.apply((XmlTag)e) != TagKind.Inline); @@ -63,6 +60,7 @@ public class XmlTextExtractor extends TextExtractor { } } + IElementType type = PsiUtilCore.getElementType(element); if (type == XmlTokenType.XML_COMMENT_CHARACTERS && allowedDomains.contains(COMMENTS) && hasSuitableDialect(element)) { return builder.build(element, COMMENTS); } @@ -105,7 +103,7 @@ public class XmlTextExtractor extends TextExtractor { unknownBefore = true; } - if (isText(PsiUtilCore.getElementType(each))) { + if (isText(each)) { group.add(each); } super.visitElement(each); @@ -138,7 +136,15 @@ public class XmlTextExtractor extends TextExtractor { return full.excludeRange(new TextRange(range.getEndOffset(), full.length())).excludeRange(new TextRange(0, range.getStartOffset())); } - private static boolean isText(IElementType type) { + private static boolean isText(PsiElement leaf) { + PsiElement parent = leaf.getParent(); + if (!(parent instanceof XmlText) && + !(PsiUtilCore.getElementType(parent) == XmlElementType.XML_CDATA && parent.getParent() instanceof XmlText) && + !(parent instanceof XmlDocument)) { + return false; + } + + IElementType type = PsiUtilCore.getElementType(leaf); return type == XmlTokenType.XML_WHITE_SPACE || type == TokenType.WHITE_SPACE || type == XmlTokenType.XML_CHAR_ENTITY_REF || type == XmlTokenType.XML_DATA_CHARACTERS; } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/confusing/GroovyPointlessBooleanInspection.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/confusing/GroovyPointlessBooleanInspection.java index 040426940cb6..8dbd95f1311b 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/confusing/GroovyPointlessBooleanInspection.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/codeInspection/confusing/GroovyPointlessBooleanInspection.java @@ -33,7 +33,6 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrBinary import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrUnaryExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrLiteral; -import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; import org.jetbrains.plugins.groovy.lang.psi.impl.utils.ComparisonUtils; import org.jetbrains.plugins.groovy.lang.psi.impl.utils.ParenthesesUtils; @@ -269,14 +268,13 @@ public class GroovyPointlessBooleanInspection extends BaseInspection { private static boolean equalityExpressionIsPointless(GrExpression lhs, GrExpression rhs) { - return (isTrue(lhs) || isFalse(lhs)) && isBoolean(rhs) - || (isTrue(rhs) || isFalse(rhs)) && isBoolean(lhs); + return ((isTrue(lhs) || isFalse(lhs)) && isBoolean(rhs)) || + ((isTrue(rhs) || isFalse(rhs)) && isBoolean(lhs)); } private static boolean isBoolean(GrExpression expression) { final PsiType type = expression.getType(); - final PsiType unboxed = TypesUtil.unboxPrimitiveTypeWrapper(type); - return unboxed != null && PsiType.BOOLEAN.equals(unboxed); + return PsiType.BOOLEAN.equals(type); } private static boolean andExpressionIsPointless(GrExpression lhs, diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/typing/DefaultMethodCallTypeCalculator.kt b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/typing/DefaultMethodCallTypeCalculator.kt index 97a79993b5da..d0a4ae5cb1ee 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/typing/DefaultMethodCallTypeCalculator.kt +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/typing/DefaultMethodCallTypeCalculator.kt @@ -1,14 +1,17 @@ -// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.jetbrains.plugins.groovy.lang.typing import com.intellij.openapi.extensions.ExtensionPointName import com.intellij.openapi.util.registry.Registry import com.intellij.psi.* +import com.intellij.util.castSafelyTo +import org.jetbrains.plugins.groovy.lang.psi.GroovyElementTypes import org.jetbrains.plugins.groovy.lang.psi.api.GroovyMethodResult import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrAccessorMethod import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod import org.jetbrains.plugins.groovy.lang.psi.impl.GrLiteralClassType @@ -30,7 +33,7 @@ class DefaultMethodCallTypeCalculator : GrTypeCalculator<GrMethodCall> { for (result in results) { type = TypesUtil.getLeastUpperBoundNullable(type, getTypeFromResult(result, arguments, expression), expression.manager) } - return type + return type?.boxIfNecessary(expression) } } @@ -80,6 +83,17 @@ fun PsiType?.devoid(context: PsiElement): PsiType? { return if (this == PsiType.VOID && !isCompileStatic(context)) PsiType.NULL else this } +private fun PsiType.boxIfNecessary(call: GrMethodCall) : PsiType { + if (this !is PsiPrimitiveType) { + return this + } + return if (call.invokedExpression.castSafelyTo<GrReferenceExpression>()?.dotTokenType == GroovyElementTypes.T_SAFE_DOT) { + this.box(call) + } else { + this + } +} + private fun hasGenerics(type: PsiType): Boolean { return !Registry.`is`("groovy.return.type.optimization") || type.accept(GenericsSearcher) == true } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/debugger/GroovyPositionManager.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/debugger/GroovyPositionManager.java index a472d02c717b..4544cf9ccb02 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/debugger/GroovyPositionManager.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/debugger/GroovyPositionManager.java @@ -16,6 +16,8 @@ import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.fileTypes.FileTypeRegistry; +import com.intellij.openapi.fileTypes.LanguageFileType; +import com.intellij.openapi.fileTypes.UnknownFileType; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.impl.scopes.ModuleWithDependenciesScope; import com.intellij.openapi.progress.ProcessCanceledException; @@ -38,6 +40,7 @@ import com.sun.jdi.request.ClassPrepareRequest; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.GroovyFileType; +import org.jetbrains.plugins.groovy.GroovyLanguage; import org.jetbrains.plugins.groovy.extensions.debugger.ScriptPositionManagerHelper; import org.jetbrains.plugins.groovy.lang.psi.GroovyFile; import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase; @@ -49,10 +52,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefini import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; import org.jetbrains.plugins.groovy.lang.stubs.GroovyShortNamesCache; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Set; +import java.util.*; public class GroovyPositionManager extends PositionManagerEx { private static final Logger LOG = Logger.getInstance(PositionManagerImpl.class); @@ -98,27 +98,31 @@ public class GroovyPositionManager extends PositionManagerEx { @Override public @Nullable XStackFrame createStackFrame(@NotNull StackFrameDescriptorImpl descriptor) { - if (!isInGroovyFile(descriptor)) { + if (isInGroovyFile(descriptor.getLocation()) != ThreeState.YES) { return null; } return new GroovyStackFrame(descriptor, true); } - private static boolean isInGroovyFile(StackFrameDescriptorImpl descriptor) { - Location location = descriptor.getLocation(); + private static ThreeState isInGroovyFile(@Nullable Location location) { if (location != null) { var refType = location.declaringType(); try { String safeName = refType.sourceName(); FileType fileType = FileTypeRegistry.getInstance().getFileTypeByFileName(safeName); - if (fileType != GroovyFileType.GROOVY_FILE_TYPE) { - return false; + if (fileType == UnknownFileType.INSTANCE) { + return ThreeState.UNSURE; } - } catch (AbsentInformationException e) { - return false; + if (fileType instanceof LanguageFileType) { + LanguageFileType languageFileType = (LanguageFileType)fileType; + if (languageFileType.getLanguage() == GroovyLanguage.INSTANCE) { + return ThreeState.YES; + } + } + } catch (AbsentInformationException ignore) { } } - return true; + return ThreeState.NO; } @Nullable @@ -285,6 +289,10 @@ public class GroovyPositionManager extends PositionManagerEx { @Override public SourcePosition getSourcePosition(@Nullable final Location location) throws NoDataException { if (location == null) throw NoDataException.INSTANCE; + if (isInGroovyFile(location) == ThreeState.NO) throw NoDataException.INSTANCE; + + int lineNumber = calcLineIndex(location); + if (lineNumber < 0) throw NoDataException.INSTANCE; if (LOG.isDebugEnabled()) { LOG.debug("getSourcePosition: " + location); @@ -292,8 +300,6 @@ public class GroovyPositionManager extends PositionManagerEx { PsiFile psiFile = getPsiFileByLocation(getDebugProcess().getProject(), location); if (psiFile == null) throw NoDataException.INSTANCE; - int lineNumber = calcLineIndex(location); - if (lineNumber < 0) throw NoDataException.INSTANCE; return SourcePosition.createFromLine(psiFile, lineNumber); } @@ -479,6 +485,9 @@ public class GroovyPositionManager extends PositionManagerEx { @NotNull @Override public Set<? extends FileType> getAcceptedFileTypes() { - return ourFileTypes; + var result = new HashSet<FileType>(); + ScriptPositionManagerHelper.EP_NAME.forEachExtensionSafe(ext -> result.addAll(ext.getAcceptedFileTypes())); + result.addAll(ourFileTypes); + return result; } }
\ No newline at end of file diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/extensions/debugger/ScriptPositionManagerHelper.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/extensions/debugger/ScriptPositionManagerHelper.java index ea5d224a7191..ff5a46a40c39 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/extensions/debugger/ScriptPositionManagerHelper.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/extensions/debugger/ScriptPositionManagerHelper.java @@ -16,6 +16,7 @@ package org.jetbrains.plugins.groovy.extensions.debugger; import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiFile; @@ -25,6 +26,9 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.psi.GroovyFile; +import java.util.Collection; +import java.util.Collections; + /** * Class to extend debugger functionality to handle various Groovy scripts * @@ -83,4 +87,13 @@ public abstract class ScriptPositionManagerHelper { public String customizeClassName(@NotNull PsiClass psiClass) { return null; } + + /** + * If a position manager works with scripts of FileType other, + * then {@link org.jetbrains.plugins.groovy.GroovyFileType#GROOVY_FILE_TYPE}, it should report them here. + * @return file types supported by this position manager helper + */ + public Collection<? extends FileType> getAcceptedFileTypes() { + return Collections.emptySet(); + } } diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/codeInspection/confusing/GroovyPointlessBooleanInspectionTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/codeInspection/confusing/GroovyPointlessBooleanInspectionTest.groovy index 331a8483dfb2..e1f137646974 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/codeInspection/confusing/GroovyPointlessBooleanInspectionTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/codeInspection/confusing/GroovyPointlessBooleanInspectionTest.groovy @@ -1,4 +1,4 @@ -// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.jetbrains.plugins.groovy.codeInspection.confusing import com.intellij.codeInspection.LocalInspectionTool @@ -34,6 +34,11 @@ class GroovyPointlessBooleanInspectionTest extends GroovyLatestTest implements H doTest($/!<caret><warning descr="Redundant boolean operations">!!true</warning>/$, '!true') } + @Test + void 'non-primitive type'() { + highlightingTest('Boolean x = null; x == true') + } + private void doTest(String before, String after) { highlightingTest before doActionTest('Simplify', after) diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/TypeInferenceTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/TypeInferenceTest.groovy index 1faa3505aa76..0c779c1e4839 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/TypeInferenceTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/resolve/TypeInferenceTest.groovy @@ -2003,4 +2003,11 @@ def foo() { } """, "java.util.Collection<java.lang.Integer>" } + + void 'test boxing on nullable receiver'() { + doTest """ +def xx = ""?.length() +x<caret>x +""", "java.lang.Integer" + } } diff --git a/plugins/ide-features-trainer/src/training/util/Utils.kt b/plugins/ide-features-trainer/src/training/util/Utils.kt index 085bbb866bfd..98b45c3e9ba0 100644 --- a/plugins/ide-features-trainer/src/training/util/Utils.kt +++ b/plugins/ide-features-trainer/src/training/util/Utils.kt @@ -175,7 +175,7 @@ fun scaledRigid(width: Int, height: Int): Component { internal fun getLearnToolWindowForProject(project: Project): LearnToolWindow? { val toolWindow = ToolWindowManager.getInstance(project).getToolWindow(LearnToolWindowFactory.LEARN_TOOL_WINDOW) ?: return null - val jComponent = toolWindow.contentManager.contents.singleOrNull()?.component + val jComponent = toolWindow.contentManagerIfCreated?.contents?.singleOrNull()?.component return jComponent as? LearnToolWindow } diff --git a/python/helpers/pydev/_pydevd_bundle/pydevd_console_output.py b/python/helpers/pydev/_pydevd_bundle/pydevd_console_output.py index f26f1175ade4..3503e8ca2dad 100644 --- a/python/helpers/pydev/_pydevd_bundle/pydevd_console_output.py +++ b/python/helpers/pydev/_pydevd_bundle/pydevd_console_output.py @@ -1,5 +1,6 @@ WRITE_OUT = 1 WRITE_ERR = 2 +MAX_STRING_LENGTH = 1000 class ConsoleOutputHook: @@ -9,12 +10,10 @@ class ConsoleOutputHook: self.is_error = is_stderr def write(self, str): - if self.is_error: - cmd = self.dbg.cmd_factory.make_io_message(str, WRITE_ERR) + if len(str) > MAX_STRING_LENGTH: + self._write_long_string(str) else: - cmd = self.dbg.cmd_factory.make_io_message(str, WRITE_OUT) - - self.dbg.writer.add_command(cmd) + self._write_short_string(str) def flush(self): pass @@ -24,3 +23,16 @@ class ConsoleOutputHook: if hasattr(self.original_out, item): return getattr(self.original_out, item) raise AttributeError("%s has no attribute %s" % (self.original_out, item)) + + def _write_long_string(self, str): + str_len, chunk_size = len(str), len(str) // MAX_STRING_LENGTH + for i in range(0, str_len, chunk_size): + self._write_short_string(str[i:i + chunk_size]) + + def _write_short_string(self, str): + if self.is_error: + cmd = self.dbg.cmd_factory.make_io_message(str, WRITE_ERR) + else: + cmd = self.dbg.cmd_factory.make_io_message(str, WRITE_OUT) + + self.dbg.writer.add_command(cmd) diff --git a/python/ideCoreSrc/idea/PyCharmCoreApplicationInfo.xml b/python/ideCoreSrc/idea/PyCharmCoreApplicationInfo.xml index d268cd2f56ff..404b5c2b7cb7 100644 --- a/python/ideCoreSrc/idea/PyCharmCoreApplicationInfo.xml +++ b/python/ideCoreSrc/idea/PyCharmCoreApplicationInfo.xml @@ -2,7 +2,7 @@ <component xmlns="http://jetbrains.org/intellij/schema/application-info" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jetbrains.org/intellij/schema/application-info http://jetbrains.org/intellij/schema/ApplicationInfo.xsd"> - <version major="2022" minor="1.2" eap="false"/> + <version major="2022" minor="1.3" suffix="RC" eap="false"/> <company name="JetBrains s.r.o." url="https://www.jetbrains.com" copyrightStart="2010"/> <build number="PC-__BUILD__" date="__BUILD_DATE__" majorReleaseDate="20220413"/> <logo url="/pycharm_core_logo.png" textcolor="ffffff" progressColor="ffffff" progressY="396" progressHeight="4"/> diff --git a/python/src/com/jetbrains/python/sdk/PythonSdkUpdater.java b/python/src/com/jetbrains/python/sdk/PythonSdkUpdater.java index d80d7e4c1cef..1bfc5a629864 100644 --- a/python/src/com/jetbrains/python/sdk/PythonSdkUpdater.java +++ b/python/src/com/jetbrains/python/sdk/PythonSdkUpdater.java @@ -447,7 +447,10 @@ public class PythonSdkUpdater implements StartupActivity.Background { final var pathsToTransfer = new HashSet<VirtualFile>(); pathsToTransfer.addAll(sdkRoots.second); pathsToTransfer.addAll(userAddedRoots.second); - pathsToTransfer.removeAll(moduleRoots); + // Presumably source and content roots that were configured manually by user, not set up automatically as "transferred" + HashSet<VirtualFile> nonTransferredModuleRoots = new HashSet<>(moduleRoots); + nonTransferredModuleRoots.removeAll(PyTransferredSdkRootsKt.getPathsToTransfer(sdk)); + pathsToTransfer.removeAll(nonTransferredModuleRoots); /* Don't run actions related to transferred roots on editable sdks since they can share data with original ones. diff --git a/python/testSrc/com/jetbrains/python/sdk/PySdkPathsTest.kt b/python/testSrc/com/jetbrains/python/sdk/PySdkPathsTest.kt index d1c808cf4173..6ba5053bea30 100644 --- a/python/testSrc/com/jetbrains/python/sdk/PySdkPathsTest.kt +++ b/python/testSrc/com/jetbrains/python/sdk/PySdkPathsTest.kt @@ -239,7 +239,10 @@ class PySdkPathsTest { mockPythonPluginDisposable() updateSdkPaths(sdk) + checkRoots(sdk, module, listOf(moduleRoot, entryPath), emptyList()) + // Subsequent updates should keep already set up source roots + updateSdkPaths(sdk) checkRoots(sdk, module, listOf(moduleRoot, entryPath), emptyList()) val simpleSdk = PythonMockSdk.create().also { diff --git a/spellchecker/src/com/intellij/spellchecker/inspections/IdentifierSplitter.java b/spellchecker/src/com/intellij/spellchecker/inspections/IdentifierSplitter.java index 9a393f0a1c44..90a855240496 100644 --- a/spellchecker/src/com/intellij/spellchecker/inspections/IdentifierSplitter.java +++ b/spellchecker/src/com/intellij/spellchecker/inspections/IdentifierSplitter.java @@ -118,6 +118,7 @@ public class IdentifierSplitter extends BaseSplitter { type == Character.TITLECASE_LETTER || type == Character.OTHER_LETTER || type == Character.MODIFIER_LETTER || + type == Character.NON_SPACING_MARK || type == Character.OTHER_PUNCTUATION ) { //letter diff --git a/spellchecker/src/com/intellij/spellchecker/inspections/TextSplitter.java b/spellchecker/src/com/intellij/spellchecker/inspections/TextSplitter.java index 092fc790e2f6..5c0554566931 100644 --- a/spellchecker/src/com/intellij/spellchecker/inspections/TextSplitter.java +++ b/spellchecker/src/com/intellij/spellchecker/inspections/TextSplitter.java @@ -33,8 +33,17 @@ public class TextSplitter extends BaseSplitter { return INSTANCE; } - private static final Pattern EXTENDED_WORD_AND_SPECIAL = Pattern.compile("(&[^;]+;)|(([#]|0x[0-9]*)?\\p{L}+'?\\p{L}[_\\p{L}]*)"); - + private static final String letter = "(\\p{L}\\p{Mn}*)"; + private static final String xmlEntity = "(&.+?;)"; + // using possessive quantifiers ++ and *+ to avoid SOE on large inputs + // see https://blog.sonarsource.com/crafting-regexes-to-avoid-stack-overflows/ + private static final Pattern EXTENDED_WORD_AND_SPECIAL = Pattern.compile( + xmlEntity + "|" + + "(#|0x\\d*)?" + // an optional prefix + letter + "++" + // some letters + "('" + letter + ")?" + // if there's an apostrophe, it should be followed by a letter + "(_|" + letter + ")*+" // more letters and underscores + ); @Override public void split(@Nullable String text, @NotNull TextRange range, Consumer<TextRange> consumer) { if (text == null || StringUtil.isEmpty(text)) { diff --git a/spellchecker/testData/inspection/commentsWithMistakes/test.txt b/spellchecker/testData/inspection/commentsWithMistakes/test.txt index 55589a76654d..ddec30627e4a 100644 --- a/spellchecker/testData/inspection/commentsWithMistakes/test.txt +++ b/spellchecker/testData/inspection/commentsWithMistakes/test.txt @@ -2,3 +2,4 @@ simple <TYPO descr="Typo: In word 'ttest'">ttest</TYPO> file (just plain text) русский (например) без словаря не проверять!!! data && !changed; else data && !<TYPO descr="Typo: In word 'changsed'">changsed</TYPO>; else +YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFh<TYPO descr="Typo: In word 'YWEK'">YWEK</TYPO>
\ No newline at end of file diff --git a/spellchecker/testSrc/com/intellij/spellchecker/inspector/SplitterTest.java b/spellchecker/testSrc/com/intellij/spellchecker/inspector/SplitterTest.java index 62fb0fb82d38..30a6b97793d9 100644 --- a/spellchecker/testSrc/com/intellij/spellchecker/inspector/SplitterTest.java +++ b/spellchecker/testSrc/com/intellij/spellchecker/inspector/SplitterTest.java @@ -139,6 +139,12 @@ public class SplitterTest { } @Test + public void testUnicodeCombiningChars() { + correctListToCheck(PlainTextSplitter.getInstance(), "бо́льшую", "бо́льшую"); + correctListToCheck(PlainTextSplitter.getInstance(), "dafür", "dafür"); + } + + @Test public void testConstantName() { String text = "TEST_CONSTANT"; correctListToCheck(IdentifierSplitter.getInstance(), text, "TEST", "CONSTANT"); diff --git a/xml/dom-openapi/src/com/intellij/util/xml/ui/UndoHelper.java b/xml/dom-openapi/src/com/intellij/util/xml/ui/UndoHelper.java index ed10f84682fe..dda6b55894ae 100644 --- a/xml/dom-openapi/src/com/intellij/util/xml/ui/UndoHelper.java +++ b/xml/dom-openapi/src/com/intellij/util/xml/ui/UndoHelper.java @@ -61,13 +61,13 @@ public class UndoHelper { }); } - final void startListeningDocuments() { + public final void startListeningDocuments() { for (final Document document : myCurrentDocuments) { document.addDocumentListener(myDocumentAdapter); } } - final void stopListeningDocuments() { + public final void stopListeningDocuments() { for (final Document document : myCurrentDocuments) { document.removeDocumentListener(myDocumentAdapter); } @@ -87,7 +87,7 @@ public class UndoHelper { startListeningDocuments(); } - final void removeWatchedDocument(@NotNull Document document) { + public final void removeWatchedDocument(@NotNull Document document) { stopListeningDocuments(); myCurrentDocuments.remove(document); startListeningDocuments(); |