From e47205bebe3ad70ea818a75db07b34ee8a4573e4 Mon Sep 17 00:00:00 2001 From: Tomasz Pasternak Date: Fri, 16 Feb 2024 15:40:20 +0100 Subject: [PATCH] perf: avoid scanning convenience symlink directories during initial import Prior to this change, IntelliJ excluded convenience symlinks by using the `addExcludeFolder` method. Its drawback was that it didn't work perfectly when the file didn't exist during exclusion. This might cause a race condition where some IntelliJ indexers detected new file creation in these directories while the exclusion mechanism hadn't detected the existence of the symlinks themselves. This issue is addressed by using 'exclusion patterns' instead of 'excluded folders'. Patterns do not require the files to actually exist. However, their drawback is that they will catch files named `bazel-bin` and `bazel-out` nested deep inside the directory hierarchy, not just the top-level ones. To address this, a project view setting has been added to disable the new behavior. Fixes #6082 --- .../AutomaticallyDeriveTargetsSection.java | 46 ++----------- .../sections/BooleanSectionParser.java | 65 +++++++++++++++++++ .../section/sections/Sections.java | 3 +- .../sections/UseExclusionPatternsSection.java | 41 ++++++++++++ .../projectstructure/ContentEntryEditor.java | 11 +++- 5 files changed, 121 insertions(+), 45 deletions(-) create mode 100644 base/src/com/google/idea/blaze/base/projectview/section/sections/BooleanSectionParser.java create mode 100644 base/src/com/google/idea/blaze/base/projectview/section/sections/UseExclusionPatternsSection.java diff --git a/base/src/com/google/idea/blaze/base/projectview/section/sections/AutomaticallyDeriveTargetsSection.java b/base/src/com/google/idea/blaze/base/projectview/section/sections/AutomaticallyDeriveTargetsSection.java index 7a1a80c1ad9..338f2489f9f 100644 --- a/base/src/com/google/idea/blaze/base/projectview/section/sections/AutomaticallyDeriveTargetsSection.java +++ b/base/src/com/google/idea/blaze/base/projectview/section/sections/AutomaticallyDeriveTargetsSection.java @@ -17,58 +17,20 @@ import com.google.idea.blaze.base.projectview.ProjectView; import com.google.idea.blaze.base.projectview.ProjectViewSet; -import com.google.idea.blaze.base.projectview.parser.ParseContext; -import com.google.idea.blaze.base.projectview.parser.ProjectViewParser; import com.google.idea.blaze.base.projectview.section.ProjectViewDefaultValueProvider; import com.google.idea.blaze.base.projectview.section.ScalarSection; -import com.google.idea.blaze.base.projectview.section.ScalarSectionParser; import com.google.idea.blaze.base.projectview.section.SectionKey; import com.google.idea.blaze.base.projectview.section.SectionParser; import com.google.idea.blaze.base.settings.BuildSystemName; -import javax.annotation.Nullable; /** If set to true, automatically derives targets from the project directories. */ public class AutomaticallyDeriveTargetsSection { public static final SectionKey> KEY = SectionKey.of("derive_targets_from_directories"); - public static final SectionParser PARSER = new AutomaticallyDeriveTargetsSectionParser(); - - private static class AutomaticallyDeriveTargetsSectionParser - extends ScalarSectionParser { - AutomaticallyDeriveTargetsSectionParser() { - super(KEY, ':'); - } - - @Override - @Nullable - protected Boolean parseItem(ProjectViewParser parser, ParseContext parseContext, String text) { - if (text.equals("true")) { - return true; - } - if (text.equals("false")) { - return false; - } - parseContext.addError( - "'derive_targets_from_directories' must be set to 'true' or 'false' (e.g." - + " 'derive_targets_from_directories: true')"); - return null; - } - - @Override - protected void printItem(StringBuilder sb, Boolean item) { - sb.append(item); - } - - @Override - public ItemType getItemType() { - return ItemType.Other; - } - - @Override - public String quickDocs() { - return "If set to true, project targets will be derived from the directories."; - } - } + public static final SectionParser PARSER = new BooleanSectionParser( + KEY, + "If set to true, project targets will be derived from the directories." + ); static class DefaultValueProvider implements ProjectViewDefaultValueProvider { @Override diff --git a/base/src/com/google/idea/blaze/base/projectview/section/sections/BooleanSectionParser.java b/base/src/com/google/idea/blaze/base/projectview/section/sections/BooleanSectionParser.java new file mode 100644 index 00000000000..11b0509edea --- /dev/null +++ b/base/src/com/google/idea/blaze/base/projectview/section/sections/BooleanSectionParser.java @@ -0,0 +1,65 @@ +/* + * Copyright 2024 The Bazel Authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.idea.blaze.base.projectview.section.sections; + +import com.google.idea.blaze.base.projectview.parser.ParseContext; +import com.google.idea.blaze.base.projectview.parser.ProjectViewParser; +import com.google.idea.blaze.base.projectview.section.ScalarSection; +import com.google.idea.blaze.base.projectview.section.ScalarSectionParser; +import com.google.idea.blaze.base.projectview.section.SectionKey; + +import javax.annotation.Nullable; + +class BooleanSectionParser + extends ScalarSectionParser { + private final String quickDocs; + + BooleanSectionParser(SectionKey> key, String quickDocs) { + super(key, ':'); + this.quickDocs = quickDocs; + } + + @Override + @Nullable + protected Boolean parseItem(ProjectViewParser parser, ParseContext parseContext, String text) { + if (text.equals("true")) { + return true; + } + if (text.equals("false")) { + return false; + } + String key = super.getSectionKey().getName(); + parseContext.addError( + "'" + key + "' must be set to 'true' or 'false' (e.g." + + " '" + key + ": true')"); + return null; + } + + @Override + protected void printItem(StringBuilder sb, Boolean item) { + sb.append(item); + } + + @Override + public ItemType getItemType() { + return ItemType.Other; + } + + @Override + public String quickDocs() { + return quickDocs; + } +} diff --git a/base/src/com/google/idea/blaze/base/projectview/section/sections/Sections.java b/base/src/com/google/idea/blaze/base/projectview/section/sections/Sections.java index 5aea4db980c..9e5da256549 100644 --- a/base/src/com/google/idea/blaze/base/projectview/section/sections/Sections.java +++ b/base/src/com/google/idea/blaze/base/projectview/section/sections/Sections.java @@ -46,7 +46,8 @@ public class Sections { ShardBlazeBuildsSection.PARSER, TargetShardSizeSection.PARSER, BazelBinarySection.PARSER, - BuildConfigSection.PARSER); + BuildConfigSection.PARSER, + UseExclusionPatternsSection.PARSER); public static List getParsers() { List parsers = Lists.newArrayList(PARSERS); diff --git a/base/src/com/google/idea/blaze/base/projectview/section/sections/UseExclusionPatternsSection.java b/base/src/com/google/idea/blaze/base/projectview/section/sections/UseExclusionPatternsSection.java new file mode 100644 index 00000000000..e9ef075108b --- /dev/null +++ b/base/src/com/google/idea/blaze/base/projectview/section/sections/UseExclusionPatternsSection.java @@ -0,0 +1,41 @@ +/* + * Copyright 2024 The Bazel Authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.idea.blaze.base.projectview.section.sections; + +import com.google.idea.blaze.base.projectview.section.ScalarSection; +import com.google.idea.blaze.base.projectview.section.SectionKey; +import com.google.idea.blaze.base.projectview.section.SectionParser; + +/** 'use_exclusion_pattern' section. */ +public class UseExclusionPatternsSection { + public static final SectionKey> KEY = + SectionKey.of("use_exclusion_patterns"); + + public static final SectionParser PARSER = new BooleanSectionParser( + KEY, + """ + Add an exclusion pattern for all directories that are supposed to be excluded from the project. + It might significantly improve first import performance, but if your project contains files + that are typically ignored (`bazel-bin`, `bazel-out`, `.ijwb` etc.) they will be excluded, even + if they do noy lay in the root directory. In these cases, please disable exclusion patterns (set + to false). By default the exclusion patterns are enabled. For more info please check + https://www.jetbrains.com/help/idea/content-roots.html#exclude_folders + """); + + +} + + diff --git a/base/src/com/google/idea/blaze/base/sync/projectstructure/ContentEntryEditor.java b/base/src/com/google/idea/blaze/base/sync/projectstructure/ContentEntryEditor.java index 8b4a25d51e4..9be0fe1db3c 100644 --- a/base/src/com/google/idea/blaze/base/sync/projectstructure/ContentEntryEditor.java +++ b/base/src/com/google/idea/blaze/base/sync/projectstructure/ContentEntryEditor.java @@ -22,6 +22,7 @@ import com.google.idea.blaze.base.model.primitives.WorkspacePath; import com.google.idea.blaze.base.model.primitives.WorkspaceRoot; import com.google.idea.blaze.base.projectview.ProjectViewSet; +import com.google.idea.blaze.base.projectview.section.sections.UseExclusionPatternsSection; import com.google.idea.blaze.base.settings.Blaze; import com.google.idea.blaze.base.sync.SourceFolderProvider; import com.google.idea.blaze.base.sync.data.BlazeDataStorage; @@ -68,8 +69,14 @@ public static void createContentEntries( modifiableRootModel.addContentEntry(UrlUtil.pathToUrl(rootFile.getPath())); for (WorkspacePath exclude : excludesByRootDirectory.get(rootDirectory)) { - File excludeFolder = workspaceRoot.fileForPath(exclude); - contentEntry.addExcludeFolder(UrlUtil.fileToIdeaUrl(excludeFolder)); + if (projectViewSet.getScalarValue(UseExclusionPatternsSection.KEY).orElse(true) + && !exclude.asPath().isAbsolute() + && exclude.asPath().getNameCount() == 1) { + contentEntry.addExcludePattern(exclude.relativePath()); + } else { + File excludeFolder = workspaceRoot.fileForPath(exclude); + contentEntry.addExcludeFolder(UrlUtil.fileToIdeaUrl(excludeFolder)); + } } File directory = new File(workspaceRoot.toString());