diff options
author | Dana Dahlstrom <dahlstrom@google.com> | 2022-07-25 15:00:00 -0700 |
---|---|---|
committer | Dana Dahlstrom <dahlstrom@google.com> | 2022-08-08 08:00:00 -0700 |
commit | 23cd6161724179c75add0b4e69a543275c41433a (patch) | |
tree | 77c480904f0c092a2388cd4f8f5b2e2f95d85261 | |
parent | 441e3cf8013b8cb185db20018f694a7c3aa69115 (diff) | |
parent | 174bb89c93c5eff6c743ccc61ebb93ccb8037cd7 (diff) | |
download | idea-23cd6161724179c75add0b4e69a543275c41433a.tar.gz |
Merge IntelliJ IDEA 2022.1.4 221.6008.13
Change-Id: I174bb89c93c5eff6c743ccc61ebb93ccb8037cd7
64 files changed, 588 insertions, 358 deletions
diff --git a/.gitignore b/.gitignore index 5e640a6eff50..7a81c1e3c8de 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,5 @@ edu/dependencies/.gradle edu/dependencies/build native/**/build/ stale_outputs_checked -/android
\ No newline at end of file +/android +/tools/ideTestingFramework/intellij.tools.ide.starter
\ No newline at end of file diff --git a/bin/mac/libmacscreenmenu64.dylib b/bin/mac/libmacscreenmenu64.dylib Binary files differindex 8aabfd7209f6..dfd9b142573f 100755 --- a/bin/mac/libmacscreenmenu64.dylib +++ b/bin/mac/libmacscreenmenu64.dylib diff --git a/build.txt b/build.txt index 66d5774b2fdb..80f4908c6d38 100644 --- a/build.txt +++ b/build.txt @@ -1 +1 @@ -221.5921.22.2211.SNAPSHOT +221.6008.13.2211.SNAPSHOT diff --git a/community-resources/src/idea/IdeaApplicationInfo.xml b/community-resources/src/idea/IdeaApplicationInfo.xml index 6db949992468..80843aa9e787 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.3"/> + <version major="2022" minor="1.4"/> <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/java-tests/testSrc/com/intellij/util/indexing/IndexDiagnosticTest.kt b/java/java-tests/testSrc/com/intellij/util/indexing/IndexDiagnosticTest.kt index cbda9b713438..8bdfcbcbaceb 100644 --- a/java/java-tests/testSrc/com/intellij/util/indexing/IndexDiagnosticTest.kt +++ b/java/java-tests/testSrc/com/intellij/util/indexing/IndexDiagnosticTest.kt @@ -10,7 +10,6 @@ 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 @@ -74,7 +73,7 @@ class IndexDiagnosticTest : JavaCodeInsightFixtureTestCase() { projectName = "projectName", times = JsonProjectIndexingHistoryTimes( "reason", - ScanningType.PARTIAL, + false, JsonDuration(123), JsonDuration(456), JsonDuration(789), diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaMatchingVisitor.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaMatchingVisitor.java index c03ae151b04d..7fb108f7066a 100644 --- a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaMatchingVisitor.java +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaMatchingVisitor.java @@ -1195,8 +1195,14 @@ public class JavaMatchingVisitor extends JavaElementVisitor { final Object value1 = expression.getValue(); final Object value2 = other.getValue(); if ((value1 instanceof String || value1 instanceof Character) && (value2 instanceof String || value2 instanceof Character)) { - myMatchingVisitor.setResult(myMatchingVisitor.matchText(StructuralSearchUtil.normalize(value1.toString()), - StructuralSearchUtil.normalize(value2.toString()))); + String patternValue = value1.toString(); + if (!patternValue.isEmpty() && patternValue.equals(patternValue.trim())) { + myMatchingVisitor.setResult(myMatchingVisitor.matchText(StructuralSearchUtil.normalize(patternValue), + StructuralSearchUtil.normalize(value2.toString()))); + } + else { + myMatchingVisitor.setResult(myMatchingVisitor.matchText(patternValue, value2.toString())); + } } else if (value1 != null && value2 != null) { myMatchingVisitor.setResult(value1.equals(value2)); diff --git a/native/MacScreenMenu/src/Menu.m b/native/MacScreenMenu/src/Menu.m index f3f6ef3e02b6..bcc54947c925 100644 --- a/native/MacScreenMenu/src/Menu.m +++ b/native/MacScreenMenu/src/Menu.m @@ -427,4 +427,49 @@ Java_com_intellij_ui_mac_screenmenu_Menu_nativeGetAppMenu(JNIEnv *env, jclass pe } return (jlong)appMenu; JNI_COCOA_EXIT(); -}
\ No newline at end of file +} + +/* + * Class: com_intellij_ui_mac_screenmenu_Menu + * Method: nativeRenameAppMenuItems + * Signature: ()V + */ +JNIEXPORT void JNICALL +Java_com_intellij_ui_mac_screenmenu_Menu_nativeRenameAppMenuItems(JNIEnv *env, jclass peerClass, jobjectArray jStringArray) { + JNI_COCOA_ENTER(); + const int stringCount = (*env)->GetArrayLength(env, jStringArray); + if (stringCount < 2) return; // simple protection (just for insurance) + NSMutableArray * __strong stringArray = [NSMutableArray arrayWithCapacity:stringCount]; + for (int i = 0; i < stringCount; i += 2) { + jstring jTitle = (jstring) ((*env)->GetObjectArrayElement(env, jStringArray, i)); + [stringArray addObject:JavaStringToNSString(env, jTitle)]; + if (i + 1 >= stringCount) return; // simple protection (just for insurance) + jstring jLocalizedTitle = (jstring) ((*env)->GetObjectArrayElement(env, jStringArray, i + 1)); + [stringArray addObject:JavaStringToNSString(env, jLocalizedTitle)]; + } + + dispatch_block_t block = ^{ + NSMenu * mainMenu = [NSApplication sharedApplication].mainMenu; + id appMenu = [mainMenu numberOfItems] > 0 ? [mainMenu itemAtIndex:0] : nil; + if (appMenu != nil) { + appMenu = [appMenu submenu]; + } + for (int i = 0; i < stringCount; i += 2) { + NSString * title = [stringArray objectAtIndex:i]; + NSString * localizedTitle = [stringArray objectAtIndex:i + 1]; + NSMenuItem * item = findItemByTitle(appMenu, title); + if (item != nil) { + [item setTitle:localizedTitle]; + if ([item.view isKindOfClass:CustomMenuItemView.class]) { + [(CustomMenuItemView *)(item.view) recalcSizes]; + } + } + } + }; + if ([NSThread isMainThread]) { + block(); + } else { + dispatch_async(dispatch_get_main_queue(), block); + } + JNI_COCOA_EXIT(); +} diff --git a/platform/build-scripts/groovy/org/jetbrains/intellij/build/BuildOptions.groovy b/platform/build-scripts/groovy/org/jetbrains/intellij/build/BuildOptions.groovy index c38322f3f363..37398d293c7b 100644 --- a/platform/build-scripts/groovy/org/jetbrains/intellij/build/BuildOptions.groovy +++ b/platform/build-scripts/groovy/org/jetbrains/intellij/build/BuildOptions.groovy @@ -234,6 +234,12 @@ final class BuildOptions { static final String VALIDATE_MODULES_STRUCTURE = "intellij.build.module.structure" boolean validateModuleStructure = System.getProperty(VALIDATE_MODULES_STRUCTURE, "false").toBoolean() + /** + * Verify whether class files have a forbidden subpaths in them, false by default + */ + static final String VALIDATE_CLASSFILE_SUBPATHS_PROPERTY = "intellij.verify.classfile.subpaths" + boolean validateClassFileSubpaths = System.getProperty(VALIDATE_CLASSFILE_SUBPATHS_PROPERTY, "false").toBoolean() + @ApiStatus.Internal public boolean compressNonBundledPluginArchive = true diff --git a/platform/build-scripts/groovy/org/jetbrains/intellij/build/ProductProperties.groovy b/platform/build-scripts/groovy/org/jetbrains/intellij/build/ProductProperties.groovy index e12066cac2ca..3639159c8e4f 100644 --- a/platform/build-scripts/groovy/org/jetbrains/intellij/build/ProductProperties.groovy +++ b/platform/build-scripts/groovy/org/jetbrains/intellij/build/ProductProperties.groovy @@ -157,6 +157,11 @@ abstract class ProductProperties { Map<String, String> versionCheckerConfig = null /** + * Strings which are forbidden as a part of resulting class file path + */ + List<String> forbiddenClassFileSubPaths = [] + + /** * Paths to properties files the content of which should be appended to idea.properties file */ List<String> additionalIDEPropertiesFilePaths = [] diff --git a/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/BuildTasksImpl.groovy b/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/BuildTasksImpl.groovy index cc27f9d45811..4b37b32c9150 100644 --- a/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/BuildTasksImpl.groovy +++ b/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/BuildTasksImpl.groovy @@ -963,10 +963,8 @@ idea.fatal.error.notification=disabled DistributionJARsBuilder distributionJARsBuilder = compileModulesForDistribution(context) ProjectStructureMapping projectStructureMapping = distributionJARsBuilder.buildJARs(context) layoutShared(context) - Map<String, String> checkerConfig = context.productProperties.versionCheckerConfig - if (checkerConfig != null) { - ClassVersionChecker.checkVersions(checkerConfig, context, context.paths.distAllDir) - } + + ClassFileChecker.checkClassFiles(context.paths.distAllDir, context) if (context.productProperties.buildSourcesArchive) { DistributionJARsBuilder.buildSourcesArchive(projectStructureMapping, context) diff --git a/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/ClassVersionChecker.groovy b/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/ClassFileChecker.groovy index 15c7a62f9e6b..2a6439466881 100644 --- a/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/ClassVersionChecker.groovy +++ b/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/ClassFileChecker.groovy @@ -36,7 +36,26 @@ import java.util.zip.ZipException * <p>Example: <code>["": "1.8", "lib/idea_rt.jar": "1.3"]</code>.</p> */ @CompileStatic -final class ClassVersionChecker { +final class ClassFileChecker { + static void checkClassFiles(Path targetFile, BuildContext context) { + Map<String, String> versionCheckerConfig = + context.options.buildStepsToSkip.contains(BuildOptions.VERIFY_CLASS_FILE_VERSIONS) + ? [:] + : context.productProperties.versionCheckerConfig ?: [:] + + List<String> forbiddenSubPaths = + context.options.validateClassFileSubpaths + ? context.productProperties.forbiddenClassFileSubPaths ?: [] + : [] + + boolean classFileCheckRequired = + (!versionCheckerConfig.isEmpty() || !forbiddenSubPaths.isEmpty()) + + if (classFileCheckRequired) { + checkClassFilesImpl(versionCheckerConfig, forbiddenSubPaths, context, targetFile) + } + } + private static final class Rule { final String path final int version @@ -49,30 +68,32 @@ final class ClassVersionChecker { } } - private final List<Rule> rules + private final List<Rule> versionRules + private final List<String> forbiddenSubPaths private AtomicInteger checkedJarCount = new AtomicInteger() private AtomicInteger checkedClassCount = new AtomicInteger() - private ClassVersionChecker(List<Rule> rules) { - this.rules = rules + private ClassFileChecker(List<Rule> versionRules, List<String> forbiddenSubPaths) { + this.versionRules = versionRules + this.forbiddenSubPaths = forbiddenSubPaths } static int classVersion(String version) { return version.isEmpty() ? -1 : JavaVersion.parse(version).feature + 44 // 1.1 = 45 } - static void checkVersions(Map<String, String> config, BuildContext context, Path root) { + private static void checkClassFilesImpl(Map<String, String> versionCheckConfig, List<String> forbiddenSubPaths, BuildContext context, Path root) { if (context.options.buildStepsToSkip.contains(BuildOptions.VERIFY_CLASS_FILE_VERSIONS)) { return } BuildHelper.getInstance(context).span(TracerManager.spanBuilder("verify class file versions") - .setAttribute("ruleCount", config.size()) - .setAttribute("root", root.toString()), new Runnable() { + .setAttribute("ruleCount", versionCheckConfig.size()) + .setAttribute("root", root.toString()).setAttribute("forbiddenSubpathCount", forbiddenSubPaths.size()), new Runnable() { @Override void run() { - List<Rule> rules = new ArrayList<Rule>(config.size()) - for (Map.Entry<String, String> entry : config.entrySet()) { + List<Rule> rules = new ArrayList<Rule>(versionCheckConfig.size()) + for (Map.Entry<String, String> entry : versionCheckConfig.entrySet()) { rules.add(new Rule(entry.key, classVersion(entry.value))) } rules.sort(new Comparator<Rule>() { @@ -81,11 +102,11 @@ final class ClassVersionChecker { return Integer.compare(-o1.path.length(), -o2.path.length()) } }) - if (rules.isEmpty() || !rules.last().path.isEmpty()) { + if (!rules.isEmpty() && !rules.last().path.isEmpty()) { throw new IllegalArgumentException("Invalid configuration: missing default version") } - ClassVersionChecker checker = new ClassVersionChecker(rules) + ClassFileChecker checker = new ClassFileChecker(rules, forbiddenSubPaths) Collection<String> errors = new ConcurrentLinkedQueue<>() if (Files.isDirectory(root)) { checker.visitDirectory(root, "", errors) @@ -94,7 +115,7 @@ final class ClassVersionChecker { checker.visitFile(root, "", errors) } - if (checker.checkedClassCount.get() == 0) { + if (!rules.isEmpty() && checker.checkedClassCount.get() == 0) { context.messages.error("No classes found under $root - please check the configuration") } @@ -107,13 +128,13 @@ final class ClassVersionChecker { for (String error in errors) { context.messages.warning(error) } - context.messages.error("Failed with $errorCount problems") + context.messages.error("ClassFileChecker failed with $errorCount problems") } Collection<String> unusedRules = rules.findResults { it.wasUsed ? null : it.path } if (!unusedRules.isEmpty()) { context.messages.error("Class version check rules for the following paths don't match any files, probably entries in " + - "ProductProperties::versionCheckerConfig are out of date:\n${String.join("\n", unusedRules)}") + "ProductProperties::versionCheckerConfig are out of date:\n${String.join("\n", unusedRules)}") } } }) @@ -155,8 +176,13 @@ final class ClassVersionChecker { if (fullPath.endsWith(".zip") || fullPath.endsWith(".jar")) { visitZip(fullPath, relPath, new ZipFile(FileChannel.open(file, EnumSet.of(StandardOpenOption.READ))), errors) } - else if (fullPath.endsWith(".class") && !fullPath.endsWith("module-info.class") && !isMultiVersion(fullPath)) { - new BufferedInputStream(Files.newInputStream(file)).withCloseable { checkVersion(relPath, it, errors) } + else if (fullPath.endsWith(".class")) { + checkIfSubPathIsForbidden(relPath, errors) + + boolean contentCheckRequired = !versionRules.isEmpty() && !fullPath.endsWith("module-info.class") && !isMultiVersion(fullPath) + if (contentCheckRequired) { + new BufferedInputStream(Files.newInputStream(file)).withCloseable { checkVersion(relPath, it, errors) } + } } } @@ -185,8 +211,15 @@ final class ClassVersionChecker { throw new RuntimeException("Cannot read " + childZipPath, e) } } - else if (name.endsWith(".class") && !name.endsWith("module-info.class") && !isMultiVersion(name)) { - checkVersion(join(zipRelPath, "!/", name), file.getInputStream(entry), errors) + else if (name.endsWith(".class")) { + String relPath = join(zipRelPath, "!/", name) + + checkIfSubPathIsForbidden(relPath, errors) + + boolean contentCheckRequired = !versionRules.isEmpty() && !name.endsWith("module-info.class") && !isMultiVersion(name) + if (contentCheckRequired) { + checkVersion(relPath, file.getInputStream(entry), errors) + } } } } @@ -195,6 +228,14 @@ final class ClassVersionChecker { } } + private checkIfSubPathIsForbidden(String relPath, Collection<String> errors) { + for (f in forbiddenSubPaths) { + if (relPath.contains(f)) { + errors.add(relPath + " .class file has a forbidden subpath: " + f) + } + } + } + private static String join(String prefix, String separator, String suffix) { return prefix.isEmpty() ? suffix : (prefix + separator + suffix) } @@ -215,7 +256,7 @@ final class ClassVersionChecker { return } - Rule rule = rules.find { it.path.isEmpty() || path.startsWith(it.path) } + Rule rule = versionRules.find { it.path.isEmpty() || path.startsWith(it.path) } rule.wasUsed = true int expected = rule.version if (expected > 0 && major > expected) { diff --git a/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/CrossPlatformDistributionBuilder.groovy b/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/CrossPlatformDistributionBuilder.groovy index da5171e94ffb..793ce86155c5 100644 --- a/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/CrossPlatformDistributionBuilder.groovy +++ b/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/CrossPlatformDistributionBuilder.groovy @@ -47,10 +47,7 @@ final class CrossPlatformDistributionBuilder { ProductInfoValidator.checkInArchive(context, targetFile, "") context.notifyArtifactBuilt(targetFile) - Map<String, String> checkerConfig = context.productProperties.versionCheckerConfig - if (checkerConfig != null) { - ClassVersionChecker.checkVersions(checkerConfig, context, targetFile) - } + ClassFileChecker.checkClassFiles(targetFile, context) return targetFile } } diff --git a/platform/diff-impl/intellij.platform.diff.impl.iml b/platform/diff-impl/intellij.platform.diff.impl.iml index afc78f576e8d..5d1b8d7fe421 100644 --- a/platform/diff-impl/intellij.platform.diff.impl.iml +++ b/platform/diff-impl/intellij.platform.diff.impl.iml @@ -23,6 +23,5 @@ <orderEntry type="library" name="fastutil-min" level="project" /> <orderEntry type="module" module-name="intellij.platform.ide.util.io.impl" /> <orderEntry type="library" name="kotlinx-coroutines-jdk8" level="project" /> - <orderEntry type="library" name="StreamEx" level="project" /> </component> </module>
\ No newline at end of file diff --git a/platform/diff-impl/src/com/intellij/diff/tools/external/ExternalDiffSettings.kt b/platform/diff-impl/src/com/intellij/diff/tools/external/ExternalDiffSettings.kt index 8dcfcc26d45c..6212c47a899a 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/external/ExternalDiffSettings.kt +++ b/platform/diff-impl/src/com/intellij/diff/tools/external/ExternalDiffSettings.kt @@ -18,6 +18,10 @@ class ExternalDiffSettings : BaseState(), PersistentStateComponent<ExternalDiffS copyFrom(state) } + override fun noStateLoaded() { + isSettingsMigrated = true + } + @get:OptionTag("MIGRATE_OLD_SETTINGS") var isSettingsMigrated by property(false) @@ -98,6 +102,16 @@ class ExternalDiffSettings : BaseState(), PersistentStateComponent<ExternalDiffS get() = FileTypeManager.getInstance() @JvmStatic + fun findDefaultDiffTool(): ExternalTool? { + val diffToolName = instance.defaultToolConfiguration.diffToolName + + if (diffToolName == ExternalToolConfiguration.BUILTIN_TOOL) return null + val diffTools = instance.externalTools[ExternalToolGroup.DIFF_TOOL] ?: return null + + return findTool(diffTools, diffToolName) + } + + @JvmStatic fun findDiffTool(fileType: FileType): ExternalTool? { val diffToolName = findToolConfiguration(fileType)?.diffToolName ?: instance.defaultToolConfiguration.diffToolName diff --git a/platform/diff-impl/src/com/intellij/diff/tools/external/ExternalDiffTool.java b/platform/diff-impl/src/com/intellij/diff/tools/external/ExternalDiffTool.java index 1c3643482ece..e5f9eb381a85 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/external/ExternalDiffTool.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/external/ExternalDiffTool.java @@ -14,12 +14,14 @@ import com.intellij.openapi.ListSelection; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diff.DiffBundle; import com.intellij.openapi.fileTypes.FileTypeManager; +import com.intellij.openapi.fileTypes.UnknownFileType; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.Conditions; import com.intellij.openapi.util.NlsContexts; import com.intellij.openapi.util.UserDataHolderBase; import com.intellij.openapi.util.io.FileUtilRt; @@ -29,12 +31,13 @@ import com.intellij.util.ThrowableConvertor; import com.intellij.util.concurrency.annotations.RequiresBackgroundThread; import com.intellij.util.concurrency.annotations.RequiresEdt; import com.intellij.util.containers.ContainerUtil; -import one.util.streamex.StreamEx; +import com.intellij.util.containers.JBIterable; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; public final class ExternalDiffTool { @@ -52,12 +55,32 @@ public final class ExternalDiffTool { if (isDefault()) return true; FileTypeManager fileTypeManager = FileTypeManager.getInstance(); - return diffProducers.stream() - .map(DiffRequestProducer::getName) - .filter(filePath -> !FileUtilRt.getExtension(filePath).equals("tmp")) - .map(filePath -> fileTypeManager.getFileTypeByFileName(filePath)) - .distinct() - .anyMatch(fileType -> ExternalDiffSettings.findDiffTool(fileType) != null); + return JBIterable.from(diffProducers) + .map(DiffRequestProducer::getName) + .filter(filePath -> !FileUtilRt.getExtension(filePath).equals("tmp")) + .map(filePath -> fileTypeManager.getFileTypeByFileName(filePath)) + .unique() + .map(fileType -> ExternalDiffSettings.findDiffTool(fileType)) + .filter(Conditions.notNull()) + .first() != null; + } + + @Nullable + private static ExternalDiffSettings.ExternalTool getExternalToolFor(@NotNull ContentDiffRequest request) { + ExternalDiffSettings.ExternalTool diffTool = JBIterable.from(request.getContents()) + .map(content -> content.getContentType()) + .filter(Conditions.notNull()) + .unique() + .sort(Comparator.comparing(fileType -> fileType != UnknownFileType.INSTANCE ? -1 : 1)) + .map(fileType -> ExternalDiffSettings.findDiffTool(fileType)) + .filter(Conditions.notNull()) + .first(); + if (diffTool != null) return diffTool; + + if (isDefault()) { + return ExternalDiffSettings.findDefaultDiffTool(); + } + return null; } public static boolean showIfNeeded(@Nullable Project project, @@ -120,13 +143,7 @@ public final class ExternalDiffTool { throws IOException, ExecutionException { if (!canShow(request)) return false; - List<DiffContent> contents = ((ContentDiffRequest)request).getContents(); - ExternalDiffSettings.ExternalTool externalTool = StreamEx.of(contents) - .map(content -> content.getContentType()) - .nonNull() - .map(fileType -> ExternalDiffSettings.findDiffTool(fileType)) - .nonNull() - .findFirst().orElse(null); + ExternalDiffSettings.ExternalTool externalTool = getExternalToolFor(((ContentDiffRequest)request)); if (externalTool == null) return false; showRequest(project, request, externalTool); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/completion/PostfixTemplatesCompletionProvider.java b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/completion/PostfixTemplatesCompletionProvider.java index 7a131a837ffa..d9cfa62f2748 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/completion/PostfixTemplatesCompletionProvider.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/completion/PostfixTemplatesCompletionProvider.java @@ -1,4 +1,4 @@ -// 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.codeInsight.template.postfix.completion; import com.intellij.codeInsight.completion.CompletionParameters; @@ -24,9 +24,7 @@ class PostfixTemplatesCompletionProvider extends CompletionProvider<CompletionPa Editor editor = parameters.getEditor(); if (!isCompletionEnabled(parameters) || LiveTemplateCompletionContributor.shouldShowAllTemplates() || editor.getCaretModel().getCaretCount() != 1) { - /* - disabled or covered with {@link com.intellij.codeInsight.template.impl.LiveTemplateCompletionContributor} - */ + // disabled or covered with com.intellij.codeInsight.template.impl.LiveTemplateCompletionContributor return; } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplate.java b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplate.java index 4630390d962e..6ffef2da0658 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplate.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplate.java @@ -1,4 +1,4 @@ -// 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.codeInsight.template.postfix.templates; import com.intellij.codeInsight.CodeInsightBundle; @@ -10,6 +10,7 @@ import com.intellij.openapi.util.NlsContexts; import com.intellij.openapi.util.NlsSafe; import com.intellij.openapi.util.NotNullLazyValue; import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -19,20 +20,27 @@ import java.util.Objects; /** * Represents a postfix template. - * Postfix templates are live template that applicable to a specific code fragment e.g. "sout" template: + * Postfix template is a live template that is applicable to a specific code fragment, e.g. "sout" template: + * <br> * <code> * "hello".sout * </code> + * <br> * is expanded to: - * <code> - * System.out.println("hello") - * <code/> * <br> - * Editable templates: - * editable postfix template MUST know the provider that created it. + * <code> + * System.out.println("hello"); + * </code> * <p> - * Editable postfix templates MUST provide proper equals/hashCode implementation. - * Equal postfix templates produces by the very same provider will overwrite each other. + * Editable postfix template MUST: + * <ul> + * <li>know the provider that created it</li> + * <li>provide proper {@code equals()}/{@code hashCode()} implementation</li> + * </ul> + * Equal postfix templates produced by the very same provider will overwrite each other. + * + * @see PostfixTemplateProvider + * @see <a href="https://plugins.jetbrains.com/docs/intellij/postfix-templates.html">Postfix Templates (IntelliJ Platform Docs)</a> */ public abstract class PostfixTemplate { private final @NotNull @NonNls String myId; @@ -92,9 +100,8 @@ public abstract class PostfixTemplate { return defaultDescription; } - /** - * Template's identifier. Used for saving the settings related to this templates. + * @return identifier used for saving the settings related to this template */ @NotNull public @NonNls String getId() { @@ -102,25 +109,32 @@ public abstract class PostfixTemplate { } /** - * Template's key. Used while expanding template in editor. - * - * @return + * @return key used for expanding the template in the editor */ @NotNull public final @NlsSafe String getKey() { return myKey; } + /** + * @return template name displayed in UI + */ @NotNull public @NlsSafe String getPresentableName() { return myPresentableName; } + /** + * @return template description displayed in UI + */ @NotNull public @NlsContexts.DetailedDescription String getDescription() { return myLazyDescription.getValue(); } + /** + * @return short example of the expanded form shown in the completion popup and templates tree on the configuration page + */ @NotNull public @NlsSafe String getExample() { return myExample; @@ -130,30 +144,53 @@ public abstract class PostfixTemplate { return true; } + /** + * @return {@code true} if general postfix templates setting is enabled and this template is enabled in settings + */ public boolean isEnabled(PostfixTemplateProvider provider) { final PostfixTemplatesSettings settings = PostfixTemplatesSettings.getInstance(); return settings.isPostfixTemplatesEnabled() && settings.isTemplateEnabled(this, provider); } + /** + * Determines whether this template can be used in the given context specified by the parameters. + * + * @param context PSI element before the template key + * @param copyDocument copy of the document that contains changes introduced + * in {@link PostfixTemplateProvider#preCheck(PsiFile, Editor, int)} method + * @param newOffset offset before the template key + * @return {@code true} if template is applicable in the given context, {@code false} otherwise + */ public abstract boolean isApplicable(@NotNull PsiElement context, @NotNull Document copyDocument, int newOffset); + /** + * Inserts the template content in the given editor. + * + * @param context PSI element before the template key + * @param editor current editor + */ public abstract void expand(@NotNull PsiElement context, @NotNull Editor editor); + /** + * @return the {@link PostfixTemplateProvider} that provided this template + */ @Nullable public PostfixTemplateProvider getProvider() { return myProvider; } /** - * Builtin templates cannot be removed. - * If they are editable, they can be restored to default. + * Built-in templates cannot be removed. + * If they are editable, they can be restored to the default state. */ public boolean isBuiltin() { return true; } /** - * Template can be edit. Template can be editable if its provider is not null and its key starts with . can be edited. + * Return true if this template can be edited. + * <p> + * Note that a template can be edited if its provider is not {@code null} and its key starts with {@code .} (dot), e.g., {@code .iter}. */ public boolean isEditable() { return true; diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplateExpressionSelector.java b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplateExpressionSelector.java index 57e2850b8eb4..59f23bd35012 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplateExpressionSelector.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplateExpressionSelector.java @@ -1,21 +1,6 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// 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.codeInsight.template.postfix.templates; - import com.intellij.openapi.editor.Document; import com.intellij.psi.PsiElement; import com.intellij.util.Function; @@ -24,20 +9,23 @@ import org.jetbrains.annotations.NotNull; import java.util.List; /** - * Interface provides method used in {@link PostfixTemplateWithExpressionSelector} + * Provides information about expressions available in the current postfix template context. + * + * @see PostfixTemplateWithExpressionSelector + * @see <a href="https://plugins.jetbrains.com/docs/intellij/advanced-postfix-templates.html">Advanced Postfix Templates (IntelliJ Platform Docs)</a> */ public interface PostfixTemplateExpressionSelector { /** - * Check that we can select not-null expression(PsiElement) in current context + * Checks whether the current context contains applicable expression that can be selected. */ boolean hasExpression(@NotNull PsiElement context, @NotNull Document copyDocument, int newOffset); /** - * Return list of all possible expressions in the current position. - * Postfix template implementation shows popup chooser (if size > 1) + * Returns the list of all expressions applicable in the current context. + * If the list size is greater than 1, then expression chooser popup is shown to a user. */ @NotNull List<PsiElement> getExpressions(@NotNull PsiElement context, @@ -45,8 +33,8 @@ public interface PostfixTemplateExpressionSelector { int offset); /** - * returns renderer for expressions from {@link #getExpressions}. - * Renderer is used for showing popup chooser + * Returns a renderer for expressions returned from the {@link #getExpressions} method, + * which is used to render template item in the expression chooser popup. */ @NotNull Function<PsiElement, String> getRenderer(); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplateProvider.java b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplateProvider.java index c516bf3c4c46..446e9e581d30 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplateProvider.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplateProvider.java @@ -1,7 +1,6 @@ -// 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.codeInsight.template.postfix.templates; - import com.intellij.codeInsight.template.postfix.templates.editable.PostfixTemplateEditor; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.util.NlsActions; @@ -16,12 +15,15 @@ import java.util.Set; /** * Extension point interface for providing language specific postfix templates. + * * @see LanguagePostfixTemplate#EP_NAME * @see PostfixTemplate + * @see <a href=https://plugins.jetbrains.com/docs/intellij/postfix-completion.html">Postfix Completion (IntelliJ Platform Docs)</a> */ public interface PostfixTemplateProvider { + /** - * Identifier of template provider. Used for storing settings of provider's templates. + * @return identifier used for storing settings of this provider's templates */ @NotNull default @NonNls String getId() { @@ -29,7 +31,7 @@ public interface PostfixTemplateProvider { } /** - * Presentation name of editable template type. If null, provider doesn't allow to custom templates. + * @return presentation name of an editable template type. If {@code null}, the provider doesn't allow customizing its templates. */ @Nullable default @NlsActions.ActionText String getPresentableName() { @@ -37,54 +39,59 @@ public interface PostfixTemplateProvider { } /** - * Returns builtin templates registered in the provider in their original state. + * @return built-in templates registered in the provider in their original state. * Consider using {@link PostfixTemplatesUtils#getAvailableTemplates(PostfixTemplateProvider)} for actually enabled templates. */ @NotNull Set<PostfixTemplate> getTemplates(); /** - * Check symbol can separate template keys + * @return {@code true} if a given symbol can separate template keys */ boolean isTerminalSymbol(char currentChar); /** - * Prepare file for template expanding. Running on EDT. - * E.g. java postfix templates adds semicolon after caret in order to simplify context checking. + * Prepares a file for template expanding, + * e.g. a Java postfix template adds a semicolon after caret in order to simplify context checking. * <p> * File content doesn't contain template's key, it is deleted just before this method invocation. * <p> - * Note that while postfix template is checking its availability the file parameter is a _COPY_ of the real file, - * so you can do with it anything that you want, but in the same time it doesn't recommended to modify editor state because it's real. + * Running on EDT. + * <p> + * Note that when postfix template's availability is checked, the {@code file} parameter is a COPY of the actual file, + * so you can do with it anything that you want. + * At the same time, it is not recommended to modify the editor state because it's real. */ void preExpand(@NotNull PsiFile file, @NotNull Editor editor); /** - * Invoked after template finished (doesn't matter if it finished successfully or not). - * E.g. java postfix template use this method for deleting inserted semicolon. + * Cleans up a file content after template expanding is finished (doesn't matter if it finished successfully or not), + * e.g. a Java postfix template cleans a semicolon inserted in {@link #preExpand(PsiFile, Editor)}. */ void afterExpand(@NotNull PsiFile file, @NotNull Editor editor); /** - * Prepare file for checking availability of templates. - * Almost the same as {@link this#preExpand(PsiFile, Editor)} with several differences: - * 1. Processes copy of file. So implementations can modify it without corrupting the real file. - * 2. Could be invoked from anywhere (EDT, write-action, read-action, completion-thread etc.). So implementations should make - * additional effort to make changes in file. + * Prepares a file for checking the availability of templates. + * Almost the same as {@link #preExpand(PsiFile, Editor)} with several differences: + * <ol> + * <li>Processes copy of the file. So implementations can modify it without corrupting the real file. + * <li>Could be invoked from anywhere (EDT, write-action, read-action, completion-thread, etc.) so implementations should make + * additional effort to make changes in the file. + * </ol> * <p> - * Content of file copy doesn't contain template's key, it is deleted just before this method invocation. + * File content doesn't contain template's key, it is deleted just before this method invocation. * <p> - * NOTE: editor is real (not copy) and it doesn't represents the copyFile. - * So it's safer to use currentOffset parameter instead of offset from editor. Do not modify text via editor. + * NOTE: editor is real (not a copy) and it doesn't represent the {@code copyFile}. + * It's safer to use {@code currentOffset} parameter instead of the editor offset. + * Do not modify text with the provided {@code realEditor}. */ @NotNull PsiFile preCheck(@NotNull PsiFile copyFile, @NotNull Editor realEditor, int currentOffset); /** - * Return the editor that it able to represent template in UI - * and create the template from the settings that users set in UI. + * Returns the editor that is used to represent a template in UI and create the template from the settings provided by users. * <p> - * If templateToEdit is null, it's considered like an editor for a new template. + * If {@code templateToEdit} is {@code null}, it's considered as an editor for a new template. */ @Nullable default PostfixTemplateEditor createEditor(@Nullable PostfixTemplate templateToEdit) { @@ -92,7 +99,7 @@ public interface PostfixTemplateProvider { } /** - * Instantiates the template that was serialized by the provider to XML. + * Reads from a given DOM element, and instantiates a template that was stored by this provider. */ @Nullable default PostfixTemplate readExternalTemplate(@NotNull @NonNls String id, @NotNull @NlsSafe String name, @NotNull Element template) { @@ -100,7 +107,7 @@ public interface PostfixTemplateProvider { } /** - * Serialized to XML the template that was created by the provider. + * Stores a given template that was created by this provider to a given parent DOM element. */ default void writeExternalTemplate(@NotNull PostfixTemplate template, @NotNull Element parentElement) { } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplatesUtils.java b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplatesUtils.java index 73427895ea94..41464887f005 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplatesUtils.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplatesUtils.java @@ -1,4 +1,4 @@ -// 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.codeInsight.template.postfix.templates; import com.intellij.codeInsight.CodeInsightBundle; @@ -38,7 +38,7 @@ public final class PostfixTemplatesUtils { } /** - * Returns all templates registered in the provider, including the edited templates and builtin templates in their current state + * @return all templates registered in the given provider, including the edited templates and builtin templates in their current state. */ @NotNull public static Set<PostfixTemplate> getAvailableTemplates(@NotNull PostfixTemplateProvider provider) { @@ -52,6 +52,10 @@ public final class PostfixTemplatesUtils { return result; } + /** + * Surrounds a given expression with the provided surrounder. + * @return range to select/position the caret + */ @Nullable public static TextRange surround(@NotNull Surrounder surrounder, @NotNull Editor editor, @@ -72,6 +76,9 @@ public final class PostfixTemplatesUtils { CodeInsightBundle.message("error.hint.can.t.expand.postfix.template"), ""); } + /** + * Generates a unique in the scope of a given provider template ID. + */ @NotNull public static String generateTemplateId(@NotNull String templateKey, @NotNull PostfixTemplateProvider provider) { Set<String> usedIds = new HashSet<>(); @@ -84,6 +91,12 @@ public final class PostfixTemplatesUtils { return UniqueNameGenerator.generateUniqueName(templateKey + "@userDefined", usedIds); } + /** + * Stores a given editable template in the given parent DOM element. + * The given template must be an instance of {@link EditablePostfixTemplate}. + * If the given template is {@link EditablePostfixTemplateWithMultipleExpressions}, + * then all data like usage of the topmost expression flag and expression conditions are stored. + */ public static void writeExternalTemplate(@NotNull PostfixTemplate template, @NotNull Element parentElement) { if (template instanceof EditablePostfixTemplateWithMultipleExpressions) { parentElement.setAttribute(TOPMOST_ATTR, String.valueOf(((EditablePostfixTemplateWithMultipleExpressions<?>)template).isUseTopmostExpression())); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/SurroundPostfixTemplateBase.java b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/SurroundPostfixTemplateBase.java index b7fe95b96a5c..8b65e7755b56 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/SurroundPostfixTemplateBase.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/SurroundPostfixTemplateBase.java @@ -1,4 +1,4 @@ -// Copyright 2000-2019 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.codeInsight.template.postfix.templates; import com.intellij.lang.surroundWith.Surrounder; @@ -9,6 +9,11 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; +/** + * Base for surrounding postfix templates that utilize existing {@link Surrounder} implementations. + * + * @see <a href="https://plugins.jetbrains.com/docs/intellij/advanced-postfix-templates.html">Advanced Postfix Templates (IntelliJ Platform Docs)</a> + */ public abstract class SurroundPostfixTemplateBase extends PostfixTemplateWithExpressionSelector { @NotNull protected final PostfixTemplatePsiInfo myPsiInfo; @@ -21,7 +26,6 @@ public abstract class SurroundPostfixTemplateBase extends PostfixTemplateWithExp myPsiInfo = psiInfo; } - @Override public final void expandForChooseExpression(@NotNull PsiElement expression, @NotNull final Editor editor) { PsiElement replace = getReplacedExpression(expression); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/editable/EditablePostfixTemplate.java b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/editable/EditablePostfixTemplate.java index 891063db21b0..5db14edf4830 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/editable/EditablePostfixTemplate.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/editable/EditablePostfixTemplate.java @@ -1,4 +1,4 @@ -// Copyright 2000-2019 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.codeInsight.template.postfix.templates.editable; import com.intellij.codeInsight.CodeInsightBundle; @@ -28,6 +28,14 @@ import org.jetbrains.annotations.NotNull; import java.util.List; import java.util.Objects; +/** + * Base class for editable templates. + * Template data is backed by live template. + * It supports selecting the expression a template is applied to. + * + * @see EditablePostfixTemplateWithMultipleExpressions + * @see <a href="https://plugins.jetbrains.com/docs/intellij/advanced-postfix-templates.html">Advanced Postfix Templates (IntelliJ Platform Docs)</a> + */ public abstract class EditablePostfixTemplate extends PostfixTemplate { @NotNull private final TemplateImpl myLiveTemplate; @@ -123,11 +131,11 @@ public abstract class EditablePostfixTemplate extends PostfixTemplate { } /** + * Default implementation delegates to {@link #getElementToRemove(PsiElement)} and takes the text range of the resulting element. + * Override it if it's desired to remove only a part of {@code PsiElement}'s range. + * * @param element element to which the template was applied * @return a range to remove before inserting the template - * - * Default implementation delegates to getElementToRemove and takes the range of the resulting element. - * You may override it if it's desired to remove only a part of PsiElement range */ @NotNull protected TextRange getRangeToRemove(@NotNull PsiElement element) { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/editable/EditablePostfixTemplateWithMultipleExpressions.java b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/editable/EditablePostfixTemplateWithMultipleExpressions.java index 58b57c2c262e..7b4e4e24cb86 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/editable/EditablePostfixTemplateWithMultipleExpressions.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/editable/EditablePostfixTemplateWithMultipleExpressions.java @@ -1,4 +1,4 @@ -// 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.codeInsight.template.postfix.templates.editable; import com.intellij.codeInsight.template.impl.TemplateImpl; @@ -10,6 +10,14 @@ import org.jetbrains.annotations.NotNull; import java.util.Objects; import java.util.Set; +/** + * Base class for editable templates with applicable expressions conditions. + * Template data is backed by live template. + * It supports selecting the expression a template is applied to. + * + * @param <ConditionType> expression condition type + * @see <a href="https://plugins.jetbrains.com/docs/intellij/advanced-postfix-templates.html">Advanced Postfix Templates (IntelliJ Platform Docs)</a> + */ public abstract class EditablePostfixTemplateWithMultipleExpressions<ConditionType extends PostfixTemplateExpressionCondition> extends EditablePostfixTemplate { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/editable/PostfixTemplateEditor.java b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/editable/PostfixTemplateEditor.java index d0e59be154e9..fddfeb14aaf4 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/editable/PostfixTemplateEditor.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/editable/PostfixTemplateEditor.java @@ -1,4 +1,4 @@ -// Copyright 2000-2019 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.codeInsight.template.postfix.templates.editable; import com.intellij.codeInsight.template.postfix.templates.PostfixTemplate; @@ -9,13 +9,26 @@ import org.jetbrains.annotations.NotNull; import javax.swing.*; /** - * The editor that is able to show the UI settings for a particular template, - * or to create a template from settings defined in UI form. + * Represents the postfix template editor used for creating and editing editable templates. + * + * @see com.intellij.codeInsight.template.postfix.settings.PostfixEditTemplateDialog + * @see <a href="https://plugins.jetbrains.com/docs/intellij/advanced-postfix-templates.html">Advanced Postfix Templates (IntelliJ Platform Docs)</a> */ public interface PostfixTemplateEditor extends Disposable { + + /** + * Creates a template from settings defined in the UI form. + * + * @param templateId unique template ID + * @param templateName + * @return created template + */ @NotNull PostfixTemplate createTemplate(@NotNull String templateId, @NotNull String templateName); + /** + * @return template settings form component + */ @NotNull JComponent getComponent(); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/editable/PostfixTemplateExpressionCondition.java b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/editable/PostfixTemplateExpressionCondition.java index f8d4a4be1d27..f12a59990f42 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/editable/PostfixTemplateExpressionCondition.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/editable/PostfixTemplateExpressionCondition.java @@ -1,4 +1,4 @@ -// 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.codeInsight.template.postfix.templates.editable; import com.intellij.openapi.util.Condition; @@ -8,6 +8,14 @@ import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +/** + * Editable postfix template expression condition used to determine contexts that a postfix template can be applied in. + * + * @param <T> the supported PSI element type + * + * @see EditablePostfixTemplateWithMultipleExpressions + * @see <a href="https://plugins.jetbrains.com/docs/intellij/advanced-postfix-templates.html">Advanced Postfix Templates (IntelliJ Platform Docs)</a> + */ public interface PostfixTemplateExpressionCondition<T extends PsiElement> extends Condition<T> { @NonNls String ID_ATTR = "id"; @@ -19,7 +27,7 @@ public interface PostfixTemplateExpressionCondition<T extends PsiElement> extend /** - * @return id for serialization + * @return ID for serialization */ @NotNull @NonNls String getId(); @@ -31,6 +39,11 @@ public interface PostfixTemplateExpressionCondition<T extends PsiElement> extend element.setAttribute(ID_ATTR, getId()); } + /** + * @param t PSI element to check + * @return {@code true} if an expression context determined by a given element is applicable for evaluated postfix template, + * {@code false} otherwise + */ @Override boolean value(@NotNull T t); } diff --git a/platform/lang-impl/src/com/intellij/ide/actions/CreateFileAction.java b/platform/lang-impl/src/com/intellij/ide/actions/CreateFileAction.java index 9da3d43fa620..c07eb8ca5739 100644 --- a/platform/lang-impl/src/com/intellij/ide/actions/CreateFileAction.java +++ b/platform/lang-impl/src/com/intellij/ide/actions/CreateFileAction.java @@ -7,6 +7,7 @@ import com.intellij.ide.IdeBundle; import com.intellij.ide.ui.newItemPopup.NewItemPopupUtil; import com.intellij.ide.ui.newItemPopup.NewItemSimplePopupPanel; import com.intellij.idea.ActionsBundle; +import com.intellij.internal.statistic.collectors.fus.fileTypes.FileTypeUsageCounterCollector; import com.intellij.lang.LangBundle; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.Experiments; @@ -26,6 +27,7 @@ import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -111,7 +113,9 @@ public class CreateFileAction extends CreateElementActionBase implements DumbAwa @Override protected PsiElement @NotNull [] create(@NotNull String newName, @NotNull PsiDirectory directory) throws Exception { MkDirs mkdirs = new MkDirs(newName, directory); - return new PsiElement[]{WriteAction.compute(() -> mkdirs.directory.createFile(getFileName(mkdirs.newName)))}; + PsiFile file = WriteAction.compute(() -> mkdirs.directory.createFile(getFileName(mkdirs.newName))); + FileTypeUsageCounterCollector.triggerCreate(file.getProject(), file.getVirtualFile()); + return new PsiElement[]{file}; } @NotNull 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 b8878cd81cbc..51e6618a34c9 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-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// Copyright 2000-2021 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,7 +15,6 @@ 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; @@ -93,7 +92,7 @@ public final class FileBasedIndexProjectHandler { @NotNull Project project, long refreshedFilesCalcDuration) { ProjectIndexingHistoryImpl projectIndexingHistory = - new ProjectIndexingHistoryImpl(project, "On refresh of " + files.size() + " files", ScanningType.REFRESH); + new ProjectIndexingHistoryImpl(project, "On refresh of " + files.size() + " files", false); 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 e464c4142cf5..a59c83922d21 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/ForceIndexRescanningAction.java +++ b/platform/lang-impl/src/com/intellij/util/indexing/ForceIndexRescanningAction.java @@ -1,10 +1,9 @@ -// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// 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.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 { @@ -16,8 +15,7 @@ final class ForceIndexRescanningAction extends DumbAwareAction { false, false, null, - "Force re-scanning", - ScanningType.FULL_FORCED); + "Force re-scanning"); 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 1f659a703777..d381ab9056d5 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-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// 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.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,9 +39,7 @@ class RescanIndexesAction : RecoveryAction { if (recoveryScope is FilesRecoveryScope) { predefinedIndexableFilesIterators = recoveryScope.files.map { ProjectIndexableFilesIteratorImpl(it) } } - object : UnindexedFilesUpdater(project, false, false, - predefinedIndexableFilesIterators, "Rescanning indexes recovery action", - if(predefinedIndexableFilesIterators == null) ScanningType.FULL_FORCED else ScanningType.PARTIAL_FORCED) { + object : UnindexedFilesUpdater(project, predefinedIndexableFilesIterators, "Rescanning indexes recovery action") { 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 53798fa938ca..87597de332ba 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-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// Copyright 2000-2021 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,7 +36,6 @@ 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; @@ -86,7 +85,6 @@ 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; @@ -94,13 +92,11 @@ public class UnindexedFilesUpdater extends DumbModeTask { boolean startSuspended, boolean onProjectOpen, @Nullable List<IndexableFilesIterator> predefinedIndexableFilesIterators, - @Nullable @NonNls String indexingReason, - @NotNull ScanningType scanningType) { + @Nullable @NonNls String indexingReason) { myProject = project; myStartSuspended = startSuspended; myOnProjectOpen = onProjectOpen; myIndexingReason = indexingReason; - myScanningType = scanningType; myPusher = PushedFilePropertiesUpdater.getInstance(myProject); myPredefinedIndexableFilesIterators = predefinedIndexableFilesIterators; @@ -160,8 +156,7 @@ public class UnindexedFilesUpdater extends DumbModeTask { myStartSuspended, false, mergeIterators(myPredefinedIndexableFilesIterators, ((UnindexedFilesUpdater)taskFromQueue).myPredefinedIndexableFilesIterators), - reason, - ScanningType.Companion.merge(oldTask.myScanningType, oldTask.myScanningType) + reason ); } @@ -182,18 +177,17 @@ 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, ScanningType.FULL); + this(project, false, false, null, null); } public UnindexedFilesUpdater(@NotNull Project project, @Nullable @NonNls String indexingReason) { - this(project, false, false, null, indexingReason, ScanningType.FULL); + this(project, false, false, null, indexingReason); } public UnindexedFilesUpdater(@NotNull Project project, @Nullable List<IndexableFilesIterator> predefinedIndexableFilesIterators, @Nullable @NonNls String indexingReason) { - this(project, false, false, predefinedIndexableFilesIterators, indexingReason, - predefinedIndexableFilesIterators == null ? ScanningType.FULL : ScanningType.PARTIAL); + this(project, false, false, predefinedIndexableFilesIterators, indexingReason); } private void updateUnindexedFiles(@NotNull ProjectIndexingHistoryImpl projectIndexingHistory, @NotNull ProgressIndicator indicator) { @@ -579,8 +573,7 @@ public class UnindexedFilesUpdater extends DumbModeTask { } protected @NotNull ProjectIndexingHistoryImpl performScanningAndIndexing(@NotNull ProgressIndicator indicator) { - ProjectIndexingHistoryImpl projectIndexingHistory = - new ProjectIndexingHistoryImpl(myProject, myIndexingReason, myScanningType); + ProjectIndexingHistoryImpl projectIndexingHistory = new ProjectIndexingHistoryImpl(myProject, myIndexingReason, isFullIndexUpdate()); myIndex.loadIndexes(); myIndex.filesUpdateStarted(myProject, isFullIndexUpdate()); IndexDiagnosticDumper.getInstance().onIndexingStarted(projectIndexingHistory); @@ -650,21 +643,17 @@ 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, - ScanningType.FULL_ON_PROJECT_OPEN).queue(project); + new UnindexedFilesUpdater(project, startSuspended, true, Collections.singletonList(iterator), indexingReason).queue(project); } project.putUserData(CONTENT_SCANNED, true); } else { - new UnindexedFilesUpdater(project, startSuspended, true, null, indexingReason, ScanningType.FULL_ON_PROJECT_OPEN). - queue(project); + new UnindexedFilesUpdater(project, startSuspended, true, null, indexingReason).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 495348b196db..f7fa953ce974 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,56 +33,6 @@ 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 @@ -108,7 +58,7 @@ interface StatsPerIndexer { interface IndexingTimes { val indexingReason: String? - val scanningType: ScanningType + val wasFullIndexing: Boolean 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 b845875b42a6..b3643550c5ce 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,9 +11,7 @@ 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 { @@ -50,7 +48,7 @@ class ProjectIndexingHistoryFusReporterListener : ProjectIndexingHistoryListener ProjectIndexingHistoryFusReporter.reportIndexingFinished( projectIndexingHistory.project, projectIndexingHistory.indexingSessionId, - projectIndexingHistory.times.scanningType, + projectIndexingHistory.times.wasFullIndexing, projectIndexingHistory.times.totalUpdatingTime.toMillis(), projectIndexingHistory.times.indexingDuration.toMillis(), scanningTime, @@ -80,14 +78,13 @@ class ProjectIndexingHistoryFusReporterListener : ProjectIndexingHistoryListener } object ProjectIndexingHistoryFusReporter : CounterUsagesCollector() { - private val GROUP = EventLogGroup("indexing.statistics", 6) + private val GROUP = EventLogGroup("indexing.statistics", 5) 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") @@ -115,7 +112,6 @@ object ProjectIndexingHistoryFusReporter : CounterUsagesCollector() { "finished", indexingSessionId, isFullIndexing, - scanningType, totalTime, indexingTime, scanningTime, @@ -138,7 +134,7 @@ object ProjectIndexingHistoryFusReporter : CounterUsagesCollector() { fun reportIndexingFinished( project: Project, indexingSessionId: Long, - scanningType: ScanningType, + wasFullIndexing: Boolean, totalTime: Long, indexingTime: Long, scanningTime: Long, @@ -154,8 +150,7 @@ object ProjectIndexingHistoryFusReporter : CounterUsagesCollector() { indexingFinished.log( project, this.indexingSessionId.with(indexingSessionId), - this.isFullIndexing.with(scanningType.isFull), - this.scanningType.with(scanningType), + this.isFullIndexing.with(wasFullIndexing), 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 fa47fb1b29ec..198a2c2a3ce4 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 scanningType: ScanningType) : ProjectIndexingHistory { + private val wasFullIndexing: Boolean) : 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, scanningType = scanningType, + private val timesImpl = IndexingTimesImpl(indexingReason = indexingReason, wasFullIndexing = wasFullIndexing, 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 scanningType: ScanningType, + override val wasFullIndexing: Boolean, 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 f3d2684389ff..4b8fa5a6874f 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, - scanningType = scanningType, + wasFullIndexing = wasFullIndexing, 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 d90b8ff2fc27..b9e498f53150 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,13 +3,12 @@ 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 scanningType: ScanningType = ScanningType.FULL, + val wasFullIndexing: Boolean = false, 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 ceaec19987d4..9a23f69063e2 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,7 +14,6 @@ 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, @@ -96,7 +95,7 @@ fun createAggregateHtml( td(diagnostic.appInfo.productCode + "-" + diagnostic.appInfo.build) //Indexing type section - td(diagnostic.indexingTimes.scanningType.name.lowercase(Locale.ENGLISH).replace('_', ' ')) + td(if (diagnostic.indexingTimes.wasFullIndexing) "Full" else "Partial") } } } @@ -379,7 +378,7 @@ fun JsonIndexDiagnostic.generateHtml(): String { if (times.indexingReason != null) { tr { td("Reason"); td(times.indexingReason) } } - tr { td("Type"); td(times.scanningType.name.lowercase(Locale.ENGLISH).replace('_', ' ')) } + tr { td("Full or partial"); td(if (times.wasFullIndexing) "full" else "partial") } 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 0e9e7a373ee0..12327dea2ad7 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-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// 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.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", ScanningType.FULL) + val history = ProjectIndexingHistoryImpl(DummyProject.getInstance(), "test", true) 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", ScanningType.FULL) + val history = ProjectIndexingHistoryImpl(DummyProject.getInstance(), "test", true) 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", ScanningType.FULL) + val history = ProjectIndexingHistoryImpl(DummyProject.getInstance(), "test", true) 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", ScanningType.FULL) + val history = ProjectIndexingHistoryImpl(DummyProject.getInstance(), "test", true) 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", ScanningType.FULL) + val history = ProjectIndexingHistoryImpl(DummyProject.getInstance(), "test", true) 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", ScanningType.FULL) + val history = ProjectIndexingHistoryImpl(DummyProject.getInstance(), "test", true) 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", ScanningType.FULL) + val history = ProjectIndexingHistoryImpl(DummyProject.getInstance(), "test", true) 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", ScanningType.FULL) + val history = ProjectIndexingHistoryImpl(DummyProject.getInstance(), "test", true) val instant = Instant.now() history.startStage(ProjectIndexingHistoryImpl.Stage.PushProperties, instant) history.suspendStages(instant.plusNanos(1)) diff --git a/platform/platform-api/src/com/intellij/ui/mac/screenmenu/Menu.java b/platform/platform-api/src/com/intellij/ui/mac/screenmenu/Menu.java index aef61e0ff058..38e69956aa57 100644 --- a/platform/platform-api/src/com/intellij/ui/mac/screenmenu/Menu.java +++ b/platform/platform-api/src/com/intellij/ui/mac/screenmenu/Menu.java @@ -1,6 +1,7 @@ // 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.ui.mac.screenmenu; +import com.intellij.DynamicBundle; import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.PathManager; @@ -17,6 +18,7 @@ import java.lang.reflect.Method; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; +import java.util.Set; @SuppressWarnings("NonPrivateFieldAccessedInSynchronizedContext") public class Menu extends MenuItem { @@ -50,6 +52,23 @@ public class Menu extends MenuItem { return ourAppMenu; } + public static void renameAppMenuItems(@Nullable DynamicBundle replacements) { + if (replacements == null) return; + + Set<String> keySet = replacements.getResourceBundle().keySet(); + if (keySet.isEmpty()) return; + + String replace[] = new String[keySet.size()*2]; + int c = 0; + for (String title: keySet) { + replace[c] = title; + replace[c + 1] = replacements.getMessage(title); + c += 2; + } + + nativeRenameAppMenuItems(replace); + } + public void setOnOpen(Runnable fillMenuProcedure, Component component) { this.myOnOpen = fillMenuProcedure; this.myComponent = component; @@ -264,6 +283,9 @@ public class Menu extends MenuItem { static private native long nativeGetAppMenu(); + static + private native void nativeRenameAppMenuItems(String[] replacements); + // // Static API // @@ -274,7 +296,7 @@ public class Menu extends MenuItem { IS_ENABLED = false; - if (!SystemInfo.isMac) return false; + if (!SystemInfo.isMacOSMojave) return false; if (!Boolean.getBoolean("jbScreenMenuBar.enabled")) return false; if (Boolean.getBoolean("apple.laf.useScreenMenuBar")) { Logger.getInstance(Menu.class).info("apple.laf.useScreenMenuBar==true, default screen menu implementation will be used"); diff --git a/platform/platform-impl/src/com/intellij/internal/statistic/collectors/fus/fileTypes/FileTypeUsageCounterCollector.java b/platform/platform-impl/src/com/intellij/internal/statistic/collectors/fus/fileTypes/FileTypeUsageCounterCollector.java index 62e9978d56e0..610189ae1226 100644 --- a/platform/platform-impl/src/com/intellij/internal/statistic/collectors/fus/fileTypes/FileTypeUsageCounterCollector.java +++ b/platform/platform-impl/src/com/intellij/internal/statistic/collectors/fus/fileTypes/FileTypeUsageCounterCollector.java @@ -67,6 +67,7 @@ public final class FileTypeUsageCounterCollector extends CounterUsagesCollector } private static final VarargEventId SELECT = registerFileTypeEvent("select"); + private static final VarargEventId CREATE_BY_NEW_FILE = registerFileTypeEvent("create_by_new_file"); private static final VarargEventId EDIT = registerFileTypeEvent("edit", FILE_EXTENSION_FIELD); private static final VarargEventId OPEN = registerFileTypeEvent( "open", FILE_EDITOR, EventFields.TimeToShowMs, EventFields.DurationMs, IS_WRITABLE, IS_IN_READER_MODE, FILE_EXTENSION_FIELD @@ -86,6 +87,10 @@ public final class FileTypeUsageCounterCollector extends CounterUsagesCollector } } + public static void triggerCreate(@NotNull Project project, @NotNull VirtualFile file) { + log(CREATE_BY_NEW_FILE, project, file, false); + } + public static void triggerOpen(@NotNull Project project, @NotNull FileEditorManager source, @NotNull VirtualFile file, @Nullable Long openStartedNs) { long timeToShow = openStartedNs != null ? TimeoutUtil.getDurationMillis(openStartedNs) : -1; diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeMenuBar.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeMenuBar.java index 859b4f9c954b..2548f034ce24 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeMenuBar.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeMenuBar.java @@ -378,18 +378,8 @@ public class IdeMenuBar extends JMenuBar implements IdeEventQueue.EventDispatche private void updateAppMenu() { if (!Menu.isJbScreenMenuEnabled()) return; - final Menu appMenu = Menu.getAppMenu(); - // 1. rename with localized - final DynamicBundle bundle = new DynamicBundle("messages.MacAppMenuBundle"); - for (String title: bundle.getResourceBundle().keySet()) { - String localizedTitle = bundle.getMessage(title); - MenuItem item = appMenu.findItemByTitle(title); - if (item != null) { - item.setLabel(localizedTitle); - item.dispose(); // must always dispose java-wrapper for native NSMenuItem after usage - } - } + Menu.renameAppMenuItems(new DynamicBundle("messages.MacAppMenuBundle")); // // 2. add custom new items in AppMenu 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 29304c37706f..f7a672bd966f 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-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// 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.project import com.intellij.openapi.application.ApplicationManager @@ -21,7 +21,6 @@ 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 @@ -172,7 +171,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", ScanningType.PARTIAL)) + indicator, new ProjectIndexingHistoryImpl(getProject(), "Testing", false)) } } catch (ProcessCanceledException e) { 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 7af410828c16..f78a77c29cdf 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-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// 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.util.io; import com.intellij.openapi.application.ApplicationManager; @@ -15,7 +15,6 @@ 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; @@ -109,7 +108,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", ScanningType.PARTIAL)); + new EmptyProgressIndicator(), new ProjectIndexingHistoryImpl(getProject(), "Testing", false)); } for (Future<Boolean> future : futures) { assertTrue(future.get()); diff --git a/platform/remoteDev-util/src/com/intellij/remoteDev/downloader/JetBrainsClientDownloaderConfigurationProvider.kt b/platform/remoteDev-util/src/com/intellij/remoteDev/downloader/JetBrainsClientDownloaderConfigurationProvider.kt index c58c22ab19b9..70913f632f8e 100644 --- a/platform/remoteDev-util/src/com/intellij/remoteDev/downloader/JetBrainsClientDownloaderConfigurationProvider.kt +++ b/platform/remoteDev-util/src/com/intellij/remoteDev/downloader/JetBrainsClientDownloaderConfigurationProvider.kt @@ -42,8 +42,8 @@ interface JetBrainsClientDownloaderConfigurationProvider { class RealJetBrainsClientDownloaderConfigurationProvider : JetBrainsClientDownloaderConfigurationProvider { override fun modifyClientCommandLine(clientCommandLine: GeneralCommandLine) { } - override val clientDownloadLocation: URI = URI("https://cache-redirector.jetbrains.com/download.jetbrains.com/idea/code-with-me/") - override val jreDownloadLocation: URI = URI("https://cache-redirector.jetbrains.com/download.jetbrains.com/idea/jbr/") + override val clientDownloadLocation: URI = URI("https://download.jetbrains.com/idea/code-with-me/") + override val jreDownloadLocation: URI = URI("https://download.jetbrains.com/idea/jbr/") override val clientCachesDir: Path = getJetBrainsSystemCachesDir() / "JetBrainsClientDist" override val verifySignature: Boolean = true @@ -70,8 +70,8 @@ class TestJetBrainsClientDownloaderConfigurationProvider : JetBrainsClientDownlo } } - override var clientDownloadLocation: URI = URI("https://cache-redirector.jetbrains.com/download.jetbrains.com/idea/code-with-me/") - override var jreDownloadLocation: URI = URI("https://cache-redirector.jetbrains.com/download.jetbrains.com/idea/jbr/") + override var clientDownloadLocation: URI = URI("https://download.jetbrains.com/idea/code-with-me/") + override var jreDownloadLocation: URI = URI("https://download.jetbrains.com/idea/jbr/") override var clientCachesDir: Path = Files.createTempDirectory("") override var verifySignature: Boolean = true @@ -179,4 +179,4 @@ class TestJetBrainsClientDownloaderConfigurationProvider : JetBrainsClientDownlo serveFile(clientDistribution) serveFile(clientJdkBuildTxt) } -}
\ No newline at end of file +} diff --git a/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralSearchTest.java b/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralSearchTest.java index 4896b1692e93..3df8216b10db 100644 --- a/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralSearchTest.java +++ b/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralSearchTest.java @@ -300,6 +300,15 @@ public class StructuralSearchTest extends StructuralSearchTestCase { assertEquals("match literal by value", 1, findMatchesCount(s3, "32")); assertEquals("match char with substitution", 3, findMatchesCount(s3, "\\''_x\\'")); assertEquals("string literal should not match char", 0, findMatchesCount(s3, "\"a\"")); + + String s4 = "class X {" + + " String s = \"\\n\";" + + " String t = \" \";" + + " String u = \" \";" + + " String v = \"\";" + + "}"; + assertEquals("match empty string", 1, findMatchesCount(s4, "\"\"")); + assertEquals("match space", 2, findMatchesCount(s4, "\" \"")); } public void testCovariantArraySearch() { diff --git a/platform/testFramework/src/com/intellij/testFramework/UsefulTestCase.java b/platform/testFramework/src/com/intellij/testFramework/UsefulTestCase.java index 8c4fee2092d8..11f0ccfc224b 100644 --- a/platform/testFramework/src/com/intellij/testFramework/UsefulTestCase.java +++ b/platform/testFramework/src/com/intellij/testFramework/UsefulTestCase.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.testFramework; import com.intellij.analytics.AndroidStudioAnalytics; @@ -76,32 +76,31 @@ import static org.junit.Assume.assumeTrue; /** * This class is compatible with both JUnit 3 and JUnit 4. To use JUnit 4, just annotate your test subclass - * with @RunWith(JUnit4.class) or any other (like Parametrized.class), and you are all set. - * - * Don't annotate the JUnit 3 setUp()/tearDown() methods as @Before/@After, and don't call them from other @Before/@After methods. - * Also don't define @Rule's calling runBare(), just subclassing this class (directly or indirectly) is enough. <p/> - * + * with {@code @RunWith(JUnit4.class)} or any other (like {@code Parametrized.class}), and you are all set. + * <p> + * Don't annotate the JUnit 3 {@linkplain #setUp()}/{@linkplain #tearDown()} methods as {@code @Before}/{@code @After}, and don't call them from other {@code @Before}/{@code @After} methods. + * <p> + * Don't define {@code @Rule}s calling {@linkplain #runBare()}, just subclassing this class (directly or indirectly) is enough. + * <p> * The execution order is the following: - * <pre{@code - * - * - (JUnit 4 only) #checkShouldRunTest(Description) that can be used to ignore tests with meaningful message - * - #shouldRunTest() is also called (both JUnit 3 and JUnit 4) - * - * - #setUp(), usually overridden so that it initializes classes in order from base to specific - * - * - (JUnit 4 only) any @Rule fields, then @Rule methods - * - (JUnit 4 only) any @Before methods - * - * - the testXxx() method (JUnit 3), or the @Test method (JUnit 4) - * - * - (JUnit 4 only) any @After methods - * - (JUnit 4 only) any @Rule methods, then @Rule fields, cleanup - * - * - #tearDown(), usually overridden in the reverse order: from specific to base - * } </pre> - * - * Note that @Rule, @Before and @After methods execute within the same context/thread as the @Test method, - * which may differ from how setUp()/tearDown() are executed. + * <ul> + * <li><em>(JUnit 4 only)</em> {@linkplain #checkShouldRunTest(Description)} that can be used to ignore tests with meaningful message + * <li>{@linkplain #shouldRunTest()} is also called (both JUnit 3 and JUnit 4) + * <li>{@linkplain #setUp()}, usually overridden so that it initializes classes in order from base to specific + * <ul> + * <li><em>(JUnit 4 only)</em> any {@code @Rule} fields, then {@code @Rule} methods + * <li><em>(JUnit 4 only)</em> any {@code @Before} methods + * <ul> + * <li>the testXxx() method (JUnit 3), or the {@code @Test} method (JUnit 4) + * </ul> + * <li><em>(JUnit 4 only)</em> any {@code @After} methods + * <li><em>(JUnit 4 only)</em> any {@code @Rule} methods, then {@code @Rule} fields, cleanup + * </ul> + * <li>{@linkplain #tearDown()}, usually overridden in the reverse order: from specific to base + * </ul> + * <p> + * Note that {@code @Rule}, {@code @Before} and {@code @After} methods execute within the same context/thread as the @Test method, + * which may differ from how {@linkplain #setUp()}/{@linkplain #tearDown()} are executed. */ public abstract class UsefulTestCase extends TestCase { public static final boolean IS_UNDER_TEAMCITY = System.getenv("TEAMCITY_VERSION") != null; diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/ModuleVcsDetector.kt b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/ModuleVcsDetector.kt index 56339620c747..b1a91a03739a 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/ModuleVcsDetector.kt +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/ModuleVcsDetector.kt @@ -136,27 +136,28 @@ internal class ModuleVcsDetector(private val project: Project) { when (change) { is EntityChange.Removed<ContentRootEntity> -> { removedUrls.add(change.entity.url) - addedUrls.remove(change.entity.url) } is EntityChange.Added<ContentRootEntity> -> { addedUrls.add(change.entity.url) - removedUrls.remove(change.entity.url) } is EntityChange.Replaced<ContentRootEntity> -> { if (change.oldEntity.url != change.newEntity.url) { removedUrls.add(change.oldEntity.url) - addedUrls.remove(change.oldEntity.url) - addedUrls.add(change.newEntity.url) - removedUrls.remove(change.newEntity.url) } } } } val fileManager = VirtualFileManager.getInstance() - val removed = removedUrls.mapNotNull { fileManager.findFileByUrl(it.url) }.filter { it.isDirectory } - val added = addedUrls.mapNotNull { fileManager.findFileByUrl(it.url) }.filter { it.isDirectory } + val removed = removedUrls + .filter { !addedUrls.contains(it) } // do not process 'modifications' of any kind + .mapNotNull { fileManager.findFileByUrl(it.url) } + .filter { it.isDirectory } + val added = addedUrls + .filter { !removedUrls.contains(it) } // do not process 'modifications' of any kind + .mapNotNull { fileManager.findFileByUrl(it.url) } + .filter { it.isDirectory } if (added.isNotEmpty() && vcsManager.haveDefaultMapping() == null) { synchronized(dirtyContentRoots) { diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/details/commit/CommitDetailsPanel.kt b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/details/commit/CommitDetailsPanel.kt index bfcf7e99b2e9..538379d4e3fe 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/details/commit/CommitDetailsPanel.kt +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/details/commit/CommitDetailsPanel.kt @@ -7,7 +7,6 @@ import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.DefaultActionGroup import com.intellij.openapi.actionSystem.impl.ActionToolbarImpl import com.intellij.openapi.project.DumbAwareAction -import com.intellij.openapi.ui.VerticalFlowLayout import com.intellij.openapi.ui.popup.Balloon import com.intellij.openapi.util.registry.Registry import com.intellij.openapi.util.text.HtmlChunk @@ -38,6 +37,7 @@ class CommitDetailsPanel @JvmOverloads constructor(navigate: (CommitId) -> Unit const val SIDE_BORDER = 14 const val INTERNAL_BORDER = 10 const val EXTERNAL_BORDER = 14 + const val VCS_LOG_DESCRIPTION_MIN_WIDTH = 40 } private val statusesActionGroup = DefaultActionGroup() @@ -65,7 +65,7 @@ class CommitDetailsPanel @JvmOverloads constructor(navigate: (CommitId) -> Unit isOpaque = false val mainPanel = JPanel(null).apply { - layout = VerticalFlowLayout(VerticalFlowLayout.TOP, 0, 0, true, false) + layout = MigLayout(LC().gridGap("0", "0").insets("0").fill().flowY()) isOpaque = false val metadataPanel = BorderLayoutPanel().apply { @@ -75,11 +75,12 @@ class CommitDetailsPanel @JvmOverloads constructor(navigate: (CommitId) -> Unit addToCenter(hashAndAuthorPanel) } - add(messagePanel) - add(metadataPanel) - add(branchesPanel) - add(tagsPanel) - add(containingBranchesPanel) + val componentLayout = CC().minWidth("$VCS_LOG_DESCRIPTION_MIN_WIDTH").grow().push() + add(messagePanel, componentLayout) + add(metadataPanel, componentLayout) + add(branchesPanel, componentLayout) + add(tagsPanel, componentLayout) + add(containingBranchesPanel, componentLayout) } add(mainPanel, CC().grow().push()) @@ -232,7 +233,8 @@ private class ContainingBranchesPanel : HtmlPanel() { override fun getBody(): String { val insets = insets - val text = getBranchesText(branches, expanded, width - insets.left - insets.right, getFontMetrics(bodyFont)) + val availableWidth = width - insets.left - insets.right + val text = getBranchesText(branches, expanded, availableWidth, getFontMetrics(bodyFont)) return if (expanded) text else HtmlChunk.raw(text).wrapWith("nobr").toString() } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/numeric/UnpredictableBigDecimalConstructorCallInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/numeric/UnpredictableBigDecimalConstructorCallInspection.java index c1a27c740625..de3f49d9c62d 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/numeric/UnpredictableBigDecimalConstructorCallInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/numeric/UnpredictableBigDecimalConstructorCallInspection.java @@ -7,6 +7,7 @@ import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel; import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.util.PsiUtil; +import com.intellij.util.text.LiteralFormatUtil; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; @@ -65,7 +66,7 @@ public class UnpredictableBigDecimalConstructorCallInspection extends BaseInspec } static String getLiteralText(PsiLiteralExpression firstArgument) { - final String text = firstArgument.getText(); + final String text = LiteralFormatUtil.removeUnderscores(firstArgument.getText()); final char c = text.charAt(text.length() - 1); return c == 'd' || c == 'D' || c == 'f' || c == 'F' ? text.substring(0, text.length() - 1) diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/numeric/unpredictable_big_decimal/Underscores.after.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/numeric/unpredictable_big_decimal/Underscores.after.java new file mode 100644 index 000000000000..7e035d3d809c --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/numeric/unpredictable_big_decimal/Underscores.after.java @@ -0,0 +1,6 @@ +import java.math.BigDecimal; + +class Underscores { + + BigDecimal bd = new BigDecimal("1000.1"); +}
\ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/numeric/unpredictable_big_decimal/Underscores.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/numeric/unpredictable_big_decimal/Underscores.java new file mode 100644 index 000000000000..7cfdafc41a57 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/numeric/unpredictable_big_decimal/Underscores.java @@ -0,0 +1,6 @@ +import java.math.BigDecimal; + +class Underscores { + + BigDecimal bd = new <caret>BigDecimal(1_000.1d); +}
\ No newline at end of file diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/numeric/UnpredictableBigDecimalConstructorCallFixTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/numeric/UnpredictableBigDecimalConstructorCallFixTest.java index 1de7059b78a3..ce3e12b367aa 100644 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/numeric/UnpredictableBigDecimalConstructorCallFixTest.java +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/numeric/UnpredictableBigDecimalConstructorCallFixTest.java @@ -1,4 +1,4 @@ -// Copyright 2000-2019 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.siyeh.ig.fixes.numeric; import com.intellij.codeInspection.CommonQuickFixBundle; @@ -40,4 +40,8 @@ public class UnpredictableBigDecimalConstructorCallFixTest extends IGQuickFixesT public void testLiteral() { doTest(CommonQuickFixBundle.message("fix.replace.with.x", "new BigDecimal(\"2\")")); } + + public void testUnderscores() { + doTest(CommonQuickFixBundle.message("fix.replace.with.x", "new BigDecimal(\"1000.1\")")); + } } diff --git a/plugins/devkit/devkit-core/resources/fileTemplates/j2ee/devkit-build.gradle.kts.ft b/plugins/devkit/devkit-core/resources/fileTemplates/j2ee/devkit-build.gradle.kts.ft index 92c6c3ff1ea8..095d8b379afa 100644 --- a/plugins/devkit/devkit-core/resources/fileTemplates/j2ee/devkit-build.gradle.kts.ft +++ b/plugins/devkit/devkit-core/resources/fileTemplates/j2ee/devkit-build.gradle.kts.ft @@ -13,7 +13,8 @@ repositories { mavenCentral() } -// Configure Gradle IntelliJ Plugin - read more: https://github.com/JetBrains/gradle-intellij-plugin +// Configure Gradle IntelliJ Plugin +// Read more: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html intellij { version.set("${context.getBomProperty("intellij.version")}") type.set("IC") // Target IDE Platform diff --git a/plugins/git4idea/src/git4idea/checkin/GitSkipHooksCommitHandlerFactory.kt b/plugins/git4idea/src/git4idea/checkin/GitSkipHooksCommitHandlerFactory.kt index 1ccbda46cf20..ba10b15dcb13 100644 --- a/plugins/git4idea/src/git4idea/checkin/GitSkipHooksCommitHandlerFactory.kt +++ b/plugins/git4idea/src/git4idea/checkin/GitSkipHooksCommitHandlerFactory.kt @@ -3,7 +3,6 @@ package git4idea.checkin import com.intellij.openapi.util.Key import com.intellij.openapi.vcs.CheckinProjectPanel -import com.intellij.openapi.vcs.ProjectLevelVcsManager import com.intellij.openapi.vcs.changes.CommitContext import com.intellij.openapi.vcs.changes.LocalChangeList import com.intellij.openapi.vcs.checkin.CheckinChangeListSpecificComponent @@ -13,7 +12,6 @@ import com.intellij.openapi.vcs.ui.RefreshableOnComponent import com.intellij.ui.NonFocusableCheckBox import com.intellij.util.ui.JBUI import com.intellij.vcs.commit.commitProperty -import git4idea.GitUtil.getRepositoryManager import git4idea.GitVcs import git4idea.i18n.GitBundle import git4idea.repo.GitRepository @@ -46,33 +44,30 @@ private class GitSkipHooksConfigurationPanel( ) : RefreshableOnComponent, CheckinChangeListSpecificComponent { - private val vcs = GitVcs.getInstance(panel.project) + private val repositoryManager get() = GitRepositoryManager.getInstance(panel.project) private val runHooks = NonFocusableCheckBox(GitBundle.message("checkbox.run.git.hooks")).apply { + isSelected = true mnemonic = KeyEvent.VK_H toolTipText = GitBundle.message("tooltip.run.git.hooks") } - private var selectedState = true override fun getComponent(): JComponent = JBUI.Panels.simplePanel(runHooks) + private fun refreshAvailability() { + runHooks.isVisible = repositoryManager.repositories.any { it.hasCommitHooks() } + } + override fun onChangeListSelected(list: LocalChangeList) { - if (runHooks.isEnabled) selectedState = runHooks.isSelected - val affectedGitRoots = panel.roots.intersect(setOf(*ProjectLevelVcsManager.getInstance(panel.project).getRootsUnderVcs(vcs))) - val repositoryManager = GitRepositoryManager.getInstance(panel.project) - runHooks.isEnabled = affectedGitRoots.any { repositoryManager.getRepositoryForRootQuick(it)?.hasCommitHooks() == true } - runHooks.isSelected = if (runHooks.isEnabled) selectedState else false + refreshAvailability() } override fun saveState() { - commitContext.isSkipHooks = shouldSkipHook() + commitContext.isSkipHooks = runHooks.isVisible && !runHooks.isSelected } override fun restoreState() { - runHooks.isVisible = getRepositoryManager(panel.project).repositories.any { it.hasCommitHooks() } - runHooks.isSelected = true + refreshAvailability() } - private fun shouldSkipHook() = runHooks.isVisible && !runHooks.isSelected - private fun GitRepository.hasCommitHooks() = info.hooksInfo.areCommitHooksAvailable } diff --git a/plugins/grazie/src/main/kotlin/com/intellij/grazie/grammar/LanguageToolChecker.kt b/plugins/grazie/src/main/kotlin/com/intellij/grazie/grammar/LanguageToolChecker.kt index 024b3d87577d..5148c64e799b 100644 --- a/plugins/grazie/src/main/kotlin/com/intellij/grazie/grammar/LanguageToolChecker.kt +++ b/plugins/grazie/src/main/kotlin/com/intellij/grazie/grammar/LanguageToolChecker.kt @@ -130,7 +130,8 @@ open class LanguageToolChecker : TextChecker() { private val logger = LoggerFactory.getLogger(LanguageToolChecker::class.java) private val interner = Interner.createWeakInterner<String>() private val sentenceSeparationRules = setOf("LC_AFTER_PERIOD", "PUNT_GEEN_HL", "KLEIN_NACH_PUNKT") - private val openClosedRegexp = Regex("[\\[(].+(\\.\\.|:|,).+[])]") + private val openClosedRangeStart = Regex("[\\[(].+?(\\.\\.|:|,).+[])]") + private val openClosedRangeEnd = Regex(".*" + openClosedRangeStart.pattern) internal fun grammarRules(tool: JLanguageTool, lang: Lang): List<LanguageToolRule> { return tool.allRules.asSequence() @@ -174,10 +175,11 @@ open class LanguageToolChecker : TextChecker() { } // https://github.com/languagetool-org/languagetool/issues/6566 + @OptIn(ExperimentalStdlibApi::class) private fun couldBeOpenClosedRange(text: TextContent, index: Int): Boolean { val unpaired = text[index] - return "([".contains(unpaired) && openClosedRegexp.find(text, index)?.range?.start == index || - ")]".contains(unpaired) && openClosedRegexp.findAll(text.subSequence(0, index + 1)).any { it.range.last == index } + return "([".contains(unpaired) && openClosedRangeStart.matchesAt(text, index) || + ")]".contains(unpaired) && openClosedRangeEnd.matches(text.subSequence(0, index + 1)) } // https://github.com/languagetool-org/languagetool/issues/5230 diff --git a/plugins/grazie/src/main/kotlin/com/intellij/grazie/text/TextContentBuilder.java b/plugins/grazie/src/main/kotlin/com/intellij/grazie/text/TextContentBuilder.java index fc74f70278fb..cd99ec822d5a 100644 --- a/plugins/grazie/src/main/kotlin/com/intellij/grazie/text/TextContentBuilder.java +++ b/plugins/grazie/src/main/kotlin/com/intellij/grazie/text/TextContentBuilder.java @@ -102,25 +102,32 @@ public class TextContentBuilder { @Nullable public TextContent build(PsiElement root, TextContent.TextDomain domain, TextRange valueRange) { - int rootStart = root.getTextRange().getStartOffset(); - String rootText = root.getText(); - if (isWrongRange(valueRange, rootText.length())) { - LOG.error("The range " + valueRange + " is out of the PSI element, length " + rootText.length()); + TextRange rootRange = root.getTextRange(); + if (isWrongRange(valueRange, rootRange.getLength())) { + LOG.error("The range " + valueRange + " is out of the PSI element, length " + rootRange.getLength()); + return null; + } + + int rootStart = rootRange.getStartOffset(); + TextRange fileValueRange = valueRange.shiftRight(rootStart); + CharSequence fileText = root.getContainingFile().getViewProvider().getContents(); + if (isWrongRange(fileValueRange, fileText.length())) { + LOG.error("The range " + fileValueRange + " is out of the file, length " + fileText.length()); return null; } TextContent content = new PsiRecursiveElementWalkingVisitor() { final List<TextContentImpl.TokenInfo> tokens = new ArrayList<>(); - int currentStart = valueRange.getStartOffset(); + int currentStart = fileValueRange.getStartOffset(); @Override public void visitElement(@NotNull PsiElement element) { - TextRange range = element.getTextRange().shiftLeft(rootStart).intersection(valueRange); + TextRange range = element.getTextRange().intersection(fileValueRange); if (range == null) return; if (unknown.test(element)) { exclusionStarted(range); - tokens.add(new TextContentImpl.PsiToken("", root, TextRange.from(range.getStartOffset(), 0), true)); + tokens.add(new TextContentImpl.PsiToken("", root, TextRange.from(range.getStartOffset(), 0).shiftLeft(rootStart), true)); } else if (excluded.test(element)) { exclusionStarted(range); @@ -133,14 +140,15 @@ public class TextContentBuilder { private void exclusionStarted(TextRange range) { if (range.getStartOffset() != currentStart) { TextRange tokenRange = new TextRange(currentStart, range.getStartOffset()); - tokens.add(new TextContentImpl.PsiToken(tokenRange.substring(rootText), root, tokenRange, false)); + String tokenText = tokenRange.subSequence(fileText).toString(); + tokens.add(new TextContentImpl.PsiToken(tokenText, root, tokenRange.shiftLeft(rootStart), false)); } currentStart = range.getEndOffset(); } @Nullable TextContent walkPsiTree() { root.accept(this); - exclusionStarted(TextRange.from(valueRange.getEndOffset(), 0)); + exclusionStarted(TextRange.from(fileValueRange.getEndOffset(), 0)); return tokens.isEmpty() ? null : new TextContentImpl(domain, tokens); } }.walkPsiTree(); diff --git a/plugins/grazie/src/main/kotlin/com/intellij/grazie/text/TextContentImpl.java b/plugins/grazie/src/main/kotlin/com/intellij/grazie/text/TextContentImpl.java index cacb0d0c66b6..3874b99039d6 100644 --- a/plugins/grazie/src/main/kotlin/com/intellij/grazie/text/TextContentImpl.java +++ b/plugins/grazie/src/main/kotlin/com/intellij/grazie/text/TextContentImpl.java @@ -456,5 +456,15 @@ class TextContentImpl extends UserDataHolderBase implements TextContent { static class WSTokenInfo extends TokenInfo { WSTokenInfo(char ws) { super(String.valueOf(ws)); } + + @Override + public boolean equals(Object obj) { + return obj instanceof WSTokenInfo && ((WSTokenInfo)obj).text.equals(text); + } + + @Override + public int hashCode() { + return text.hashCode(); + } } } diff --git a/plugins/grazie/src/test/kotlin/com/intellij/grazie/text/TextContentTest.java b/plugins/grazie/src/test/kotlin/com/intellij/grazie/text/TextContentTest.java index 7415b6f601f6..b5732abb5fd9 100644 --- a/plugins/grazie/src/test/kotlin/com/intellij/grazie/text/TextContentTest.java +++ b/plugins/grazie/src/test/kotlin/com/intellij/grazie/text/TextContentTest.java @@ -101,6 +101,8 @@ public class TextContentTest extends BasePlatformTestCase { assertEquals("| c|", unknownOffsets(joined.markUnknown(new TextRange(0, 1)))); assertEquals("c|", unknownOffsets(joined.excludeRange(new TextRange(0, 1)))); + + assertEquals(joined, TextContent.joinWithWhitespace(' ', List.of(f1, f2))); } public static String unknownOffsets(TextContent text) { 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 f0facee7550b..16ae0a34ac8f 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 @@ -15,6 +15,7 @@ import com.intellij.openapi.util.registry.Registry; import com.intellij.psi.*; import com.intellij.psi.impl.source.resolve.reference.impl.manipulators.StringLiteralManipulator; import com.intellij.psi.javadoc.PsiDocComment; +import com.intellij.psi.javadoc.PsiDocTag; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.xml.XmlTag; import com.intellij.testFramework.PlatformTestUtil; @@ -320,6 +321,21 @@ public class TextExtractionTest extends BasePlatformTestCase { }).assertTiming(); } + public void testBuildingPerformance_complexPsi() { + String link = "{@link foo.bar.goo1.goo2.goo3.goo4.goo5.goo6.goo7#zoo(x.x1.x2.x3,y.y1.y2.y3,z.z1.z2.z3)}"; + var text = "/** @return something if " + link.repeat(10_000) + " is not too expensive */"; + PsiFile file = myFixture.configureByText("a.java", text); + TextExtractor extractor = new JavaTextExtractor(); + PsiElement tag = PsiTreeUtil.findElementOfClassAtOffset(file, text.indexOf("something"), PsiDocTag.class, false); + PlatformTestUtil.startPerformanceTest("TextContent building from complex PSI", 400, () -> { + for (int i = 0; i < 10; i++) { + TextContent content = extractor.buildTextContent(tag, TextContent.TextDomain.ALL); + assertEquals("something if is not too expensive", content.toString()); + } + }).assertTiming(); + } + + public void testCachingWorks() { TextExtractor delegate = TextExtractor.EP.forLanguage(MarkdownLanguage.INSTANCE); var countingExtractor = new TextExtractor() { diff --git a/plugins/ide-features-trainer/src/training/featuresSuggester/settings/FeatureSuggesterSettings.kt b/plugins/ide-features-trainer/src/training/featuresSuggester/settings/FeatureSuggesterSettings.kt index 1ab5e7630d4c..6156bea8808b 100644 --- a/plugins/ide-features-trainer/src/training/featuresSuggester/settings/FeatureSuggesterSettings.kt +++ b/plugins/ide-features-trainer/src/training/featuresSuggester/settings/FeatureSuggesterSettings.kt @@ -1,7 +1,9 @@ +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package training.featuresSuggester.settings import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.components.PersistentStateComponent +import com.intellij.openapi.components.RoamingType import com.intellij.openapi.components.State import com.intellij.openapi.components.Storage import com.intellij.openapi.diagnostic.thisLogger @@ -15,7 +17,7 @@ import kotlin.math.min @State( name = "FeatureSuggesterSettings", - storages = [Storage("FeatureSuggester.xml")] + storages = [Storage("FeatureSuggester.xml", roamingType = RoamingType.DISABLED)] ) class FeatureSuggesterSettings : PersistentStateComponent<FeatureSuggesterSettings> { var suggesters: MutableMap<String, Boolean> = run { diff --git a/plugins/package-search/src/com/jetbrains/packagesearch/intellij/plugin/extensibility/PackageVersionRangeInspection.kt b/plugins/package-search/src/com/jetbrains/packagesearch/intellij/plugin/extensibility/PackageVersionRangeInspection.kt index ceead934202f..b913f81a2aa7 100644 --- a/plugins/package-search/src/com/jetbrains/packagesearch/intellij/plugin/extensibility/PackageVersionRangeInspection.kt +++ b/plugins/package-search/src/com/jetbrains/packagesearch/intellij/plugin/extensibility/PackageVersionRangeInspection.kt @@ -9,23 +9,17 @@ import com.jetbrains.packagesearch.intellij.plugin.util.packageSearchProjectServ abstract class PackageVersionRangeInspection : AbstractPackageUpdateInspectionCheck() { - companion object { - - private fun isRange(version: String) = version.any { !it.isLetter() && !it.isDigit() && it != '_' && it != '.' && it != '-' } - } - override fun ProblemsHolder.checkFile(file: PsiFile, fileModule: Module) { file.project.packageSearchProjectService.dependenciesByModuleStateFlow.value .entries .find { it.key.nativeModule == fileModule } ?.value - ?.filter { dependency -> dependency.coordinates.version?.let { isRange(it) } ?: false } + ?.filter { dependency -> dependency.coordinates.version?.let { isIvyRange(it) } ?: false } ?.mapNotNull { coordinates -> runCatching { getVersionPsiElement(file, coordinates) }.getOrNull() ?.let { coordinates to it } } ?.forEach { (dependency, psiElement) -> - val message = dependency.coordinates.version ?.let { PackageSearchBundle.message("packagesearch.inspection.upgrade.range.withVersion", it) } ?: PackageSearchBundle.message("packagesearch.inspection.upgrade.range") @@ -33,4 +27,18 @@ abstract class PackageVersionRangeInspection : AbstractPackageUpdateInspectionCh registerProblem(psiElement, message, ProblemHighlightType.WEAK_WARNING) } } -}
\ No newline at end of file + + private fun isIvyRange(version: String): Boolean { + // See https://ant.apache.org/ivy/history/2.1.0/ivyfile/dependency.html + val normalizedVersion = version.trimEnd() + if (normalizedVersion.endsWith('+')) return true + + if (normalizedVersion.startsWith("latest.")) return true + + val startsWithParenthesisOrBrackets = normalizedVersion.startsWith('(') || normalizedVersion.startsWith('[') + val endsWithParenthesisOrBrackets = normalizedVersion.endsWith(')') || normalizedVersion.endsWith(']') + if (startsWithParenthesisOrBrackets && endsWithParenthesisOrBrackets) return true + + return false + } +} diff --git a/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/run/XsltConfigurationProducer.java b/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/run/XsltConfigurationProducer.java index 5daa71464bee..1541c5bf1808 100644 --- a/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/run/XsltConfigurationProducer.java +++ b/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/run/XsltConfigurationProducer.java @@ -2,12 +2,9 @@ package org.intellij.lang.xpath.xslt.run; -import com.intellij.execution.RunManager; -import com.intellij.execution.RunnerAndConfigurationSettings; import com.intellij.execution.actions.ConfigurationContext; import com.intellij.execution.actions.LazyRunConfigurationProducer; import com.intellij.execution.configurations.ConfigurationFactory; -import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Ref; import com.intellij.psi.PsiElement; import com.intellij.psi.util.PsiTreeUtil; @@ -28,10 +25,7 @@ public class XsltConfigurationProducer extends LazyRunConfigurationProducer<Xslt if (file == null) { return false; } - final Project project = file.getProject(); - final RunnerAndConfigurationSettings settings = - RunManager.getInstance(project).createConfiguration(file.getName(), getConfigurationFactory()); - ((XsltRunConfiguration)settings.getConfiguration()).initFromFile(file); + configuration.initFromFile(file); return true; } diff --git a/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/run/XsltRunConfiguration.java b/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/run/XsltRunConfiguration.java index acf6a7d0affc..a58676104337 100644 --- a/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/run/XsltRunConfiguration.java +++ b/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/run/XsltRunConfiguration.java @@ -479,6 +479,7 @@ public final class XsltRunConfiguration extends LocatableConfigurationBase imple public XsltRunConfiguration initFromFile(@NotNull XmlFile file) { assert XsltSupport.isXsltFile(file) : "Not an XSLT file: " + file.getName(); mySuggestedName = file.getName(); + setName(mySuggestedName); final VirtualFile virtualFile = file.getVirtualFile(); assert virtualFile != null : "No VirtualFile for " + file.getName(); diff --git a/python/ideCoreSrc/idea/PyCharmCoreApplicationInfo.xml b/python/ideCoreSrc/idea/PyCharmCoreApplicationInfo.xml index 404b5c2b7cb7..5693151fde5c 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.3" suffix="RC" eap="false"/> + <version suffix="RC" major="2022" minor="1.4" 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/uast/uast-common/src/org/jetbrains/uast/UastUtils.kt b/uast/uast-common/src/org/jetbrains/uast/UastUtils.kt index 38ead72a07a9..0a9adda0226c 100644 --- a/uast/uast-common/src/org/jetbrains/uast/UastUtils.kt +++ b/uast/uast-common/src/org/jetbrains/uast/UastUtils.kt @@ -27,7 +27,7 @@ fun <T : UElement> UElement.getParentOfType(parentClass: Class<out T>, strict: B } fun UElement.skipParentOfType(strict: Boolean, vararg parentClasses: Class<out UElement>): UElement? { - var element = (if (strict) uastParent else this) ?: return null + var element = (if (strict) uastParent else this) ?: return null while (true) { if (!parentClasses.any { it.isInstance(element) }) { return element @@ -143,9 +143,9 @@ fun UElement.tryResolveNamed(): PsiNamedElement? = (this as? UResolvable)?.resol fun UReferenceExpression?.getQualifiedName(): String? = (this?.resolve() as? PsiClass)?.qualifiedName /** - * Returns the String expression value, or null if the value can't be calculated or if the calculated value is not a String. + * Returns the String expression value, or null if the value can't be calculated, or if the calculated value is not a String or an integral literal. */ -fun UExpression.evaluateString(): String? = evaluate() as? String +fun UExpression.evaluateString(): String? = evaluate().takeIf { it is String || isIntegralLiteral() }?.toString() fun UExpression.skipParenthesizedExprDown(): UExpression? { var expression = this @@ -231,7 +231,7 @@ tailrec fun UElement.isLastElementInControlFlow(scopeElement: UElement? = null): } fun UNamedExpression.getAnnotationMethod(): PsiMethod? { - val annotation : UAnnotation? = getParentOfType(UAnnotation::class.java, true) + val annotation: UAnnotation? = getParentOfType(UAnnotation::class.java, true) val fqn = annotation?.qualifiedName ?: return null val annotationSrc = annotation.sourcePsi if (annotationSrc == null) return null |