diff --git a/RegExpSupport/resources/META-INF/RegExpPlugin.xml b/RegExpSupport/resources/META-INF/RegExpPlugin.xml index eb7ef09a7bb4..beb81686cfde 100644 --- a/RegExpSupport/resources/META-INF/RegExpPlugin.xml +++ b/RegExpSupport/resources/META-INF/RegExpPlugin.xml @@ -20,7 +20,7 @@ - + diff --git a/RegExpSupport/src/org/intellij/lang/regexp/RegExpSupportLoader.java b/RegExpSupport/src/org/intellij/lang/regexp/RegExpSupportLoader.java deleted file mode 100644 index 4a9964dc7c6c..000000000000 --- a/RegExpSupport/src/org/intellij/lang/regexp/RegExpSupportLoader.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2006 Sascha Weinreuter - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.intellij.lang.regexp; - -import com.intellij.openapi.fileTypes.FileTypeConsumer; -import com.intellij.openapi.fileTypes.FileTypeFactory; -import org.jetbrains.annotations.NotNull; - -public class RegExpSupportLoader extends FileTypeFactory { - public static final RegExpLanguage LANGUAGE = RegExpLanguage.INSTANCE; - public static final RegExpFileType FILE_TYPE = RegExpFileType.INSTANCE; - - @Override - public void createFileTypes(final @NotNull FileTypeConsumer consumer) { - consumer.consume(FILE_TYPE, FILE_TYPE.getDefaultExtension()); - } -} diff --git a/build/dependencies/gradle.properties b/build/dependencies/gradle.properties index f5a36f2426dd..f0a24ed12cc2 100644 --- a/build/dependencies/gradle.properties +++ b/build/dependencies/gradle.properties @@ -5,5 +5,5 @@ jetSignBuild=42.30 secondJreVersion=11 secondJreBuild=11_0_3b304.2 bundledMavenVersion=3.6.1 -jdkBuild=u212b1586.2 +jdkBuild=u212b1586.4 gradleApiVersion=5.2.1 diff --git a/build/gant.xml b/build/gant.xml index c05fd880f33c..581250a27ddb 100644 --- a/build/gant.xml +++ b/build/gant.xml @@ -19,7 +19,12 @@ - + + + + + + diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java index fc0df4a9b360..24842d35eb92 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java @@ -55,8 +55,8 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh private LanguageLevel myLanguageLevel; private JavaSdkVersion myJavaSdkVersion; - @SuppressWarnings("StatefulEp") private PsiFile myFile; - @SuppressWarnings("StatefulEp") private PsiJavaModule myJavaModule; + private PsiFile myFile; + private PsiJavaModule myJavaModule; // map codeBlock->List of PsiReferenceExpression of uninitialized final variables private final Map> myUninitializedVarProblems = new THashMap<>(); @@ -814,8 +814,12 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh public void visitLiteralExpression(PsiLiteralExpression expression) { super.visitLiteralExpression(expression); if (myHolder.hasErrorResults()) return; + myHolder.add(HighlightUtil.checkLiteralExpressionParsingError(expression, myLanguageLevel,myFile)); - if (myRefCountHolder != null && !myHolder.hasErrorResults()) registerReferencesFromInjectedFragments(expression); + + if (myRefCountHolder != null && !myHolder.hasErrorResults()) { + registerReferencesFromInjectedFragments(expression); + } if (myRefCountHolder != null && !myHolder.hasErrorResults()) { for (PsiReference reference : expression.getReferences()) { diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaPredefinedConfigurations.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaPredefinedConfigurations.java index 6625b39355be..12825246783e 100644 --- a/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaPredefinedConfigurations.java +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaPredefinedConfigurations.java @@ -52,7 +52,7 @@ class JavaPredefinedConfigurations { // Class based createSearchTemplateInfo( SSRBundle.message("predefined.configuration.methods.of.the.class"), - "'_ReturnType '_Method('_ParameterType '_Parameter*);", + "'_ReturnType? '_Method('_ParameterType '_Parameter*);", CLASS_TYPE ), createSearchTemplateInfo( diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaStructuralSearchProfile.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaStructuralSearchProfile.java index c0051d57be9f..0eadc89d1150 100644 --- a/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaStructuralSearchProfile.java +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaStructuralSearchProfile.java @@ -288,7 +288,7 @@ public class JavaStructuralSearchProfile extends StructuralSearchProfile { @NotNull PatternTreeContext context, @NotNull LanguageFileType fileType, @NotNull Language language, - String contextName, + String contextId, @NotNull Project project, boolean physical) { if (physical) { @@ -311,7 +311,7 @@ public class JavaStructuralSearchProfile extends StructuralSearchProfile { if (shouldTryExpressionPattern(result)) { try { final PsiElement[] expressionPattern = - createPatternTree(text, PatternTreeContext.Expression, fileType, language, contextName, project, false); + createPatternTree(text, PatternTreeContext.Expression, fileType, language, contextId, project, false); if (expressionPattern.length == 1) { return expressionPattern; } @@ -319,7 +319,7 @@ public class JavaStructuralSearchProfile extends StructuralSearchProfile { } else if (shouldTryClassPattern(result)) { final PsiElement[] classPattern = - createPatternTree(text, PatternTreeContext.Class, fileType, language, contextName, project, false); + createPatternTree(text, PatternTreeContext.Class, fileType, language, contextId, project, false); if (classPattern.length <= result.size()) { return classPattern; } @@ -416,7 +416,7 @@ public class JavaStructuralSearchProfile extends StructuralSearchProfile { @NotNull @Override - public PsiCodeFragment createCodeFragment(Project project, String text) { + public PsiCodeFragment createCodeFragment(Project project, String text, String contextId) { return JavaCodeFragmentFactory.getInstance(project).createCodeBlockCodeFragment(text, null, true); } diff --git a/platform/built-in-server/start-up-visualizer/src/charts/ActivityChartDescriptor.ts b/platform/built-in-server/start-up-visualizer/src/charts/ActivityChartDescriptor.ts index 65607a78b7c0..3cb86b27e772 100644 --- a/platform/built-in-server/start-up-visualizer/src/charts/ActivityChartDescriptor.ts +++ b/platform/built-in-server/start-up-visualizer/src/charts/ActivityChartDescriptor.ts @@ -1,16 +1,20 @@ // 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. import {Item} from "@/state/data" +import {ChartManager} from "./ChartManager" export interface ActivityChartDescriptor { readonly label: string readonly id: string + readonly isInfoChart?: boolean + readonly sourceNames?: Array readonly rotatedLabels?: boolean readonly groupByThread?: boolean readonly sourceHasPluginInformation?: boolean + readonly chartManagerProducer?: (container: HTMLElement, sourceNames: Array, descriptor: ActivityChartDescriptor) => Promise readonly shortNameProducer?: (item: Item) => string } @@ -27,6 +31,7 @@ export const chartDescriptors: Array = [ id: "components", sourceNames: ["appComponents", "projectComponents", "moduleComponents"], shortNameProducer: getShortName, + chartManagerProducer: async (container, sourceNames, descriptor) => new (await import(/* webpackMode: "eager" */ "./ComponentChartManager")).ComponentChartManager(container, sourceNames!!, descriptor) }, { label: "Services", @@ -67,4 +72,18 @@ export const chartDescriptors: Array = [ id: "GCs", rotatedLabels: false, }, + { + label: "Time Distribution", + isInfoChart: true, + id: "timeDistribution", + sourceNames: [], + chartManagerProducer: async (container, _sourceNames, _descriptor) => new (await import(/* webpackMode: "eager" */ "./TreeMapChartManager")).TreeMapChartManager(container), + }, + { + label: "Plugin Classes", + isInfoChart: true, + id: "pluginClassCount", + sourceNames: [], + chartManagerProducer: async (container, _sourceNames, _descriptor) => new (await import(/* webpackMode: "eager" */ "./PluginClassCountTreeMapChartManager")).PluginClassCountTreeMapChartManager(container), + }, ] diff --git a/platform/built-in-server/start-up-visualizer/src/charts/ActivityChartManager.ts b/platform/built-in-server/start-up-visualizer/src/charts/ActivityChartManager.ts index 148dda070c08..7369bc7344e9 100644 --- a/platform/built-in-server/start-up-visualizer/src/charts/ActivityChartManager.ts +++ b/platform/built-in-server/start-up-visualizer/src/charts/ActivityChartManager.ts @@ -113,11 +113,7 @@ export class ActivityChartManager extends XYChartManager { let getItemListBySourceName: (name: string) => Array | null | undefined = name => { // @ts-ignore - const result: Array | null = data.data[name] - if (result != null) { - return result.filter(it => it.duration >= 10) - } - return result + return data.data[name] } let sourceNameToLegendName: (sourceName: string, itemCount: number) => string = this.sourceNameToLegendName.bind(this) diff --git a/platform/built-in-server/start-up-visualizer/src/charts/BaseChartComponent.ts b/platform/built-in-server/start-up-visualizer/src/charts/BaseChartComponent.ts index b1eff8bb7bd0..a928e4cc8109 100644 --- a/platform/built-in-server/start-up-visualizer/src/charts/BaseChartComponent.ts +++ b/platform/built-in-server/start-up-visualizer/src/charts/BaseChartComponent.ts @@ -3,6 +3,7 @@ import {Component, Vue, Watch} from "vue-property-decorator" import {AppState, mainModuleName} from "@/state/StateStorageManager" import {DataManager} from "@/state/DataManager" import {ChartManager} from "@/charts/ChartManager" +import {Notification} from "element-ui" // @ts-ignore @Component @@ -18,7 +19,7 @@ export abstract class BaseChartComponent extends Vue { this.renderDataIfAvailable() } - protected abstract createChartManager(): T + protected abstract createChartManager(): Promise @Watch("measurementData") /** @final */ @@ -31,10 +32,19 @@ export abstract class BaseChartComponent extends Vue { let chartManager = this.chartManager if (chartManager == null) { - chartManager = this.createChartManager() - this.chartManager = chartManager + this.createChartManager() + .then(chartManager => { + this.chartManager = chartManager + chartManager.render(data) + }) + .catch(e => { + console.log(e) + Notification.error(e) + }) + } + else { + chartManager.render(data) } - chartManager.render(data) } beforeDestroy() { diff --git a/platform/built-in-server/start-up-visualizer/src/charts/BaseTreeMapChartManager.ts b/platform/built-in-server/start-up-visualizer/src/charts/BaseTreeMapChartManager.ts new file mode 100644 index 000000000000..ae4279372acb --- /dev/null +++ b/platform/built-in-server/start-up-visualizer/src/charts/BaseTreeMapChartManager.ts @@ -0,0 +1,29 @@ +// 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. +import {BaseChartManager, configureCursor} from "@/charts/ChartManager" +import * as am4charts from "@amcharts/amcharts4/charts" +import * as am4core from "@amcharts/amcharts4/core" + +export abstract class BaseTreeMapChartManager extends BaseChartManager { + protected constructor(container: HTMLElement) { + super(am4core.create(container, am4charts.TreeMap)) + + configureCursor(this.chart) + + // cursor tooltip is distracting (cannot be in BaseChartManager because only TreeMap creates axis as part of chart creation, for other charts axis is created customly) + this.chart.xAxis.cursorTooltipEnabled = false + this.chart.yAxis.cursorTooltipEnabled = false + } + + protected enableZoom() { + const chart = this.chart + chart.mouseWheelBehavior = "zoomX" + chart.scrollbarX = new am4core.Scrollbar() + chart.mouseWheelBehavior = "zoomXY" + } + + protected configureLabelBullet(bullet: am4charts.LabelBullet) { + bullet.locationY = 0.5 + bullet.locationX = 0.5 + bullet.label.fill = am4core.color("#fff") + } +} \ No newline at end of file diff --git a/platform/built-in-server/start-up-visualizer/src/charts/ChartManager.ts b/platform/built-in-server/start-up-visualizer/src/charts/ChartManager.ts index b9fe145c38a1..3e030232020a 100644 --- a/platform/built-in-server/start-up-visualizer/src/charts/ChartManager.ts +++ b/platform/built-in-server/start-up-visualizer/src/charts/ChartManager.ts @@ -9,10 +9,14 @@ export interface ChartManager { dispose(): void } -function configureCommonChartSettings(chart: am4charts.XYChart) { +export function configureCommonChartSettings(chart: am4charts.XYChart) { chart.mouseWheelBehavior = "zoomX" chart.scrollbarX = new am4core.Scrollbar() + configureCursor(chart) +} + +export function configureCursor(chart: am4charts.XYChart) { const cursor = new am4charts.XYCursor() cursor.lineY.disabled = true cursor.lineX.disabled = true diff --git a/platform/built-in-server/start-up-visualizer/src/charts/PluginClassCountTreeMapChartManager.ts b/platform/built-in-server/start-up-visualizer/src/charts/PluginClassCountTreeMapChartManager.ts new file mode 100644 index 000000000000..3a05606e0dfe --- /dev/null +++ b/platform/built-in-server/start-up-visualizer/src/charts/PluginClassCountTreeMapChartManager.ts @@ -0,0 +1,56 @@ +// 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. +import * as am4charts from "@amcharts/amcharts4/charts" +import {DataManager} from "@/state/DataManager" +import {BaseTreeMapChartManager} from "@/charts/BaseTreeMapChartManager" + +export class PluginClassCountTreeMapChartManager extends BaseTreeMapChartManager { + constructor(container: HTMLElement) { + super(container) + + const chart = this.chart + chart.dataFields.value = "count" + chart.dataFields.name = "name" + + this.enableZoom() + + const level1 = chart.seriesTemplates.create("0") + const level1Bullet = level1.bullets.push(new am4charts.LabelBullet()) + this.configureLabelBullet(level1Bullet) + level1Bullet.label.text = "{abbreviatedName} ({count})" + } + + render(data: DataManager): void { + const items: Array = [] + + const loadedClasses = data.data.stats.loadedClasses + if (loadedClasses != null) { + for (const name of Object.keys(loadedClasses)) { + items.push({ + name, + abbreviatedName: getAbbreviatedName(name), + count: loadedClasses[name], + }) + } + } + + this.chart.data = items + } +} + +function getAbbreviatedName(name: string): string { + if (!name.includes(".")) { + return name + } + + let abbreviatedName = "" + const names = name.split(".") + for (let i = 0; i < names.length; i++) { + const unqualifiedName = names[i] + if (i == (names.length - 1)) { + abbreviatedName += unqualifiedName + } else { + abbreviatedName += unqualifiedName.substring(0, 1) + "." + } + } + return abbreviatedName +} \ No newline at end of file diff --git a/platform/built-in-server/start-up-visualizer/src/charts/TreeMapChartManager.ts b/platform/built-in-server/start-up-visualizer/src/charts/TreeMapChartManager.ts index ef72fb2cff4b..493a58411b36 100644 --- a/platform/built-in-server/start-up-visualizer/src/charts/TreeMapChartManager.ts +++ b/platform/built-in-server/start-up-visualizer/src/charts/TreeMapChartManager.ts @@ -1,14 +1,15 @@ // 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. -import {BaseChartManager} from "@/charts/ChartManager" import * as am4charts from "@amcharts/amcharts4/charts" -import * as am4core from "@amcharts/amcharts4/core" import {DataManager} from "@/state/DataManager" import {IconData, Item} from "@/state/data" import {getShortName} from "@/charts/ActivityChartDescriptor" +import {BaseTreeMapChartManager} from "@/charts/BaseTreeMapChartManager" -export class TreeMapChartManager extends BaseChartManager { +export class TreeMapChartManager extends BaseTreeMapChartManager { constructor(container: HTMLElement) { - super(am4core.create(container, am4charts.TreeMap)) + super(container) + + // enableZoom is not called because for this chart it doesn't work correctly and not really required const chart = this.chart chart.dataFields.value = "duration" @@ -35,12 +36,6 @@ export class TreeMapChartManager extends BaseChartManager { chart.seriesTemplates.create("3").bullets.push(level2Bullet) } - private configureLabelBullet(bullet: am4charts.LabelBullet) { - bullet.locationY = 0.5 - bullet.locationX = 0.5 - bullet.label.fill = am4core.color("#fff") - } - render(data: DataManager): void { const items: Array = [] @@ -114,10 +109,6 @@ export class TreeMapChartManager extends BaseChartManager { }) } } - - dispose(): void { - this.chart.dispose() - } } function toTreeMapItem(items: Array | null | undefined) { diff --git a/platform/built-in-server/start-up-visualizer/src/router.ts b/platform/built-in-server/start-up-visualizer/src/router.ts index 6ee68e0b4b15..8985275eee6f 100644 --- a/platform/built-in-server/start-up-visualizer/src/router.ts +++ b/platform/built-in-server/start-up-visualizer/src/router.ts @@ -3,9 +3,7 @@ import Vue from "vue" import Router, {RouteConfig} from "vue-router" import {Notification} from "element-ui" import Main from "@/views/Main.vue" -import ItemChart from "@/charts/ActivityChart.vue" import {chartDescriptors} from "@/charts/ActivityChartDescriptor" -import TimelineChart from "@/timeline/TimelineChart.vue" Vue.use(Router) @@ -14,7 +12,7 @@ const chartComponentRoutes: Array = chartDescriptors.map(it => { return { path: `/${it.id}`, name: it.label, - component: ItemChart, + component: () => import(/* webpackMode: "eager" */ "@/views/ActivityChart.vue"), props: {type: it.id}, } }) @@ -27,7 +25,7 @@ const routes: Array = [ { path: `/timeline`, name: "Timeline", - component: TimelineChart, + component: () => import(/* webpackMode: "eager" */ "@/timeline/TimelineChart.vue"), }, { path: "*", diff --git a/platform/built-in-server/start-up-visualizer/src/state/data.ts b/platform/built-in-server/start-up-visualizer/src/state/data.ts index 088c29567872..a573d33c8f95 100644 --- a/platform/built-in-server/start-up-visualizer/src/state/data.ts +++ b/platform/built-in-server/start-up-visualizer/src/state/data.ts @@ -49,6 +49,8 @@ export interface Stats { readonly component: StatItem readonly service: StatItem + + readonly loadedClasses: { [key: string]: number; } } export interface StatItem { diff --git a/platform/built-in-server/start-up-visualizer/src/timeline/TimelineChart.vue b/platform/built-in-server/start-up-visualizer/src/timeline/TimelineChart.vue index 2734324b9f82..2fb84cb185f6 100644 --- a/platform/built-in-server/start-up-visualizer/src/timeline/TimelineChart.vue +++ b/platform/built-in-server/start-up-visualizer/src/timeline/TimelineChart.vue @@ -7,10 +7,11 @@ import {Component} from "vue-property-decorator" import {TimelineChartManager} from "./TimeLineChartManager" import {BaseChartComponent} from "@/charts/BaseChartComponent" + import {ChartManager} from "@/charts/ChartManager" @Component - export default class TimelineChart extends BaseChartComponent { - createChartManager(): TimelineChartManager { + export default class TimelineChart extends BaseChartComponent { + async createChartManager() { return new TimelineChartManager(this.$refs.chartContainer as HTMLElement) } } diff --git a/platform/built-in-server/start-up-visualizer/src/charts/ActivityChart.vue b/platform/built-in-server/start-up-visualizer/src/views/ActivityChart.vue similarity index 65% rename from platform/built-in-server/start-up-visualizer/src/charts/ActivityChart.vue rename to platform/built-in-server/start-up-visualizer/src/views/ActivityChart.vue index 2bb2902cd363..b7951417da82 100644 --- a/platform/built-in-server/start-up-visualizer/src/charts/ActivityChart.vue +++ b/platform/built-in-server/start-up-visualizer/src/views/ActivityChart.vue @@ -5,11 +5,10 @@ diff --git a/platform/built-in-server/start-up-visualizer/yarn.lock b/platform/built-in-server/start-up-visualizer/yarn.lock index 8823edf2a03e..c451ddd9484e 100644 --- a/platform/built-in-server/start-up-visualizer/yarn.lock +++ b/platform/built-in-server/start-up-visualizer/yarn.lock @@ -2262,9 +2262,9 @@ ejs@^2.6.1: integrity sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ== electron-to-chromium@^1.3.150: - version "1.3.152" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.152.tgz#8f1c08e101d58fe2ef72655481bbf8c83f9450fa" - integrity sha512-Ah10cGMWIXYD8aUTH2Y7lGRhaOFQLyWuxvXmCPCZCbUIGJ4swnNmT6P4aA8RTgUmNw9kmcDL6SoU8TZC4YuZGg== + version "1.3.154" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.154.tgz#e5b73beecd4db9f024d807ba1faad93fe1fb98aa" + integrity sha512-r3jVJRWvQIKDdjAbtmvJ7NihBFNUpU4VJKsslAzruv9dnYde3v8U2T7J62Vap3c6l6Ku4J56kxlciIO/E93cQg== element-ui@^2.9.1: version "2.9.1" @@ -4908,25 +4908,24 @@ pbkdf2@^3.0.3: safe-buffer "^5.0.1" sha.js "^2.4.8" -pdfkit@^0.9.1: - version "0.9.1" - resolved "https://registry.yarnpkg.com/pdfkit/-/pdfkit-0.9.1.tgz#f07a66eebc64855f8345dccfd313f2d8f880f10f" - integrity sha512-45X/NjaynHVNd/866ETK9KmblL8Sqwmah1RPz04IzmZoEO+cvPid2UvkVfZQcS4Jeq/uWY+99qAq04NoigzWSA== +pdfkit@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/pdfkit/-/pdfkit-0.10.0.tgz#88f2aa8e3cf9e1cc2caff6447b68dd4e435cb284" + integrity sha512-mRJ6iuDzpIQ4ftKp5GvijLXNVRK86xjnyIPBraYSPrUPubNqWM5/oYmc7FZKUWz3wusRTj3PLR9HJ1X5ooqfsg== dependencies: crypto-js "^3.1.9-1" fontkit "^1.0.0" linebreak "^0.3.0" png-js ">=0.1.0" - saslprep "1.0.1" pdfmake@^0.1.36: - version "0.1.56" - resolved "https://registry.yarnpkg.com/pdfmake/-/pdfmake-0.1.56.tgz#a4fcabe25fa5f04c07cb6e861c0abbf9ca5b33e9" - integrity sha512-c5fSj16VTQCrmTw02Mc+Oj3boRG+PGJuu7cGXAXXljI3bMNDifwmUs+3opEwRZbTWu9WhFKYbrs2Iq136UN/NQ== + version "0.1.57" + resolved "https://registry.yarnpkg.com/pdfmake/-/pdfmake-0.1.57.tgz#408e9853777fb851eeb700aaa221905e135917a0" + integrity sha512-s6Bs71Ylh06yNgJfP61xicHZSEvFrwo8lvI/BOU4+6eDddO8lwOZi5A42RA0V8zQr6hrI1XYxtLkk/7oJ+5w+w== dependencies: iconv-lite "^0.4.24" linebreak "^0.3.0" - pdfkit "^0.9.1" + pdfkit "^0.10.0" performance-now@^2.1.0: version "2.1.0" @@ -5840,11 +5839,6 @@ safe-regex@^1.1.0: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -saslprep@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/saslprep/-/saslprep-1.0.1.tgz#b644e0ba25b156b652f3cb90df7542f896049ba6" - integrity sha512-ntN6SbE3hRqd45PKKadRPgA+xHPWg5lPSj2JWJdJvjTwXDDfkPVtXWvP8jJojvnm+rAsZ2b299C5NwZqq818EA== - sax@^1.1.4, sax@^1.2.4, sax@~1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" diff --git a/platform/configuration-store-impl/src/FileBasedStorage.kt b/platform/configuration-store-impl/src/FileBasedStorage.kt index bf87c4bca8ff..7cc92e7eacf5 100644 --- a/platform/configuration-store-impl/src/FileBasedStorage.kt +++ b/platform/configuration-store-impl/src/FileBasedStorage.kt @@ -57,7 +57,7 @@ open class FileBasedStorage(file: Path, protected open val isUseXmlProlog = false - final override val isUseVfsForWrite: Boolean + override val isUseVfsForWrite: Boolean get() = configuration.isUseVfsForWrite private val isUseUnixLineSeparator: Boolean diff --git a/platform/configuration-store-impl/src/ProjectStoreBase.kt b/platform/configuration-store-impl/src/ProjectStoreBase.kt index 44d2507e5da4..eb2173304150 100644 --- a/platform/configuration-store-impl/src/ProjectStoreBase.kt +++ b/platform/configuration-store-impl/src/ProjectStoreBase.kt @@ -4,12 +4,14 @@ package com.intellij.configurationStore import com.intellij.ide.highlighter.ProjectFileType import com.intellij.ide.highlighter.WorkspaceFileType import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.application.PathManager +import com.intellij.openapi.application.appSystemDir import com.intellij.openapi.components.* import com.intellij.openapi.components.impl.stores.IProjectStore import com.intellij.openapi.diagnostic.runAndLogException import com.intellij.openapi.project.Project import com.intellij.openapi.project.ProjectCoreUtil -import com.intellij.openapi.project.getProjectCachePath +import com.intellij.openapi.project.getProjectCacheFileName import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.util.io.FileUtilRt import com.intellij.openapi.util.registry.Registry @@ -144,7 +146,9 @@ abstract class ProjectStoreBase(final override val project: Project) : Component } } - storageManager.addMacro(StoragePathMacros.CACHE_FILE, project.getProjectCachePath(cacheDirName = "workspace", extensionWithDot = ".xml").systemIndependentPath) + val cacheFileName = project.getProjectCacheFileName(extensionWithDot = ".xml") + storageManager.addMacro(StoragePathMacros.CACHE_FILE, appSystemDir.resolve("workspace").resolve(cacheFileName).systemIndependentPath) + storageManager.addMacro(StoragePathMacros.PRODUCT_WORKSPACE_FILE, "${FileUtil.toSystemIndependentName(PathManager.getConfigPath())}/workspace/$cacheFileName") } override fun getStorageSpecs(component: PersistentStateComponent, stateSpec: State, operation: StateStorageOperation): List { @@ -180,7 +184,7 @@ abstract class ProjectStoreBase(final override val project: Project) : Component // if we create project from default, component state written not to own storage file, but to project file, // we don't have time to fix it properly, so, ancient hack restored - if (result.first().path != StoragePathMacros.CACHE_FILE) { + if (!isSpecialStorage(result.first())) { result.add(DEPRECATED_PROJECT_FILE_STORAGE_ANNOTATION) } return result @@ -192,7 +196,7 @@ abstract class ProjectStoreBase(final override val project: Project) : Component var hasOnlyDeprecatedStorages = true for (storage in storages) { @Suppress("DEPRECATION") - if (storage.path == PROJECT_FILE || storage.path == StoragePathMacros.WORKSPACE_FILE || storage.path == StoragePathMacros.CACHE_FILE) { + if (storage.path == PROJECT_FILE || storage.path == StoragePathMacros.WORKSPACE_FILE || isSpecialStorage(storage)) { if (result == null) { result = SmartList() } @@ -243,4 +247,10 @@ abstract class ProjectStoreBase(final override val project: Project) : Component } } -private fun composeFileBasedProjectWorkSpacePath(filePath: String) = "${FileUtilRt.getNameWithoutExtension(filePath)}${WorkspaceFileType.DOT_DEFAULT_EXTENSION}" \ No newline at end of file +private fun composeFileBasedProjectWorkSpacePath(filePath: String) = "${FileUtilRt.getNameWithoutExtension(filePath)}${WorkspaceFileType.DOT_DEFAULT_EXTENSION}" + +private fun isSpecialStorage(storage: Storage) = isSpecialStorage(storage.path) + +internal fun isSpecialStorage(collapsedPath: String): Boolean { + return collapsedPath == StoragePathMacros.CACHE_FILE || collapsedPath == StoragePathMacros.PRODUCT_WORKSPACE_FILE +} \ No newline at end of file diff --git a/platform/configuration-store-impl/src/StateStorageManagerImpl.kt b/platform/configuration-store-impl/src/StateStorageManagerImpl.kt index e493b2baa0f6..7957300b8d58 100644 --- a/platform/configuration-store-impl/src/StateStorageManagerImpl.kt +++ b/platform/configuration-store-impl/src/StateStorageManagerImpl.kt @@ -305,7 +305,10 @@ open class StateStorageManagerImpl(private val rootTagName: String, roamingType, provider), StorageVirtualFileTracker.TrackedStorage { override val isUseXmlProlog: Boolean - get() = rootElementName != null && storageManager.isUseXmlProlog + get() = rootElementName != null && storageManager.isUseXmlProlog && !isSpecialStorage(fileSpec) + + override val isUseVfsForWrite: Boolean + get() = super.isUseVfsForWrite && !isSpecialStorage(fileSpec) override val configuration: FileBasedStorageConfiguration get() = storageManager @@ -450,7 +453,7 @@ internal val Storage.path: String get() = if (value.isEmpty()) file else value internal fun getEffectiveRoamingType(roamingType: RoamingType, collapsedPath: String): RoamingType { - if (roamingType != RoamingType.DISABLED && (collapsedPath == StoragePathMacros.WORKSPACE_FILE || collapsedPath == StoragePathMacros.NON_ROAMABLE_FILE || collapsedPath == StoragePathMacros.CACHE_FILE)) { + if (roamingType != RoamingType.DISABLED && (collapsedPath == StoragePathMacros.WORKSPACE_FILE || collapsedPath == StoragePathMacros.NON_ROAMABLE_FILE || isSpecialStorage(collapsedPath))) { return RoamingType.DISABLED } else { diff --git a/platform/configuration-store-impl/src/statistic/eventLog/FeatureUsageSettingsEvents.kt b/platform/configuration-store-impl/src/statistic/eventLog/FeatureUsageSettingsEvents.kt index 187017ee327d..98f17a979a22 100644 --- a/platform/configuration-store-impl/src/statistic/eventLog/FeatureUsageSettingsEvents.kt +++ b/platform/configuration-store-impl/src/statistic/eventLog/FeatureUsageSettingsEvents.kt @@ -6,17 +6,19 @@ import com.intellij.internal.statistic.eventLog.EventLogGroup import com.intellij.internal.statistic.eventLog.fus.FeatureUsageLogger import com.intellij.internal.statistic.utils.getPluginInfo import com.intellij.internal.statistic.utils.getProjectId +import com.intellij.openapi.components.ReportValue import com.intellij.openapi.components.State import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.project.Project import com.intellij.util.concurrency.NonUrgentExecutor import com.intellij.util.containers.ContainerUtil +import com.intellij.serialization.MutableAccessor import com.intellij.util.xmlb.BeanBinding import org.jdom.Element import java.util.* private val LOG = Logger.getInstance("com.intellij.configurationStore.statistic.eventLog.FeatureUsageSettingsEventPrinter") -private val GROUP = EventLogGroup("settings", 3) +private val GROUP = EventLogGroup("settings", 4) private val recordedComponents: MutableSet = ContainerUtil.newConcurrentSet() private val recordedOptionNames: MutableSet = ContainerUtil.newConcurrentSet() @@ -91,20 +93,15 @@ open class FeatureUsageSettingsEventPrinter(private val recordDefault: Boolean) for (accessor in accessors) { val type = accessor.genericType if (type === Boolean::class.javaPrimitiveType) { - val value = accessor.readUnsafe(state) - val isDefault = !jdomSerializer.getDefaultSerializationFilter().accepts(accessor, state) - if (!isDefault || recordDefault) { - recordedOptionNames.add(accessor.name) - val content = HashMap() - content["component"] = componentName - content["name"] = accessor.name - content["value"] = value - if (recordDefault) { - content["default"] = isDefault - } - addProjectOptions(content, isDefaultProject, hash) - logConfig(GROUP, eventId, content) - } + logConfigValue(accessor, state, "bool", eventId, isDefaultProject, true, hash, componentName) + } + else if (type === Int::class.javaPrimitiveType || type === Long::class.javaPrimitiveType) { + val reportValue = accessor.getAnnotation(ReportValue::class.java) != null + logConfigValue(accessor, state, "int", eventId, isDefaultProject, reportValue, hash, componentName) + } + else if (type === Float::class.javaPrimitiveType || type === Double::class.javaPrimitiveType) { + val reportValue = accessor.getAnnotation(ReportValue::class.java) != null + logConfigValue(accessor, state, "float", eventId, isDefaultProject, reportValue, hash, componentName) } } @@ -113,6 +110,33 @@ open class FeatureUsageSettingsEventPrinter(private val recordDefault: Boolean) } } + private fun logConfigValue(accessor: MutableAccessor, + state: Any, + type: String, + eventId: String, + isDefaultProject: Boolean, + reportValue: Boolean, + hash: String?, + componentName: String) { + val value = accessor.readUnsafe(state) + val isDefault = !jdomSerializer.getDefaultSerializationFilter().accepts(accessor, state) + if (!isDefault || recordDefault) { + recordedOptionNames.add(accessor.name) + val content = HashMap() + content["type"] = type + content["component"] = componentName + content["name"] = accessor.name + if (reportValue) { + content["value"] = value + } + if (recordDefault) { + content["default"] = isDefault + } + addProjectOptions(content, isDefaultProject, hash) + logConfig(GROUP, eventId, content) + } + } + private fun addProjectOptions(content: HashMap, isDefaultProject: Boolean, projectHash: String?) { diff --git a/platform/configuration-store-impl/testSrc/statistics/eventLog/FeatureUsageSettingsEventsTest.kt b/platform/configuration-store-impl/testSrc/statistics/eventLog/FeatureUsageSettingsEventsTest.kt index 82cd3d776c4a..589faffeef58 100644 --- a/platform/configuration-store-impl/testSrc/statistics/eventLog/FeatureUsageSettingsEventsTest.kt +++ b/platform/configuration-store-impl/testSrc/statistics/eventLog/FeatureUsageSettingsEventsTest.kt @@ -5,11 +5,13 @@ import com.intellij.configurationStore.getStateSpec import com.intellij.configurationStore.statistic.eventLog.FeatureUsageSettingsEventPrinter import com.intellij.internal.statistic.eventLog.EventLogGroup import com.intellij.openapi.components.PersistentStateComponent +import com.intellij.openapi.components.ReportValue import com.intellij.openapi.components.State import com.intellij.openapi.project.ProjectManager import com.intellij.testFramework.ProjectRule import com.intellij.testFramework.assertions.Assertions.assertThat import com.intellij.util.xmlb.annotations.Attribute +import org.junit.Assert import org.junit.ClassRule import org.junit.Test @@ -116,8 +118,8 @@ class FeatureUsageSettingsEventsTest { val withProject = true val defaultProject = false assertThat(printer.result).hasSize(2) - assertDefaultState(printer.getOptionByName("boolOption"), "boolOption", false, withProject, defaultProject) - assertDefaultState(printer.getOptionByName("secondBoolOption"), "secondBoolOption", true, withProject, defaultProject) + assertDefaultState(printer.getOptionByName("boolOption"), "boolOption", false, "bool", withProject, defaultProject) + assertDefaultState(printer.getOptionByName("secondBoolOption"), "secondBoolOption", true, "bool", withProject, defaultProject) } @Test @@ -174,7 +176,7 @@ class FeatureUsageSettingsEventsTest { val defaultProject = false assertThat(printer.result).hasSize(2) assertInvokedRecorded(printer.getInvokedEvent(), withProject, defaultProject) - assertNotDefaultState(printer.getOptionByName("boolOption"), "boolOption", true, withRecordDefault, withProject, defaultProject) + assertNotDefaultState(printer.getOptionByName("boolOption"), "boolOption", true, "bool", withRecordDefault, withProject, defaultProject) } @Test @@ -200,7 +202,7 @@ class FeatureUsageSettingsEventsTest { val defaultProject = false assertThat(printer.result).hasSize(2) assertInvokedRecorded(printer.getInvokedEvent(), withProject, defaultProject) - assertNotDefaultState(printer.getOptionByName("boolOption"), "boolOption", true, withRecordDefault, withProject, defaultProject) + assertNotDefaultState(printer.getOptionByName("boolOption"), "boolOption", true, "bool", withRecordDefault, withProject, defaultProject) } @Test @@ -215,8 +217,8 @@ class FeatureUsageSettingsEventsTest { val withProject = true val defaultProject = false assertThat(printer.result).hasSize(2) - assertNotDefaultState(printer.getOptionByName("boolOption"), "boolOption", true, withRecordDefault, withProject, defaultProject) - assertDefaultState(printer.getOptionByName("secondBoolOption"), "secondBoolOption", true, withProject, defaultProject) + assertNotDefaultState(printer.getOptionByName("boolOption"), "boolOption", true, "bool", withRecordDefault, withProject, defaultProject) + assertDefaultState(printer.getOptionByName("secondBoolOption"), "secondBoolOption", true, "bool", withProject, defaultProject) } @Test @@ -232,7 +234,7 @@ class FeatureUsageSettingsEventsTest { val defaultProject = false assertThat(printer.result).hasSize(2) assertInvokedRecorded(printer.getInvokedEvent(), withProject, defaultProject) - assertNotDefaultState(printer.getOptionByName("boolOption"), "boolOption", true, withRecordDefault, withProject, defaultProject) + assertNotDefaultState(printer.getOptionByName("boolOption"), "boolOption", true, "bool", withRecordDefault, withProject, defaultProject) } @Suppress("SameParameterValue") @@ -248,8 +250,8 @@ class FeatureUsageSettingsEventsTest { val withProject = true val defaultProject = false assertThat(printer.result).hasSize(2) - assertNotDefaultState(printer.getOptionByName("boolOption"), "boolOption", true, withRecordDefault, withProject, defaultProject) - assertNotDefaultState(printer.getOptionByName("secondBoolOption"), "secondBoolOption", false, withRecordDefault, withProject, defaultProject) + assertNotDefaultState(printer.getOptionByName("boolOption"), "boolOption", true, "bool", withRecordDefault, withProject, defaultProject) + assertNotDefaultState(printer.getOptionByName("secondBoolOption"), "secondBoolOption", false, "bool", withRecordDefault, withProject, defaultProject) } @Test @@ -265,8 +267,76 @@ class FeatureUsageSettingsEventsTest { val defaultProject = false assertThat(printer.result).hasSize(3) assertInvokedRecorded(printer.getInvokedEvent(), withProject, defaultProject) - assertNotDefaultState(printer.getOptionByName("boolOption"), "boolOption", true, withRecordDefault, withProject, defaultProject) - assertNotDefaultState(printer.getOptionByName("secondBoolOption"), "secondBoolOption", false, withRecordDefault, withProject, defaultProject) + assertNotDefaultState(printer.getOptionByName("boolOption"), "boolOption", true, "bool", withRecordDefault, withProject, defaultProject) + assertNotDefaultState(printer.getOptionByName("secondBoolOption"), "secondBoolOption", false, "bool", withRecordDefault, withProject, defaultProject) + } + + @Test + fun `record default numerical fields in application component`() { + val component = TestComponent() + component.loadState(ComponentStateWithNumerical()) + val spec = getStateSpec(component) + val printer = TestFeatureUsageSettingsEventsPrinter(false) + printer.logConfigurationState(spec.name, component.state, null) + + val withProject = false + val defaultProject = false + Assert.assertEquals(1, printer.result.size) + assertInvokedRecorded(printer.getInvokedEvent(), withProject, defaultProject) + } + + @Test + fun `record not default numerical fields in application component`() { + val component = TestComponent() + component.loadState(ComponentStateWithNumerical(intOpt = 10, longOpt = 15, floatOpt = 5.5F, doubleOpt = 3.4)) + val spec = getStateSpec(component) + val printer = TestFeatureUsageSettingsEventsPrinter(false) + printer.logConfigurationState(spec.name, component.state, null) + + val withProject = false + val defaultProject = false + Assert.assertEquals(5, printer.result.size) + assertInvokedRecorded(printer.getInvokedEvent(), withProject, defaultProject) + assertNotDefaultState(printer.getOptionByName("integerOption"), "integerOption", null, "int", false, withProject, defaultProject) + assertNotDefaultState(printer.getOptionByName("longOption"), "longOption", null, "int", false, withProject, defaultProject) + assertNotDefaultState(printer.getOptionByName("floatOption"), "floatOption", null, "float", false, withProject, defaultProject) + assertNotDefaultState(printer.getOptionByName("doubleOption"), "doubleOption", null, "float", false, withProject, defaultProject) + } + + @Test + fun `record not default numerical fields with absolute value in application component`() { + val component = TestComponent() + component.loadState(ComponentStateWithNumerical(absIntOpt = 10, absLongOpt = 15, absFloatOpt = 5.5F, absDoubleOpt = 3.4)) + val spec = getStateSpec(component) + val printer = TestFeatureUsageSettingsEventsPrinter(false) + printer.logConfigurationState(spec.name, component.state, null) + + val withProject = false + val defaultProject = false + Assert.assertEquals(5, printer.result.size) + assertInvokedRecorded(printer.getInvokedEvent(), withProject, defaultProject) + assertNotDefaultState(printer.getOptionByName("absIntegerOption"), "absIntegerOption", 10, "int", false, withProject, defaultProject) + assertNotDefaultState(printer.getOptionByName("absLongOption"), "absLongOption", 15L, "int", false, withProject, defaultProject) + assertNotDefaultState(printer.getOptionByName("absFloatOption"), "absFloatOption", 5.5f, "float", false, withProject, defaultProject) + assertNotDefaultState(printer.getOptionByName("absDoubleOption"), "absDoubleOption", 3.4, "float", false, withProject, defaultProject) + } + + @Test + fun `record all not default numerical fields with absolute value in application component`() { + val component = TestComponent() + component.loadState(ComponentStateWithNumerical(absIntOpt = 10, absLongOpt = 15, absFloatOpt = 5.5F, absDoubleOpt = 3.4)) + val spec = getStateSpec(component) + val printer = TestFeatureUsageSettingsEventsPrinter(false) + printer.logConfigurationState(spec.name, component.state, null) + + val withProject = false + val defaultProject = false + Assert.assertEquals(5, printer.result.size) + assertInvokedRecorded(printer.getInvokedEvent(), withProject, defaultProject) + assertNotDefaultState(printer.getOptionByName("absIntegerOption"), "absIntegerOption", 10, "int", false, withProject, defaultProject) + assertNotDefaultState(printer.getOptionByName("absLongOption"), "absLongOption", 15L, "int", false, withProject, defaultProject) + assertNotDefaultState(printer.getOptionByName("absFloatOption"), "absFloatOption", 5.5f, "float", false, withProject, defaultProject) + assertNotDefaultState(printer.getOptionByName("absDoubleOption"), "absDoubleOption", 3.4, "float", false, withProject, defaultProject) } private fun assertDefaultWithoutDefaultRecording(printer: TestFeatureUsageSettingsEventsPrinter, @@ -276,14 +346,16 @@ class FeatureUsageSettingsEventsTest { assertInvokedRecorded(printer.result[0], withProject, defaultProject) } - private fun assertNotDefaultState(printer: TestFeatureUsageSettingsEventsPrinter, withRecordDefault: Boolean, withProject: Boolean, defaultProject: Boolean) { + @Suppress("SameParameterValue") + private fun assertNotDefaultState(printer: TestFeatureUsageSettingsEventsPrinter,withRecordDefault: Boolean, withProject: Boolean, defaultProject: Boolean) { assertThat(printer.result).hasSize(1) - assertNotDefaultState(printer.result[0], "boolOption", true, withRecordDefault, withProject, defaultProject) + assertNotDefaultState(printer.result[0], "boolOption", true, "bool", withRecordDefault, withProject, defaultProject) } private fun assertNotDefaultState(event: LoggedComponentStateEvents, name: String, - value: Any, + value: Any?, + type: String, withDefaultRecorded: Boolean, withProject: Boolean, defaultProject: Boolean) { @@ -292,14 +364,18 @@ class FeatureUsageSettingsEventsTest { assertThat(event.id).isEqualTo(if (withDefaultRecorded) "option" else "not.default") var size = 3 + if (value != null) size++ if (withDefaultRecorded) size++ if (withProject) size++ if (defaultProject) size++ assertThat(event.data).hasSize(size) assertThat(event.data["component"]).isEqualTo("MyTestComponent") + assertThat(event.data["type"]).isEqualTo(type) assertThat(event.data["name"]).isEqualTo(name) - assertThat(event.data["value"]).isEqualTo(value) + if (value != null) { + assertThat(event.data["value"]).isEqualTo(value) + } if (withDefaultRecorded) { assertThat(event.data["default"]).isEqualTo(false) } @@ -313,24 +389,26 @@ class FeatureUsageSettingsEventsTest { private fun assertDefaultState(printer: TestFeatureUsageSettingsEventsPrinter, withProject: Boolean, defaultProject: Boolean) { assertThat(printer.result).hasSize(1) - assertDefaultState(printer.result[0], "boolOption", false, withProject, defaultProject) + assertDefaultState(printer.result[0], "boolOption", false, "bool", withProject, defaultProject) } private fun assertDefaultState(event: LoggedComponentStateEvents, name: String, value: Any, + type: String, withProject: Boolean, defaultProject: Boolean) { assertThat(event.group.id).isEqualTo("settings") assertThat(event.group.version).isGreaterThan(0) assertThat(event.id).isEqualTo("option") - var size = 4 + var size = 5 if (withProject) size++ if (defaultProject) size++ assertThat(event.data).hasSize(size) assertThat(event.data["component"]).isEqualTo("MyTestComponent") + assertThat(event.data["type"]).isEqualTo(type) assertThat(event.data["name"]).isEqualTo(name) assertThat(event.data["value"]).isEqualTo(value) assertThat(event.data["default"]).isEqualTo(true) @@ -422,4 +500,45 @@ class FeatureUsageSettingsEventsTest { @Attribute("second-bool-value") val secondBoolOption: Boolean = secondBool } + + @Suppress("unused") + private class ComponentStateWithNumerical(intOpt: Int = 0, + longOpt: Long = 0, + floatOpt: Float = 0.0F, + doubleOpt: Double = 0.0, + absIntOpt: Int = 0, + absLongOpt: Long = 0, + absFloatOpt: Float = 0.0F, + absDoubleOpt: Double = 0.0, + bool: Boolean = false, + str: String = "string-option", + list: List = ArrayList()) : ComponentState(bool, str, list) { + @Attribute("int-option") + val integerOption: Int = intOpt + + @Attribute("long-option") + val longOption: Long = longOpt + + @Attribute("float-option") + val floatOption: Float = floatOpt + + @Attribute("double-option") + val doubleOption: Double = doubleOpt + + @Attribute("abs-int-option") + @field:ReportValue + val absIntegerOption: Int = absIntOpt + + @Attribute("abs-long-option") + @field:ReportValue + val absLongOption: Long = absLongOpt + + @Attribute("abs-float-option") + @field:ReportValue + val absFloatOption: Float = absFloatOpt + + @Attribute("abs-double-option") + @field:ReportValue + val absDoubleOption: Double = absDoubleOpt + } } \ No newline at end of file diff --git a/platform/core-impl/src/com/intellij/ide/plugins/PluginClassCache.java b/platform/core-impl/src/com/intellij/ide/plugins/PluginClassCache.java deleted file mode 100644 index 79ac07faece2..000000000000 --- a/platform/core-impl/src/com/intellij/ide/plugins/PluginClassCache.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2000-2013 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.ide.plugins; - -import com.intellij.openapi.extensions.PluginId; -import gnu.trove.TObjectIntHashMap; -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * @author peter - */ -class PluginClassCache { - private static final Object ourLock = new Object(); - private final TObjectIntHashMap myClassCounts = new TObjectIntHashMap<>(); - - void addPluginClass(@NotNull PluginId pluginId) { - synchronized(ourLock) { - myClassCounts.put(pluginId, myClassCounts.get(pluginId) + 1); - } - } - - void dumpPluginClassStatistics() { - if (!Boolean.valueOf(System.getProperty("idea.is.internal")).booleanValue()) return; - - List counters; - synchronized (ourLock) { - //noinspection unchecked - counters = new ArrayList(Arrays.asList(myClassCounts.keys())); - } - - counters.sort((o1, o2) -> myClassCounts.get(o2) - myClassCounts.get(o1)); - for (PluginId id : counters) { - PluginManagerCore.getLogger().info(id + " loaded " + myClassCounts.get(id) + " classes"); - } - } -} diff --git a/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java b/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java index d4ec87d05350..f64f45feb9cb 100644 --- a/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java +++ b/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.java @@ -77,7 +77,6 @@ public class PluginManagerCore { private static final TObjectIntHashMap ourId2Index = new TObjectIntHashMap<>(); private static final String MODULE_DEPENDENCY_PREFIX = "com.intellij.module"; private static final Map ourModulesToContainingPlugins = new THashMap<>(); - private static final PluginClassCache ourPluginClasses = new PluginClassCache(); private static final String SPECIAL_IDEA_PLUGIN = "IDEA CORE"; private static final String PROPERTY_PLUGIN_PATH = "plugin.path"; @@ -119,7 +118,7 @@ public class PluginManagerCore { private static final List ourDisabledPluginsListeners = new CopyOnWriteArrayList<>(); /** - * Returns list of all available plugin descriptors (bundled and custom, include disabled ones). Use {@link #getLoadedPlugins(StartupProgress)} + * Returns list of all available plugin descriptors (bundled and custom, include disabled ones). Use {@link #getLoadedPlugins()} * if you need to get loaded plugins only. * *

@@ -386,10 +385,6 @@ public class PluginManagerCore { return true; } - public static void addPluginClass(@NotNull PluginId pluginId) { - ourPluginClasses.addPluginClass(pluginId); - } - /** * This is an internal method, use {@link PluginException#createByClass(String, Throwable, Class)} instead. */ @@ -437,10 +432,6 @@ public class PluginManagerCore { } } - public static void dumpPluginClassStatistics() { - ourPluginClasses.dumpPluginClassStatistics(); - } - private static boolean isDependent(@NotNull IdeaPluginDescriptor descriptor, @NotNull PluginId on, @NotNull Map map, diff --git a/platform/core-impl/src/com/intellij/ide/plugins/cl/PluginClassLoader.java b/platform/core-impl/src/com/intellij/ide/plugins/cl/PluginClassLoader.java index 381c9bd17c35..8a76536a51a1 100644 --- a/platform/core-impl/src/com/intellij/ide/plugins/cl/PluginClassLoader.java +++ b/platform/core-impl/src/com/intellij/ide/plugins/cl/PluginClassLoader.java @@ -3,7 +3,6 @@ package com.intellij.ide.plugins.cl; import com.intellij.diagnostic.PluginException; import com.intellij.diagnostic.StartUpMeasurer; -import com.intellij.ide.plugins.PluginManagerCore; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.extensions.PluginId; @@ -20,6 +19,7 @@ import java.io.InputStream; import java.net.URL; import java.util.List; import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; /** @@ -38,6 +38,8 @@ public final class PluginClassLoader extends UrlClassLoader { private final AtomicLong edtTime = new AtomicLong(); private final AtomicLong backgroundTime = new AtomicLong(); + private final AtomicInteger loadedClassCounter = new AtomicInteger(); + public PluginClassLoader(@NotNull List urls, @NotNull ClassLoader[] parents, PluginId pluginId, @@ -62,6 +64,10 @@ public final class PluginClassLoader extends UrlClassLoader { return backgroundTime.get(); } + public long getLoadedClassCount() { + return loadedClassCounter.get(); + } + @Override public Class loadClass(@NotNull String name, boolean resolve) throws ClassNotFoundException { Class c = tryLoadingClass(name, resolve, null); @@ -214,7 +220,7 @@ public final class PluginClassLoader extends UrlClassLoader { throw new PluginException("While loading class " + name + ": " + e.getMessage(), e, myPluginId); } if (c != null) { - PluginManagerCore.addPluginClass(myPluginId); + loadedClassCounter.incrementAndGet(); } return c; diff --git a/platform/core-impl/src/com/intellij/openapi/application/ex/ApplicationUtil.java b/platform/core-impl/src/com/intellij/openapi/application/ex/ApplicationUtil.java index cde2e5c8e65d..39298e705baa 100644 --- a/platform/core-impl/src/com/intellij/openapi/application/ex/ApplicationUtil.java +++ b/platform/core-impl/src/com/intellij/openapi/application/ex/ApplicationUtil.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2015 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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. package com.intellij.openapi.application.ex; import com.intellij.openapi.application.Application; @@ -26,10 +12,7 @@ import com.intellij.util.ExceptionUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.ide.PooledThreadExecutor; -import java.util.concurrent.Callable; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; +import java.util.concurrent.*; public class ApplicationUtil { // throws exception if can't grab read action right now @@ -74,16 +57,19 @@ public class ApplicationUtil { } /** - * Waits for {@code future} to be complete, or the current thread's indicator to be canceled - * Note that {@code future} will not be cancelled by this method + * Waits for {@code future} to be complete, or the current thread's indicator to be canceled. + * Note that {@code future} will not be cancelled by this method. */ - public static void runWithCheckCanceled(@NotNull Future future, @NotNull final ProgressIndicator indicator) throws Exception { + public static T runWithCheckCanceled(@NotNull Future future, + @NotNull final ProgressIndicator indicator) throws ExecutionException { while (true) { indicator.checkCanceled(); try { - future.get(25, TimeUnit.MILLISECONDS); - break; + return future.get(25, TimeUnit.MILLISECONDS); + } + catch (InterruptedException e) { + throw new ProcessCanceledException(e); } catch (TimeoutException ignored) { } } diff --git a/platform/core-impl/src/com/intellij/openapi/editor/ex/util/DataStorageFactory.java b/platform/core-impl/src/com/intellij/openapi/editor/ex/util/DataStorageFactory.java new file mode 100644 index 000000000000..6c1b1520f857 --- /dev/null +++ b/platform/core-impl/src/com/intellij/openapi/editor/ex/util/DataStorageFactory.java @@ -0,0 +1,33 @@ +// 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. +package com.intellij.openapi.editor.ex.util; + +import com.intellij.psi.tree.IElementType; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +/** + * An experiment API for providing custom way of storing lexer-based highlighting data. + *

+ * By default a highlighting lexer uses {@link ShortBasedStorage} implementation which + * serializes information about element type to be highlighted with their indices ({@link IElementType#getIndex()}) + * and deserializes ids back to {@link IElementType} using + * element types registry {@link IElementType#find(short)}. + *

+ * If you need to store more information during syntax highlighting or + * if your element types cannot be restored from {@link IElementType#getIndex()}, + * you can implement you own storage and make your highlighting lexer implement {@link DataStorageFactory} + * that will create the custom storage. + *

+ * As an example, see {@link org.jetbrains.plugins.textmate.language.syntax.lexer.TextMateHighlightingLexer}, + * that lexes files with unregistered (whitout index) element types and + * its data storage ({@link org.jetbrains.plugins.textmate.language.syntax.lexer.TextMateLexerDataStorage} + * serializes/deserializes them to/from strings. + * + * @see com.intellij.openapi.editor.ex.util.LexerEditorHighlighter + * @see SegmentArrayWithData + */ +@ApiStatus.Experimental +public interface DataStorageFactory { + @NotNull + DataStorage createDataStorage(); +} diff --git a/platform/core-impl/src/com/intellij/openapi/editor/ex/util/ShortBasedStorage.java b/platform/core-impl/src/com/intellij/openapi/editor/ex/util/ShortBasedStorage.java index 5ad7576380c5..4acb119017a4 100644 --- a/platform/core-impl/src/com/intellij/openapi/editor/ex/util/ShortBasedStorage.java +++ b/platform/core-impl/src/com/intellij/openapi/editor/ex/util/ShortBasedStorage.java @@ -13,13 +13,13 @@ import static com.intellij.openapi.editor.ex.util.SegmentArray.calcCapacity; * {@link IElementType} index and and restartability of the state (positive values are for initial state). */ public class ShortBasedStorage implements DataStorage { - short[] myData; + protected short[] myData; public ShortBasedStorage() { myData = new short[INITIAL_SIZE]; } - private ShortBasedStorage(short[] data) { + protected ShortBasedStorage(short[] data) { myData = data; } diff --git a/platform/editor-ui-api/src/com/intellij/ide/ui/UISettingsState.kt b/platform/editor-ui-api/src/com/intellij/ide/ui/UISettingsState.kt index 2bc193194433..1580dca369d6 100644 --- a/platform/editor-ui-api/src/com/intellij/ide/ui/UISettingsState.kt +++ b/platform/editor-ui-api/src/com/intellij/ide/ui/UISettingsState.kt @@ -2,6 +2,7 @@ package com.intellij.ide.ui import com.intellij.openapi.components.BaseState +import com.intellij.openapi.components.ReportValue import com.intellij.openapi.util.SystemInfo import com.intellij.ui.scale.JBUIScale import com.intellij.util.PlatformUtils @@ -34,9 +35,11 @@ class UISettingsState : BaseState() { @Deprecated("", replaceWith = ReplaceWith("NotRoamableUiOptions.fontScale")) var fontScale by property(0f) + @get:ReportValue @get:OptionTag("RECENT_FILES_LIMIT") var recentFilesLimit by property(50) + @get:ReportValue @get:OptionTag("RECENT_LOCATIONS_LIMIT") var recentLocationsLimit by property(25) @@ -47,6 +50,7 @@ class UISettingsState : BaseState() { @get:OptionTag("CONSOLE_CYCLE_BUFFER_SIZE_KB") var consoleCycleBufferSizeKb by property(1024) + @get:ReportValue @get:OptionTag("EDITOR_TAB_LIMIT") var editorTabLimit by property(10) diff --git a/platform/editor-ui-ex/src/com/intellij/openapi/editor/ex/util/LexerEditorHighlighter.java b/platform/editor-ui-ex/src/com/intellij/openapi/editor/ex/util/LexerEditorHighlighter.java index 69409a662cb1..d51a1a4404c1 100644 --- a/platform/editor-ui-ex/src/com/intellij/openapi/editor/ex/util/LexerEditorHighlighter.java +++ b/platform/editor-ui-ex/src/com/intellij/openapi/editor/ex/util/LexerEditorHighlighter.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-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. package com.intellij.openapi.editor.ex.util; import com.intellij.lexer.FlexAdapter; @@ -61,7 +61,7 @@ public class LexerEditorHighlighter implements EditorHighlighter, PrioritizedDoc @NotNull protected SegmentArrayWithData createSegments() { - return new SegmentArrayWithData(myLexer instanceof RestartableLexer ? new IntBasedStorage() : new ShortBasedStorage()); + return new SegmentArrayWithData(myLexer instanceof DataStorageFactory ? ((DataStorageFactory)myLexer).createDataStorage() : new ShortBasedStorage()); } public boolean isPlain() { diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DataNode.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DataNode.java index 494e993d384f..17c1c11710a8 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DataNode.java +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DataNode.java @@ -4,7 +4,6 @@ package com.intellij.openapi.externalSystem.model; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.UserDataHolderBase; import com.intellij.openapi.util.UserDataHolderEx; -import com.intellij.util.ArrayUtilRt; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -21,8 +20,6 @@ import java.util.function.Function; * enhance any project. For example, particular framework can add facet settings as one more 'project' node's child. *

* Not thread-safe. - * - * {@link #serializeData} must be called before serialization. */ public class DataNode implements UserDataHolderEx { private static final Logger LOG = Logger.getInstance(DataNode.class); @@ -33,14 +30,12 @@ public class DataNode implements UserDataHolderEx { @NotNull private final transient UserDataHolderBase userData = new UserDataHolderBase(); - private transient T data; - - // Key data type class cannot be used because can specify interface class and not actual data class - private String dataClassName; - private byte[] rawData; + private T data; private boolean ignored; + private transient volatile boolean ready; + @Nullable private DataNode parent; @@ -56,6 +51,10 @@ public class DataNode implements UserDataHolderEx { this.parent = parent; } + public boolean isReady() { + return ready; + } + // deserialization, data decoded on demand @SuppressWarnings("unused") private DataNode() { @@ -80,9 +79,6 @@ public class DataNode implements UserDataHolderEx { @NotNull public T getData() { - if (data == null) { - deserializeData(Arrays.asList(getClass().getClassLoader(), Thread.currentThread().getContextClassLoader())); - } return data; } @@ -94,47 +90,6 @@ public class DataNode implements UserDataHolderEx { this.ignored = ignored; } - /** - * This class is a generic holder for any kind of project data. That project data might originate from different locations, e.g. - * core ide plugins, non-core ide plugins, third-party plugins etc. That means that when a service from a core plugin needs to - * unmarshall {@link DataNode} object, its content should not be unmarshalled as well because its class might be unavailable here. - *

- * That's why the content is delivered as a raw byte array and this method allows to build actual java object from it using - * the right class loader. - *

- * This method is a no-op if the content is already built. - * - * @param classLoaders class loaders which are assumed to be able to build object of the target content class - */ - public void deserializeData(@NotNull Collection classLoaders) { - if (data != null) { - return; - } - if (rawData == null) { - throw new IllegalStateException(String.format("Data node of key '%s' does not contain raw or prepared data", key)); - } - if (rawData.length == 0) { - return; - } - - - String className = dataClassName; - if (className == null) { - className = key.getDataType(); - } - - try { - MultiLoaderWrapper classLoader = new MultiLoaderWrapper(getClass().getClassLoader(), classLoaders); - //noinspection unchecked - data = SerializationKt.readDataNodeData(((Class)classLoader.findClass(className)), rawData, classLoader); - clearRawData(); - } - catch (Exception e) { - throw new IllegalStateException("Can't deserialize target data of key '" + key + "'. " + - "Given class loaders: " + classLoaders, e); - } - } - /** * Allows to replace or modify data. If function returns null, data is left unchanged * @param visitor visitor. Must accept argument of type T and return value of type T @@ -147,16 +102,9 @@ public class DataNode implements UserDataHolderEx { T newData = (T) visitor.apply(getData()); if (newData != null) { data = newData; - clearRawData(); - dataClassName = null; } } - private void clearRawData() { - rawData = null; - dataClassName = null; - } - /** * Allows to retrieve data stored for the given key at the current node or any of its parents. * @@ -221,24 +169,6 @@ public class DataNode implements UserDataHolderEx { return result; } - public void serializeData(@NotNull WriteAndCompressSession buffer) { - if (rawData != null) { - return; - } - - if (data == null) { - dataClassName = null; - rawData = ArrayUtilRt.EMPTY_BYTE_ARRAY; - } - else { - dataClassName = data.getClass().getName(); - if (dataClassName.equals(key.getDataType())) { - dataClassName = null; - } - rawData = SerializationKt.serializeDataNodeData(data, buffer); - } - } - @Override public int hashCode() { // We can't use myChildren.hashCode() because it iterates whole subtree. This should not produce many collisions because 'getData()' @@ -284,7 +214,6 @@ public class DataNode implements UserDataHolderEx { } } parent = null; - clearRawData(); children.clear(); } @@ -332,12 +261,22 @@ public class DataNode implements UserDataHolderEx { return userData.getCopyableUserData(key); } + public boolean validateData() { + if (data == null) { + ready = false; + clear(true); + } + else { + ready = true; + } + return ready; + } + @NotNull public static DataNode nodeCopy(@NotNull DataNode dataNode) { DataNode copy = new DataNode<>(dataNode.key, dataNode.data, null); - copy.dataClassName = dataNode.dataClassName; - copy.rawData = dataNode.rawData; copy.ignored = dataNode.ignored; + copy.ready = dataNode.ready; dataNode.userData.copyCopyableDataTo(copy.userData); return copy; } diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/ProjectSystemId.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/ProjectSystemId.java index e5358e7f7a9a..46c21bc56822 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/ProjectSystemId.java +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/ProjectSystemId.java @@ -81,7 +81,8 @@ public final class ProjectSystemId implements Serializable { ProjectSystemId cached = ourExistingIds.get(id); if (cached != null) { return cached; - } else { + } + else { return this; } } diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/serialization.kt b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/serialization.kt index d881cfb5d847..af860805ef0d 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/serialization.kt +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/serialization.kt @@ -1,67 +1,56 @@ // 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. package com.intellij.openapi.externalSystem.model -import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream -import com.intellij.serialization.* -import net.jpountz.lz4.LZ4CompressorWithLength -import net.jpountz.lz4.LZ4DecompressorWithLength -import net.jpountz.lz4.LZ4Factory -import java.io.OutputStream +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.externalSystem.service.project.ProjectDataManager +import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil +import com.intellij.serialization.ReadConfiguration +import com.intellij.serialization.WriteConfiguration -val externalSystemBeanConstructed: BeanConstructed = { - if (it is ProjectSystemId) { - it.intern() - } - else { - it - } -} +// do not use SkipNullAndEmptySerializationFilter for now because can lead to issues +fun createCacheWriteConfiguration() = WriteConfiguration(allowAnySubTypes = true) -fun createDataNodeReadConfiguration(classLoader: ClassLoader): ReadConfiguration { - return ReadConfiguration(allowAnySubTypes = true, classLoader = classLoader, beanConstructed = externalSystemBeanConstructed) -} +fun createCacheReadConfiguration(log: Logger): ReadConfiguration { + val projectDataManager = ProjectDataManager.getInstance() + val defaultClassLoader = DataNode::class.java.classLoader -fun readDataNodeData(dataClass: Class, data: ByteArray, classLoader: ClassLoader): T { - val decompressor = LZ4DecompressorWithLength(LZ4Factory.fastestInstance().fastDecompressor()) - return ObjectSerializer.instance.read(dataClass, decompressor.decompress(data), createDataNodeReadConfiguration(classLoader)) -} - -fun serializeDataNodeData(data: Any, buffer: WriteAndCompressSession): ByteArray { - ObjectSerializer.instance.write(data, buffer.resetAndGetOutputStream(), WriteConfiguration(allowAnySubTypes = true, filter = SkipNullAndEmptySerializationFilter)) - return buffer.compress() -} - -/** - * To write and compress a lot of elements sequentially into separate byte arrays. - * Reuses input and output byte arrays. - */ -class WriteAndCompressSession { - private val compressor = LZ4CompressorWithLength(LZ4Factory.fastestInstance().fastCompressor()) - private val buffer = BufferExposingByteArrayOutputStream() - - private var lastByteArray: ByteArray? = null - - fun compress(): ByteArray { - val maxCompressedLength = compressor.maxCompressedLength(buffer.size()) - - var compressed = lastByteArray - if (compressed == null || compressed.size < maxCompressedLength) { - compressed = ByteArray(maxCompressedLength) + val allManagers = ExternalSystemApiUtil.getAllManagers() + return createDataNodeReadConfiguration(fun(name: String, hostObject: Any): Class<*>? { + if (hostObject !is DataNode<*>) { + return defaultClassLoader.loadClass(name) } - val compressedLength = compressor.compress(buffer.internalBuffer, 0, buffer.size(), compressed, 0, maxCompressedLength) - if (compressedLength == compressed.size) { - lastByteArray = null - return compressed + val services = projectDataManager.findService(hostObject.key) + if (services != null) { + for (dataService in services) { + try { + return dataService.javaClass.classLoader.loadClass(name) + } + catch (e: ClassNotFoundException) { + } + } + } + + for (manager in allManagers) { + try { + return manager.javaClass.classLoader.loadClass(name) + } + catch (e: ClassNotFoundException) { + } + } + + log.warn("Cannot find class `$name`") + return null + }) +} + +fun createDataNodeReadConfiguration(loadClass: ((name: String, hostObject: Any) -> Class<*>?)): ReadConfiguration { + return ReadConfiguration(allowAnySubTypes = true, loadClass = loadClass, beanConstructed = { + if (it is ProjectSystemId) { + it.intern() } else { - lastByteArray = compressed - return compressed.copyOf(compressedLength) + it } - } - - fun resetAndGetOutputStream(): OutputStream { - buffer.reset() - return buffer - } + }) } \ No newline at end of file diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/ProjectDataManager.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/ProjectDataManager.java index 760e372553a3..d4c6ee6daa16 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/ProjectDataManager.java +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/ProjectDataManager.java @@ -1,23 +1,10 @@ -/* - * Copyright 2000-2017 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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. package com.intellij.openapi.externalSystem.service.project; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.externalSystem.model.DataNode; import com.intellij.openapi.externalSystem.model.ExternalProjectInfo; +import com.intellij.openapi.externalSystem.model.Key; import com.intellij.openapi.externalSystem.model.ProjectSystemId; import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataService; import com.intellij.openapi.project.Project; @@ -25,6 +12,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; +import java.util.List; /** * Aggregates all {@link ProjectDataService#EP_NAME registered data services} @@ -53,6 +41,8 @@ public interface ProjectDataManager { @NotNull Project project, boolean synchronous); + List> findService(@NotNull Key key); + void ensureTheDataIsReadyToUse(@Nullable DataNode dataNode); @Nullable diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/DeduplicateVisitorsSupplier.kt b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/DeduplicateVisitorsSupplier.kt index b0ecf4e75a19..e73f78d4ab43 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/DeduplicateVisitorsSupplier.kt +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/DeduplicateVisitorsSupplier.kt @@ -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-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. package com.intellij.openapi.externalSystem.service.project.manage import com.intellij.openapi.externalSystem.model.Key @@ -12,7 +12,6 @@ import com.intellij.util.containers.Interner import java.util.function.Function class DeduplicateVisitorsSupplier { - private val myModuleData: Interner = HashSetInterner() private val myLibraryData: Interner = HashSetInterner() diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ExternalProjectsDataStorage.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ExternalProjectsDataStorage.java index 1321e904bb88..49773b43e2ec 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ExternalProjectsDataStorage.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ExternalProjectsDataStorage.java @@ -339,28 +339,15 @@ public class ExternalProjectsDataStorage implements SettingsSavingComponentJavaA return projectDataNode; } - private static void doSave(@NotNull Project project, @NotNull Collection externalProjects) - throws IOException { + private static void doSave(@NotNull Project project, @NotNull Collection externalProjects) throws IOException { for (Iterator iterator = externalProjects.iterator(); iterator.hasNext(); ) { InternalExternalProjectInfo externalProject = iterator.next(); if (!validate(externalProject)) { iterator.remove(); - continue; } - - WriteAndCompressSession buffer = new WriteAndCompressSession(); - ExternalSystemApiUtil.visit(externalProject.getExternalProjectStructure(), dataNode -> { - try { - dataNode.serializeData(buffer); - } - catch (Exception e) { - LOG.warn(e); - dataNode.clear(true); - } - }); } - getCacheFile(project).writeList(externalProjects, InternalExternalProjectInfo.class); + getCacheFile(project).writeList(externalProjects, InternalExternalProjectInfo.class, SerializationKt.createCacheWriteConfiguration()); } @SuppressWarnings("unchecked") @@ -388,7 +375,7 @@ public class ExternalProjectsDataStorage implements SettingsSavingComponentJavaA LOG.debug("External projects data storage was invalidated"); return null; } - return cacheFile.readList(InternalExternalProjectInfo.class, SerializationKt.getExternalSystemBeanConstructed()); + return cacheFile.readList(InternalExternalProjectInfo.class, SerializationKt.createCacheReadConfiguration(LOG)); } private static boolean isInvalidated(@NotNull Path configurationFile, @NotNull BasicFileAttributes fileAttributes) throws IOException { diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ExternalSystemKeymapExtension.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ExternalSystemKeymapExtension.java index 65b6dfdf2519..55f22a416b14 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ExternalSystemKeymapExtension.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ExternalSystemKeymapExtension.java @@ -148,7 +148,7 @@ public class ExternalSystemKeymapExtension implements KeymapExtension { return result; } - public static void updateActions(Project project, Collection> taskData) { + public static void updateActions(Project project, @NotNull Collection> taskData) { clearActions(project, taskData); createActions(project, taskData); } diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ExternalSystemShortcutsManager.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ExternalSystemShortcutsManager.java index fa3a17d4bb0c..7374f67063e3 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ExternalSystemShortcutsManager.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ExternalSystemShortcutsManager.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-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. package com.intellij.openapi.externalSystem.service.project.manage; import com.intellij.openapi.Disposable; @@ -103,7 +103,7 @@ public class ExternalSystemShortcutsManager implements Disposable { void shortcutsUpdated(); } - void scheduleKeymapUpdate(Collection> taskData) { + void scheduleKeymapUpdate(@NotNull Collection> taskData) { ExternalSystemKeymapExtension.updateActions(myProject, taskData); } diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ProjectDataManager.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ProjectDataManager.java index e2809706cfab..060fa9514a4c 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ProjectDataManager.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ProjectDataManager.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2017 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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. package com.intellij.openapi.externalSystem.service.project.manage; import com.intellij.openapi.externalSystem.model.DataNode; @@ -32,7 +18,6 @@ import java.util.Collection; */ @Deprecated public class ProjectDataManager extends ProjectDataManagerImpl { - public static ProjectDataManager getInstance() { return new ProjectDataManager(ProjectDataManagerImpl.getInstance()); } diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ProjectDataManagerImpl.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ProjectDataManagerImpl.java index a25f1a2908df..f3461340399c 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ProjectDataManagerImpl.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ProjectDataManagerImpl.java @@ -5,7 +5,6 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.components.impl.ComponentManagerImpl; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.externalSystem.ExternalSystemManager; import com.intellij.openapi.externalSystem.model.*; import com.intellij.openapi.externalSystem.model.project.ModuleData; import com.intellij.openapi.externalSystem.model.project.ProjectData; @@ -39,8 +38,6 @@ import java.util.function.Supplier; public class ProjectDataManagerImpl implements ProjectDataManager { private static final Logger LOG = Logger.getInstance(ProjectDataManagerImpl.class); - private static final com.intellij.openapi.util.Key DATA_READY = - com.intellij.openapi.util.Key.create("externalSystem.data.ready"); @NotNull private final NotNullLazyValue, List>>> myServices; @@ -53,6 +50,12 @@ public class ProjectDataManagerImpl implements ProjectDataManager { this(() -> ProjectDataService.EP_NAME.getExtensions()); } + @Override + @Nullable + public List> findService(@NotNull Key key) { + return myServices.getValue().get(key); + } + @TestOnly ProjectDataManagerImpl(ProjectDataService... dataServices) { this(() -> dataServices); @@ -343,15 +346,14 @@ public class ProjectDataManagerImpl implements ProjectDataManager { @Override public void ensureTheDataIsReadyToUse(@Nullable DataNode startNode) { - if (startNode == null || Boolean.TRUE.equals(startNode.getUserData(DATA_READY))) { + if (startNode == null || startNode.isReady()) { return; } DeduplicateVisitorsSupplier supplier = new DeduplicateVisitorsSupplier(); ((DataNode)startNode).visit(dataNode -> { - if (prepareDataToUse(dataNode)) { + if (dataNode.validateData()) { dataNode.visitData(supplier.getVisitor(dataNode.getKey())); - dataNode.putUserData(DATA_READY, Boolean.TRUE); } }); } @@ -379,7 +381,7 @@ public class ProjectDataManagerImpl implements ProjectDataManager { } catch (Throwable t) { dispose(modelsProvider, project, synchronous); - ExceptionUtil.rethrowAllAsUnchecked(t); + ExceptionUtil.rethrow(t); } } @@ -423,29 +425,6 @@ public class ProjectDataManagerImpl implements ProjectDataManager { } } - private boolean prepareDataToUse(@NotNull DataNode dataNode) { - final Map, List>> servicesByKey = myServices.getValue(); - List> services = servicesByKey.get(dataNode.getKey()); - if (services != null) { - try { - Set classLoaders = new LinkedHashSet<>(); - for (ProjectDataService dataService : services) { - classLoaders.add(dataService.getClass().getClassLoader()); - } - for (ExternalSystemManager manager : ExternalSystemApiUtil.getAllManagers()) { - classLoaders.add(manager.getClass().getClassLoader()); - } - dataNode.deserializeData(classLoaders); - } - catch (Exception e) { - LOG.warn(e); - dataNode.clear(true); - return false; - } - } - return true; - } - private static void commit(@NotNull final IdeModifiableModelsProvider modelsProvider, @NotNull Project project, boolean synchronous, diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/ui/ExternalProjectDataSelectorDialog.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/ui/ExternalProjectDataSelectorDialog.java index 2cfc899aa3eb..9cb9f750f164 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/ui/ExternalProjectDataSelectorDialog.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/ui/ExternalProjectDataSelectorDialog.java @@ -18,7 +18,6 @@ import com.intellij.openapi.externalSystem.model.project.ModuleData; import com.intellij.openapi.externalSystem.model.project.ModuleDependencyData; import com.intellij.openapi.externalSystem.model.project.ProjectData; import com.intellij.openapi.externalSystem.service.project.ProjectDataManager; -import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataManagerImpl; import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil; import com.intellij.openapi.externalSystem.util.ExternalSystemBundle; import com.intellij.openapi.externalSystem.util.ExternalSystemUiUtil; @@ -111,13 +110,12 @@ public class ExternalProjectDataSelectorDialog extends DialogWrapper { } private void init(@NotNull ExternalProjectInfo projectInfo) { - ProjectDataManagerImpl.getInstance().ensureTheDataIsReadyToUse(projectInfo.getExternalProjectStructure()); myProjectInfo = projectInfo; myExternalSystemUiAware = ExternalSystemUiUtil.getUiAware(myProjectInfo.getProjectSystemId()); myTree = createTree(); updateSelectionState(); - myTree.addCheckboxTreeListener(new CheckboxTreeAdapter() { + myTree.addCheckboxTreeListener(new CheckboxTreeListener() { @Override public void nodeStateChanged(@NotNull CheckedTreeNode node) { updateSelectionState(); diff --git a/platform/external-system-impl/testSrc/com/intellij/openapi/externalSystem/model/DataNodeTest.kt b/platform/external-system-impl/testSrc/com/intellij/openapi/externalSystem/model/DataNodeTest.kt index 9ca83c9d5deb..88030da5a313 100644 --- a/platform/external-system-impl/testSrc/com/intellij/openapi/externalSystem/model/DataNodeTest.kt +++ b/platform/external-system-impl/testSrc/com/intellij/openapi/externalSystem/model/DataNodeTest.kt @@ -4,7 +4,6 @@ package com.intellij.openapi.externalSystem.model import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream import com.intellij.serialization.ObjectSerializer import org.assertj.core.api.Assertions.assertThat -import org.assertj.core.api.Assertions.assertThatExceptionOfType import org.junit.Before import org.junit.Test import java.io.Serializable @@ -26,12 +25,7 @@ class DataNodeTest { fun `instance of class from a classloader can be deserialized`() { val barObject = classLoader.loadClass("foo.Bar").newInstance() - val deserialized = wrapAndDeserialize(barObject) - - assertThatExceptionOfType(IllegalStateException::class.java) - .isThrownBy { deserialized.deserializeData(listOf(javaClass.classLoader)) } - - deserialized.deserializeData(listOf(URLClassLoader(arrayOf(libUrl), javaClass.classLoader))) + val deserialized = wrapAndDeserialize(barObject, listOf(URLClassLoader(arrayOf(libUrl), javaClass.classLoader))) assertThat(deserialized.data.javaClass.name).isEqualTo("foo.Bar") } @@ -43,12 +37,7 @@ class DataNodeTest { val proxyInstance = Proxy.newProxyInstance(classLoader, arrayOf(interfaceClass), invocationHandler) @Suppress("UNCHECKED_CAST") - val deserialized = wrapAndDeserialize(proxyInstance) - - assertThatExceptionOfType(IllegalStateException::class.java) - .isThrownBy { deserialized.deserializeData(listOf(javaClass.classLoader)) } - - deserialized.deserializeData(listOf(URLClassLoader(arrayOf(libUrl), javaClass.classLoader))) + val deserialized = wrapAndDeserialize(proxyInstance, listOf(URLClassLoader(arrayOf(libUrl), javaClass.classLoader))) assertThat(deserialized.data.javaClass.interfaces) .extracting("name") .contains("foo.Baz") @@ -61,12 +50,10 @@ class DataNodeTest { val dataNodes = listOf(DataNode(Key.create(ProjectSystemId::class.java, 0), id, null), DataNode(Key.create(ProjectSystemId::class.java, 0), id, null)) - val buffer = WriteAndCompressSession() - dataNodes.forEach { it.serializeData(buffer) } val out = BufferExposingByteArrayOutputStream() - ObjectSerializer.instance.writeList(dataNodes, DataNode::class.java, out) + ObjectSerializer.instance.writeList(dataNodes, DataNode::class.java, out, createCacheWriteConfiguration()) val bytes = out.toByteArray() - val deserializedList = ObjectSerializer.instance.readList(DataNode::class.java, bytes, createDataNodeReadConfiguration(javaClass.classLoader)) + val deserializedList = ObjectSerializer.instance.readList(DataNode::class.java, bytes, createDataNodeReadConfiguration { name, _ -> javaClass.classLoader.loadClass(name) }) assertThat(deserializedList).hasSize(2) assertThat(deserializedList[0].data === deserializedList[1].data) @@ -91,16 +78,27 @@ class DataNodeTest { handler.ref = proxy assertThat(proxy.incrementAndGet()).isEqualTo(1) - val dataNode = wrapAndDeserialize(proxy) + val dataNode = wrapAndDeserialize(proxy, listOf(javaClass.classLoader)) val counter = dataNode.data as Counter assertThat(counter.incrementAndGet()).isEqualTo(2) } - private fun wrapAndDeserialize(barObject: Any): DataNode<*> { + private fun wrapAndDeserialize(barObject: Any, classLoaders: List): DataNode<*> { val original = DataNode(Key.create(barObject.javaClass, 0), barObject, null) - original.serializeData(WriteAndCompressSession()) - val bytes = ObjectSerializer.instance.writeAsBytes(original) - return ObjectSerializer.instance.read(DataNode::class.java, bytes) + val bytes = ObjectSerializer.instance.writeAsBytes(original, createCacheWriteConfiguration()) + return ObjectSerializer.instance.read(DataNode::class.java, bytes, createDataNodeReadConfiguration { name, _ -> + var lastException: ClassNotFoundException? = null + for (classLoader in classLoaders) { + try { + return@createDataNodeReadConfiguration classLoader.loadClass(name) + } + catch (e: ClassNotFoundException) { + lastException = e + } + } + + throw lastException!! + }) } } diff --git a/platform/external-system-impl/testSrc/com/intellij/openapi/externalSystem/service/project/manage/ProjectDataManagerImplTest.java b/platform/external-system-impl/testSrc/com/intellij/openapi/externalSystem/service/project/manage/ProjectDataManagerImplTest.java index d102f966ab82..8528251461b7 100644 --- a/platform/external-system-impl/testSrc/com/intellij/openapi/externalSystem/service/project/manage/ProjectDataManagerImplTest.java +++ b/platform/external-system-impl/testSrc/com/intellij/openapi/externalSystem/service/project/manage/ProjectDataManagerImplTest.java @@ -5,14 +5,12 @@ import com.intellij.openapi.externalSystem.model.DataNode; import com.intellij.openapi.externalSystem.model.Key; import com.intellij.openapi.externalSystem.model.ProjectKeys; import com.intellij.openapi.externalSystem.model.ProjectSystemId; -import com.intellij.openapi.externalSystem.model.project.*; +import com.intellij.openapi.externalSystem.model.project.ProjectData; import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider; -import com.intellij.openapi.externalSystem.test.ExternalSystemTestUtil; import com.intellij.openapi.externalSystem.util.Order; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Computable; import com.intellij.testFramework.PlatformTestCase; -import com.intellij.util.ReflectionUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -56,31 +54,6 @@ public class ProjectDataManagerImplTest extends PlatformTestCase { "removeDataAfter"); } - public void testBrokenDataNodePreparation() { - final LibraryDependencyData data = new LibraryDependencyData(new ModuleData("id", - ExternalSystemTestUtil.TEST_EXTERNAL_SYSTEM_ID, - "typeId", - "module_name", - "fake_path", - "fake_path"), - new LibraryData(ExternalSystemTestUtil.TEST_EXTERNAL_SYSTEM_ID, - "library_name"), - LibraryLevel.PROJECT); - - final DataNode badNode = - new DataNode(ProjectKeys.LIBRARY_DEPENDENCY, data, null) { - @Override - public void deserializeData(@NotNull Collection loaders) { - // mock a node that failed to deserialize it's data. - ReflectionUtil.resetField(this, "myData"); - throw new RuntimeException("Broken node can not be prepared properly"); - } - }; - - new ProjectDataManagerImpl(new LibraryDependencyDataService()) - .ensureTheDataIsReadyToUse(badNode); - } - @Order(1) static class RunAfterTestDataService extends TestDataService { static class MyObject { diff --git a/platform/lang-api/src/com/intellij/codeInsight/hints/InlayHintsSettings.kt b/platform/lang-api/src/com/intellij/codeInsight/hints/InlayHintsSettings.kt index 3ba137705afe..f4bf2db02ec2 100644 --- a/platform/lang-api/src/com/intellij/codeInsight/hints/InlayHintsSettings.kt +++ b/platform/lang-api/src/com/intellij/codeInsight/hints/InlayHintsSettings.kt @@ -9,7 +9,7 @@ import com.intellij.openapi.components.State import com.intellij.openapi.components.Storage import org.jdom.Element -@State(name = "InlayHintsSettings", storages = [Storage("inlayHints.xml")]) +@State(name = "InlayHintsSettings", storages = [Storage("workspace.xml")]) class InlayHintsSettings : PersistentStateComponent { private var myState = State() private val lock = Any() diff --git a/platform/lang-impl/src/com/intellij/codeInsight/hints/InlayHintsSinkImpl.kt b/platform/lang-impl/src/com/intellij/codeInsight/hints/InlayHintsSinkImpl.kt index d40c2ec9d492..2a196dceba2b 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/hints/InlayHintsSinkImpl.kt +++ b/platform/lang-impl/src/com/intellij/codeInsight/hints/InlayHintsSinkImpl.kt @@ -117,10 +117,10 @@ class InlayHintsSinkImpl(val key: SettingsKey) : InlayHintsSink { val previousPresentation = renderer.presentation @Suppress("UNCHECKED_CAST") newPresentation.addListener(InlayListener(inlay as Inlay)) + renderer.presentation = newPresentation if (newPresentation.updateState(previousPresentation)) { newPresentation.fireUpdateEvent(previousPresentation.dimension()) } - renderer.presentation = newPresentation hints.remove(offset) } } diff --git a/platform/lang-impl/src/com/intellij/execution/impl/ConsoleBuffer.java b/platform/lang-impl/src/com/intellij/execution/impl/ConsoleBuffer.java index 364d85004445..91abcfcaf6a1 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/ConsoleBuffer.java +++ b/platform/lang-impl/src/com/intellij/execution/impl/ConsoleBuffer.java @@ -12,7 +12,7 @@ public class ConsoleBuffer { public static int getCycleBufferSize() { UISettings uiSettings = UISettings.getInstance(); if (uiSettings.getOverrideConsoleCycleBufferSize()) { - return uiSettings.getConsoleCycleBufferSizeKb() * 1024; + return Math.min(Integer.MAX_VALUE / 1024, uiSettings.getConsoleCycleBufferSizeKb()) * 1024; } return getLegacyCycleBufferSize(); } diff --git a/platform/lang-impl/src/com/intellij/execution/services/ServiceViewManagerImpl.java b/platform/lang-impl/src/com/intellij/execution/services/ServiceViewManagerImpl.java index 49340566b552..443e9b1e70f5 100644 --- a/platform/lang-impl/src/com/intellij/execution/services/ServiceViewManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/services/ServiceViewManagerImpl.java @@ -362,6 +362,8 @@ public final class ServiceViewManagerImpl implements ServiceViewManager, Persist if (item != null && !viewModel.getChildren(item).isEmpty()) { AppUIUtil.invokeOnEdt(() -> { int index = myContentManager.getIndexOfContent(content); + if (index < 0) return; + myContentManager.removeContent(content, true); ServiceListModel listModel = new ServiceListModel(myModel, myModelFilter, ContainerUtil.newSmartList(item), viewModel.getFilter().getParent()); diff --git a/platform/lang-impl/src/com/intellij/execution/services/SplitToNewTabsAction.java b/platform/lang-impl/src/com/intellij/execution/services/SplitToNewTabsAction.java index f3e4729102c2..928074eaaccd 100644 --- a/platform/lang-impl/src/com/intellij/execution/services/SplitToNewTabsAction.java +++ b/platform/lang-impl/src/com/intellij/execution/services/SplitToNewTabsAction.java @@ -10,7 +10,6 @@ import com.intellij.openapi.project.Project; import org.jetbrains.annotations.NotNull; import java.util.Collections; -import java.util.List; import static com.intellij.execution.services.ServiceViewActionProvider.getSelectedView; @@ -18,8 +17,7 @@ public class SplitToNewTabsAction extends DumbAwareAction { @Override public void update(@NotNull AnActionEvent e) { ServiceView serviceView = getSelectedView(e); - List items = serviceView == null ? Collections.emptyList() : serviceView.getSelectedItems(); - boolean enabled = !items.isEmpty() && items.stream().allMatch(item -> item instanceof ServiceModel.ServiceNode); + boolean enabled = serviceView != null && !serviceView.getSelectedItems().isEmpty(); e.getPresentation().setEnabled(enabled); e.getPresentation().setVisible(enabled || !ActionPlaces.isPopupPlace(e.getPlace())); } diff --git a/platform/lang-impl/src/com/intellij/find/impl/FindInProjectRecents.java b/platform/lang-impl/src/com/intellij/find/impl/FindInProjectRecents.java index cbd7d654cb68..a73824e26d69 100644 --- a/platform/lang-impl/src/com/intellij/find/impl/FindInProjectRecents.java +++ b/platform/lang-impl/src/com/intellij/find/impl/FindInProjectRecents.java @@ -1,28 +1,19 @@ -/* - * Copyright 2000-2016 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-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. package com.intellij.find.impl; import com.intellij.find.FindInProjectSettings; -import com.intellij.openapi.components.*; +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.components.State; +import com.intellij.openapi.components.Storage; +import com.intellij.openapi.components.StoragePathMacros; import com.intellij.openapi.project.Project; @State( name = "FindInProjectRecents", - storages = {@Storage(value = StoragePathMacros.WORKSPACE_FILE, roamingType = RoamingType.DISABLED)} -) + storages = { + @Storage(StoragePathMacros.PRODUCT_WORKSPACE_FILE), + @Storage(value = StoragePathMacros.WORKSPACE_FILE, deprecated = true) + }) final class FindInProjectRecents extends FindInProjectSettingsBase implements FindInProjectSettings { public static FindInProjectSettings getInstance(Project project) { return ServiceManager.getService(project, FindInProjectSettings.class); diff --git a/platform/lang-impl/src/com/intellij/ide/AttachedModuleAwareRecentProjectsManager.java b/platform/lang-impl/src/com/intellij/ide/AttachedModuleAwareRecentProjectsManager.java index 02c70b12afca..f979f21e2137 100644 --- a/platform/lang-impl/src/com/intellij/ide/AttachedModuleAwareRecentProjectsManager.java +++ b/platform/lang-impl/src/com/intellij/ide/AttachedModuleAwareRecentProjectsManager.java @@ -1,19 +1,14 @@ -// 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-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. package com.intellij.ide; import com.intellij.openapi.project.Project; import com.intellij.platform.ModuleAttachProcessor; -import com.intellij.util.messages.MessageBus; import org.jetbrains.annotations.NotNull; /** * Used by IDEs where attaching modules is supported. */ final class AttachedModuleAwareRecentProjectsManager extends RecentDirectoryProjectsManager { - AttachedModuleAwareRecentProjectsManager(@NotNull MessageBus messageBus) { - super(messageBus); - } - @NotNull @Override protected String getProjectDisplayName(@NotNull Project project) { diff --git a/platform/lang-impl/src/com/intellij/ide/actions/SaveAsAction.java b/platform/lang-impl/src/com/intellij/ide/actions/SaveAsAction.java index 3748115b2ffc..9cbcd4b05e44 100644 --- a/platform/lang-impl/src/com/intellij/ide/actions/SaveAsAction.java +++ b/platform/lang-impl/src/com/intellij/ide/actions/SaveAsAction.java @@ -1,5 +1,6 @@ package com.intellij.ide.actions; +import com.intellij.notebook.editor.BackedVirtualFile; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.project.DumbAwareAction; @@ -23,6 +24,9 @@ public class SaveAsAction extends DumbAwareAction { public void actionPerformed(@NotNull AnActionEvent e) { Project project = e.getProject(); VirtualFile virtualFile = e.getData(CommonDataKeys.VIRTUAL_FILE); + if (virtualFile instanceof BackedVirtualFile) { + virtualFile = ((BackedVirtualFile)virtualFile).getOriginFile(); + } if (project == null || virtualFile == null) return; PsiElement element = PsiManager.getInstance(project).findFile(virtualFile); if (element == null) return; diff --git a/platform/lang-impl/src/com/intellij/ide/bookmarks/BookmarkManager.java b/platform/lang-impl/src/com/intellij/ide/bookmarks/BookmarkManager.java index b54caafd2267..ee97d275540a 100644 --- a/platform/lang-impl/src/com/intellij/ide/bookmarks/BookmarkManager.java +++ b/platform/lang-impl/src/com/intellij/ide/bookmarks/BookmarkManager.java @@ -43,8 +43,11 @@ import java.awt.event.InputEvent; import java.util.List; import java.util.*; -@State(name = "BookmarkManager", storages = @Storage(StoragePathMacros.WORKSPACE_FILE)) -public class BookmarkManager implements PersistentStateComponent { +@State(name = "BookmarkManager", storages = { + @Storage(StoragePathMacros.PRODUCT_WORKSPACE_FILE), + @Storage(value = StoragePathMacros.WORKSPACE_FILE, deprecated = true) +}) +public final class BookmarkManager implements PersistentStateComponent { private static final int MAX_AUTO_DESCRIPTION_SIZE = 50; private final MultiMap myBookmarks = MultiMap.createConcurrentSet(); private final Map, Bookmark> myDeletedDocumentBookmarks = new HashMap<>(); diff --git a/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectViewImpl.java b/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectViewImpl.java index c351ef2cfed8..ccd2d8c11fc3 100644 --- a/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectViewImpl.java +++ b/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectViewImpl.java @@ -91,7 +91,10 @@ import java.awt.*; import java.util.List; import java.util.*; -@State(name = "ProjectView", storages = @Storage(StoragePathMacros.WORKSPACE_FILE)) +@State(name = "ProjectView", storages = { + @Storage(StoragePathMacros.PRODUCT_WORKSPACE_FILE), + @Storage(value = StoragePathMacros.WORKSPACE_FILE, deprecated = true) +}) public class ProjectViewImpl extends ProjectView implements PersistentStateComponent, Disposable, QuickActionProvider, BusyObject { private static final Logger LOG = Logger.getInstance("#com.intellij.ide.projectView.impl.ProjectViewImpl"); private static final Key ID_KEY = Key.create("pane-id"); diff --git a/platform/lang-impl/src/com/intellij/ide/scratch/ScratchFileServiceImpl.java b/platform/lang-impl/src/com/intellij/ide/scratch/ScratchFileServiceImpl.java index 1e21469b0a94..1010f88d7162 100644 --- a/platform/lang-impl/src/com/intellij/ide/scratch/ScratchFileServiceImpl.java +++ b/platform/lang-impl/src/com/intellij/ide/scratch/ScratchFileServiceImpl.java @@ -173,13 +173,6 @@ public class ScratchFileServiceImpl extends ScratchFileService implements Persis } } - public static class TypeFactory extends FileTypeFactory { - @Override - public void createFileTypes(@NotNull FileTypeConsumer consumer) { - consumer.consume(ScratchFileType.INSTANCE); - } - } - public static class Substitutor extends LanguageSubstitutor { @Nullable @Override diff --git a/platform/lang-impl/src/com/intellij/internal/statistic/actions/TestParseEventLogWhitelistDialog.java b/platform/lang-impl/src/com/intellij/internal/statistic/actions/TestParseEventLogWhitelistDialog.java index 5111823af61a..bf11b70cb9dd 100644 --- a/platform/lang-impl/src/com/intellij/internal/statistic/actions/TestParseEventLogWhitelistDialog.java +++ b/platform/lang-impl/src/com/intellij/internal/statistic/actions/TestParseEventLogWhitelistDialog.java @@ -186,8 +186,7 @@ public class TestParseEventLogWhitelistDialog extends DialogWrapper { myResultEditor.getSelectionModel().removeSelection(); updateResultRequest("{}"); - final BuildNumber build = BuildNumber.fromString(EventLogConfiguration.INSTANCE.getBuild()); - final FUSWhitelist whitelist = FUStatisticsWhiteListGroupsService.parseApprovedGroups(myWhitelistEditor.getDocument().getText(), build); + final FUSWhitelist whitelist = FUStatisticsWhiteListGroupsService.parseApprovedGroups(myWhitelistEditor.getDocument().getText()); try { final String parsed = parseLogAndFilter(new LogEventWhitelistFilter(whitelist), myEventLogPanel.getText()); updateResultRequest(parsed.trim()); diff --git a/platform/lang-impl/src/com/intellij/psi/stubs/PrebuiltStubs.kt b/platform/lang-impl/src/com/intellij/psi/stubs/PrebuiltStubs.kt index 7cdb6fd3ae68..a7e0c9e1d32c 100644 --- a/platform/lang-impl/src/com/intellij/psi/stubs/PrebuiltStubs.kt +++ b/platform/lang-impl/src/com/intellij/psi/stubs/PrebuiltStubs.kt @@ -88,7 +88,7 @@ abstract class PrebuiltStubsProviderBase : PrebuiltIndexProviderBase String mappings due to StringRefs and stubs indexing that initially creates stubs (doing enumerate on String) // and then index them (valueOf), also similar string items are expected to be enumerated during stubs processing myNameStorage = new PersistentStringEnumerator(myFile, true); - myStubSerializationHelper = new StubSerializationHelper(myNameStorage, this); + myStubSerializationHelper = new StubSerializationHelper(myNameStorage, unmodifiable, this); } catch (IOException e) { nameStorageCrashed(); @@ -83,10 +84,13 @@ public class SerializationManagerImpl extends SerializationManagerEx implements } StubSerializationHelper prevHelper = myStubSerializationHelper; + if (myUnmodifiable) { + LOG.error("Data provided by unmodifiable serialization manager can be invalid after repair"); + } IOUtil.deleteAllFilesStartingWith(myFile); myNameStorage = new PersistentStringEnumerator(myFile, true); - myStubSerializationHelper = new StubSerializationHelper(myNameStorage, this); + myStubSerializationHelper = new StubSerializationHelper(myNameStorage, myUnmodifiable, this); myStubSerializationHelper.copyFrom(prevHelper); } catch (IOException e) { diff --git a/platform/lang-impl/src/com/intellij/psi/stubs/StubSerializationHelper.java b/platform/lang-impl/src/com/intellij/psi/stubs/StubSerializationHelper.java index a897b1580832..297e2a118fdb 100644 --- a/platform/lang-impl/src/com/intellij/psi/stubs/StubSerializationHelper.java +++ b/platform/lang-impl/src/com/intellij/psi/stubs/StubSerializationHelper.java @@ -14,6 +14,7 @@ import com.intellij.util.containers.RecentStringInterner; import com.intellij.util.io.AbstractStringEnumerator; import com.intellij.util.io.DataInputOutputUtil; import com.intellij.util.io.IOUtil; +import com.intellij.util.io.PersistentStringEnumerator; import gnu.trove.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -25,7 +26,9 @@ import java.util.*; * Author: dmitrylomov */ class StubSerializationHelper { - private final AbstractStringEnumerator myNameStorage; + private static final Logger LOG = Logger.getInstance(StubSerializationHelper.class); + + private final PersistentStringEnumerator myNameStorage; private final TIntObjectHashMap myIdToName = new TIntObjectHashMap<>(); private final TObjectIntHashMap myNameToId = new TObjectIntHashMap<>(); @@ -34,10 +37,12 @@ class StubSerializationHelper { private final ConcurrentIntObjectMap myIdToSerializer = ContainerUtil.createConcurrentIntObjectMap(); private final Map mySerializerToId = ContainerUtil.newConcurrentMap(); + private final boolean myUnmodifiable; private final RecentStringInterner myStringInterner; - StubSerializationHelper(@NotNull AbstractStringEnumerator nameStorage, @NotNull Disposable parentDisposable) { + StubSerializationHelper(@NotNull PersistentStringEnumerator nameStorage, boolean unmodifiable, @NotNull Disposable parentDisposable) { myNameStorage = nameStorage; + myUnmodifiable = unmodifiable; myStringInterner = new RecentStringInterner(parentDisposable); } @@ -53,7 +58,17 @@ class StubSerializationHelper { return; } - int id = myNameStorage.enumerate(name); + int id; + if (myUnmodifiable) { + id = myNameStorage.tryEnumerate(name); + if (id == 0) { + LOG.info("serialized " + name + " is ignored in unmodifiable stub serialization manager"); + return; + } + } + else { + id = myNameStorage.enumerate(name); + } myIdToName.put(id, name); myNameToId.put(name, id); } diff --git a/platform/lang-impl/testSources/com/intellij/codeInsight/hints/PresentationTest.kt b/platform/lang-impl/testSources/com/intellij/codeInsight/hints/PresentationTest.kt index 566d3804c6d4..a1dc23f95674 100644 --- a/platform/lang-impl/testSources/com/intellij/codeInsight/hints/PresentationTest.kt +++ b/platform/lang-impl/testSources/com/intellij/codeInsight/hints/PresentationTest.kt @@ -126,7 +126,7 @@ class HeavyPresentationTest : LightPlatformCodeInsightFixtureTestCase() { } fun testFoldedStateIsNotUpdatedAndStatelessComponentIsUpdated() { - val factory = PresentationFactory(myFixture.editor as EditorImpl) + val factory = getFactory() val old = unwrapFolding(factory.folding(factory.smallText("outerPlaceholder")) { unwrapFolding(factory.folding(factory.smallText("innerPlaceholder")) {factory.smallText("text")}) @@ -142,8 +142,7 @@ class HeavyPresentationTest : LightPlatformCodeInsightFixtureTestCase() { } fun testFoldedBiState() { - val factory = PresentationFactory(myFixture.editor as EditorImpl) - with(factory) { + with(getFactory()) { val inner = collapsible( prefix = smallText("("), collapsed = smallText("???"), @@ -179,6 +178,18 @@ class HeavyPresentationTest : LightPlatformCodeInsightFixtureTestCase() { } } + fun testSeqSmallTextChangesWidth() { + with(getFactory()) { + val initial = roundWithBackground(seq(smallText(": "), smallText("number"))) + val final = roundWithBackground(seq(smallText(": "), smallText("void"))) + val requiresUpdate = final.updateState(initial) + assertTrue(requiresUpdate) + assertTrue(initial.width != final.width) + } + } + + private fun getFactory() = PresentationFactory(myFixture.editor as EditorImpl) + private fun unwrapFolding(presentation: InlayPresentation): InlayPresentation { presentation as ChangeOnClickPresentation presentation.state = ChangeOnClickPresentation.State(true) diff --git a/platform/object-serializer/src/BeanBinding.kt b/platform/object-serializer/src/BeanBinding.kt index 77938b809fc9..d0027a8f60b8 100644 --- a/platform/object-serializer/src/BeanBinding.kt +++ b/platform/object-serializer/src/BeanBinding.kt @@ -130,13 +130,18 @@ internal class BeanBinding(beanClass: Class<*>) : BaseBeanBinding(beanClass), Bi } } - val instance = try { + var instance = try { constructorInfo.constructor.newInstance(*initArgs) } catch (e: Exception) { throw SerializationException("Cannot create instance (beanClass=${beanClass.name}, initArgs=${initArgs.joinToString()})", e) } + // must be called after creation because child properties can reference object + context.configuration.beanConstructed?.let { + instance = it(instance) + } + if (id != -1) { context.objectIdReader.registerObject(instance, id) } @@ -147,7 +152,7 @@ internal class BeanBinding(beanClass: Class<*>) : BaseBeanBinding(beanClass), Bi readIntoObject(instance, context.createSubContext(reader), checkId = false /* already registered */) { !names.contains(it) } } } - return context.configuration.beanConstructed?.let { it(instance) } ?: instance + return instance } override fun deserialize(context: ReadContext): Any { @@ -159,7 +164,11 @@ internal class BeanBinding(beanClass: Class<*>) : BaseBeanBinding(beanClass), Bi return context.objectIdReader.getObject(reader.intValue()) } else if (ionType != IonType.STRUCT) { - throw SerializationException("Expected STRUCT, but got $ionType") + var stringValue = "" + if (ionType == IonType.SYMBOL || ionType == IonType.STRING) { + stringValue = reader.stringValue() + } + throw SerializationException("Expected STRUCT, but got $ionType (stringValue=$stringValue)") } if (propertyMapping.isInitialized()) { @@ -215,7 +224,7 @@ internal class BeanBinding(beanClass: Class<*>) : BaseBeanBinding(beanClass), Bi throw e } catch (e: Exception) { - context.errors.fields.add(ReadError("Cannot deserialize field value (field=$fieldName, binding=$binding, valueType=${reader.type}, beanClass=${beanClass.name})", e)) + throw SerializationException("Cannot deserialize field value (field=$fieldName, binding=$binding, valueType=${reader.type}, beanClass=${beanClass.name})", e) } } } diff --git a/platform/object-serializer/src/Binding.kt b/platform/object-serializer/src/Binding.kt index b34390a39e72..a5e0794a86fb 100644 --- a/platform/object-serializer/src/Binding.kt +++ b/platform/object-serializer/src/Binding.kt @@ -51,7 +51,7 @@ internal inline fun write(hostObject: Any, accessor: MutableAccessor, context: W } } -internal inline fun read(hostObject: Any, property: MutableAccessor, context: ReadContext, read: ValueReader.() -> Any) { +internal inline fun read(hostObject: Any, property: MutableAccessor, context: ReadContext, read: ValueReader.() -> Any?) { if (context.reader.type == IonType.NULL) { property.set(hostObject, null) } diff --git a/platform/object-serializer/src/IonObjectSerializer.kt b/platform/object-serializer/src/IonObjectSerializer.kt index 699e150b77dd..559aebd913b1 100644 --- a/platform/object-serializer/src/IonObjectSerializer.kt +++ b/platform/object-serializer/src/IonObjectSerializer.kt @@ -4,26 +4,19 @@ package com.intellij.serialization import com.amazon.ion.IonException import com.amazon.ion.IonType import com.amazon.ion.IonWriter -import com.amazon.ion.impl.bin.Block -import com.amazon.ion.impl.bin.BlockAllocator -import com.amazon.ion.impl.bin.BlockAllocatorProvider import com.amazon.ion.impl.bin._Private_IonManagedBinaryWriterBuilder import com.amazon.ion.system.IonReaderBuilder import com.amazon.ion.system.IonTextWriterBuilder import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream import com.intellij.util.ParameterizedTypeImpl -import com.intellij.util.containers.ContainerUtil -import org.jetbrains.annotations.TestOnly import java.io.IOException import java.io.InputStream import java.io.OutputStream import java.lang.reflect.Type import java.nio.file.Path -import java.util.* -import java.util.concurrent.atomic.AtomicInteger import kotlin.experimental.or -private const val FORMAT_VERSION = 1 +private const val FORMAT_VERSION = 2 internal class IonObjectSerializer { val readerBuilder: IonReaderBuilder = IonReaderBuilder.standard().immutable() @@ -207,86 +200,4 @@ private fun createIonWriterBuilder(binary: Boolean, out: OutputStream): IonWrite binary -> binaryWriterBuilder.newWriter(out) else -> textWriterBuilder.build(out) } -} - -internal class PooledBlockAllocatorProvider : BlockAllocatorProvider() { - companion object { - // 512 KB - internal const val POOL_THRESHOLD = 512 * 1024 - } - - @Suppress("RemoveExplicitTypeArguments") - private val allocators = ContainerUtil.createConcurrentIntObjectMap() - - private inner class PooledBlockAllocator(private val blockSize: Int) : BlockAllocator() { - private val freeBlocks = ArrayList() - - private val blockCounter = AtomicInteger() - - val byteSize: Int - get() = blockCounter.get() * blockSize - - override fun allocateBlock(): Block { - val lastIndex = freeBlocks.lastIndex - if (lastIndex != -1) { - return freeBlocks.removeAt(lastIndex) - } - - blockCounter.incrementAndGet() - return object : Block(ByteArray(blockSize)) { - override fun close() { - reset() - freeBlocks.add(this) - } - } - } - - override fun getBlockSize() = blockSize - - override fun close() { - if (allocators.putIfAbsent(blockSize, this) != null) { - // help GC - nullize - freeBlocks.clear() - blockCounter.set(0) - } - } - } - - @get:TestOnly - val byteSize: Int - get() { - var totalByteSize = 0 - for (allocator in allocators.elements()) { - totalByteSize += allocator.byteSize - } - return totalByteSize - } - - override fun vendAllocator(blockSize: Int): BlockAllocator { - if (blockSize <= 0) { - throw IllegalArgumentException("Invalid block size: $blockSize") - } - - // PooledBlockAllocator is not thread safe - do not put a new one to pool - val result = allocators.remove(blockSize) ?: PooledBlockAllocator(blockSize) - - var totalByteSize = 0 - val iterator = allocators.values().iterator() - var isExcess = false - while (iterator.hasNext()) { - val allocator = iterator.next() - if (isExcess) { - iterator.remove() - continue - } - - totalByteSize += allocator.byteSize - if (totalByteSize > POOL_THRESHOLD) { - iterator.remove() - isExcess = true - } - } - - return result - } } \ No newline at end of file diff --git a/platform/object-serializer/src/MapBinding.kt b/platform/object-serializer/src/MapBinding.kt index f0ad2636a978..8945fe526ccc 100644 --- a/platform/object-serializer/src/MapBinding.kt +++ b/platform/object-serializer/src/MapBinding.kt @@ -22,12 +22,22 @@ internal class MapBinding(keyType: Type, valueType: Type, context: BindingInitia return } - fun writeEntry(key: Any?, value: Any?) { - if (key == null) { - writer.writeNull() + fun writeEntry(key: Any?, value: Any?, isStringKey: Boolean) { + if (isStringKey) { + if (key == null) { + throw SerializationException("null string keys not supported") + } + else { + writer.setFieldName(key as String) + } } else { - keyBinding.serialize(key, context) + if (key == null) { + writer.writeNull() + } + else { + keyBinding.serialize(key, context) + } } if (value == null) { @@ -38,7 +48,8 @@ internal class MapBinding(keyType: Type, valueType: Type, context: BindingInitia } } - writer.stepIn(IonType.LIST) + val isStringKey = keyBinding is StringBinding + writer.stepIn(if (isStringKey) IonType.STRUCT else IonType.LIST) if (context.configuration.orderMapEntriesByKeys && isKeyComparable && map !is SortedMap<*, *> && map !is LinkedHashMap<*, *>) { val keys = ArrayUtil.toObjectArray(map.keys) Arrays.sort(keys) { a, b -> @@ -50,18 +61,20 @@ internal class MapBinding(keyType: Type, valueType: Type, context: BindingInitia } } for (key in keys) { - writeEntry(key, map.get(key)) + writeEntry(key, map.get(key), isStringKey) } } else { if (map is THashMap) { map.forEachEntry { k: Any?, v: Any? -> - writeEntry(k, v) + writeEntry(k, v, isStringKey) true } } else { - map.forEach(::writeEntry) + map.forEach { + key, value -> writeEntry(key, value, isStringKey) + } } } writer.stepOut() @@ -93,11 +106,26 @@ internal class MapBinding(keyType: Type, valueType: Type, context: BindingInitia private fun readInto(result: MutableMap, context: ReadContext) { val reader = context.reader + + if (reader.type == IonType.INT) { + LOG.assertTrue(context.reader.intValue() == 0) + return + } + + val isStringKeys = reader.type == IonType.STRUCT reader.stepIn() while (true) { - val key = read(reader.next() ?: break, keyBinding, context) - val value = read(reader.next() ?: break, valueBinding, context) - result.put(key, value) + if (isStringKeys) { + val type = reader.next() ?: break + val key = reader.fieldName + val value = read(type, valueBinding, context) + result.put(key, value) + } + else { + val key = read(reader.next() ?: break, keyBinding, context) + val value = read(reader.next() ?: break, valueBinding, context) + result.put(key, value) + } } reader.stepOut() } diff --git a/platform/object-serializer/src/ObjectSerializer.kt b/platform/object-serializer/src/ObjectSerializer.kt index 56cec93a9848..e3a92a93b36c 100644 --- a/platform/object-serializer/src/ObjectSerializer.kt +++ b/platform/object-serializer/src/ObjectSerializer.kt @@ -15,6 +15,8 @@ import java.io.Reader internal typealias ValueReader = IonReader internal typealias ValueWriter = IonWriter + +// not fully initialized object may be passed (only created instance without properties) if object has PropertyMapping annotation typealias BeanConstructed = (instance: Any) -> Any internal val defaultWriteConfiguration = WriteConfiguration() diff --git a/platform/object-serializer/src/PolymorphicBinding.kt b/platform/object-serializer/src/PolymorphicBinding.kt index da95ba128a2f..c157f70bd923 100644 --- a/platform/object-serializer/src/PolymorphicBinding.kt +++ b/platform/object-serializer/src/PolymorphicBinding.kt @@ -14,7 +14,17 @@ internal class PolymorphicBinding(private val superClass: Class<*>) : Binding { context.bindingProducer.getRootBinding(valueClass).serialize(obj, context) } + override fun deserialize(hostObject: Any, property: MutableAccessor, context: ReadContext) { + read(hostObject, property, context) { + doDeserialize(context, hostObject) + } + } + override fun deserialize(context: ReadContext): Any { + return doDeserialize(context, null)!! + } + + private fun doDeserialize(context: ReadContext, hostObject: Any?): Any? { if (!context.configuration.allowAnySubTypes) { throw SerializationException("Polymorphic type without specified allowed sub types is forbidden") } @@ -24,7 +34,15 @@ internal class PolymorphicBinding(private val superClass: Class<*>) : Binding { val typeAnnotationIterator = reader.iterateTypeAnnotations() if (typeAnnotationIterator.hasNext()) { val className = typeAnnotationIterator.next() - beanClass = (context.configuration.classLoader ?: javaClass.classLoader).loadClass(className) + val loadClass = context.configuration.loadClass + // loadClass for now doesn't support map or collection as host object + if (loadClass == null || hostObject == null) { + beanClass = javaClass.classLoader.loadClass(className) + } + else { + beanClass = loadClass(className, hostObject) ?: return null + } + if (!superClass.isAssignableFrom(beanClass)) { throw SerializationException("Class \"$className\" must be assignable to \"${superClass.name}\"") } diff --git a/platform/object-serializer/src/PooledBlockAllocatorProvider.kt b/platform/object-serializer/src/PooledBlockAllocatorProvider.kt new file mode 100644 index 000000000000..054c789080a0 --- /dev/null +++ b/platform/object-serializer/src/PooledBlockAllocatorProvider.kt @@ -0,0 +1,103 @@ +// 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. +package com.intellij.serialization + +import com.amazon.ion.impl.bin.Block +import com.amazon.ion.impl.bin.BlockAllocator +import com.amazon.ion.impl.bin.BlockAllocatorProvider +import com.intellij.util.containers.ContainerUtil +import org.jetbrains.annotations.TestOnly +import java.util.* +import java.util.concurrent.atomic.AtomicInteger + +internal class PooledBlockAllocatorProvider : BlockAllocatorProvider() { + companion object { + // 512 KB + internal const val POOL_THRESHOLD = 512 * 1024 + } + + @Suppress("RemoveExplicitTypeArguments") + private val allocators = ContainerUtil.createConcurrentIntObjectMap() + + private inner class PooledBlockAllocator(private val blockSize: Int) : BlockAllocator() { + private val freeBlocks = ArrayList() + + private val blockCounter = AtomicInteger() + + val byteSize: Int + get() = blockCounter.get() * blockSize + + override fun allocateBlock(): Block { + val lastIndex = freeBlocks.lastIndex + if (lastIndex != -1) { + return freeBlocks.removeAt(lastIndex) + } + + blockCounter.incrementAndGet() + return object : Block(ByteArray(blockSize)) { + override fun close() { + reset() + freeBlocks.add(this) + } + } + } + + override fun getBlockSize() = blockSize + + override fun close() { + if ((blockSize * freeBlocks.size) > POOL_THRESHOLD) { + return + } + + if (allocators.putIfAbsent(blockSize, this) == null) { + removeExcess() + } + else { + // help GC - nullize + freeBlocks.clear() + blockCounter.set(0) + } + } + } + + @get:TestOnly + val byteSize: Int + get() { + var totalByteSize = 0 + for (allocator in allocators.elements()) { + totalByteSize += allocator.byteSize + } + return totalByteSize + } + + override fun vendAllocator(blockSize: Int): BlockAllocator { + if (blockSize <= 0) { + throw IllegalArgumentException("Invalid block size: $blockSize") + } + + // PooledBlockAllocator is not thread safe - do not put a new one to pool + val result = allocators.remove(blockSize) ?: PooledBlockAllocator(blockSize) + + removeExcess() + + return result + } + + private fun removeExcess() { + var totalByteSize = 0 + val iterator = allocators.values().iterator() + var isExcess = false + while (iterator.hasNext()) { + val allocator = iterator.next() + if (isExcess) { + iterator.remove() + continue + } + + totalByteSize += allocator.byteSize + if (totalByteSize > POOL_THRESHOLD) { + iterator.remove() + isExcess = true + } + } + } +} \ No newline at end of file diff --git a/platform/object-serializer/src/VersionedFile.kt b/platform/object-serializer/src/VersionedFile.kt index e663d89dcf7b..cca794105b3a 100644 --- a/platform/object-serializer/src/VersionedFile.kt +++ b/platform/object-serializer/src/VersionedFile.kt @@ -41,10 +41,9 @@ data class VersionedFile @JvmOverloads constructor(val file: Path, val version: @Throws(IOException::class, SerializationException::class) @JvmOverloads - fun readList(itemClass: Class, beanConstructed: BeanConstructed? = null): List? { - val configuration = ReadConfiguration(beanConstructed = beanConstructed) + fun readList(itemClass: Class, configuration: ReadConfiguration = ReadConfiguration(), renameToCorruptedOnError: Boolean = true): List? { @Suppress("UNCHECKED_CAST") - return readAndHandleErrors(ArrayList::class.java, configuration, originalType = ParameterizedTypeImpl(ArrayList::class.java, itemClass)) as List? + return readAndHandleErrors(ArrayList::class.java, configuration, originalType = ParameterizedTypeImpl(ArrayList::class.java, itemClass), renameToCorruptedOnError = renameToCorruptedOnError) as List? } @Throws(IOException::class, SerializationException::class) @@ -53,20 +52,22 @@ data class VersionedFile @JvmOverloads constructor(val file: Path, val version: return readAndHandleErrors(objectClass, ReadConfiguration(beanConstructed = beanConstructed)) } - private fun readAndHandleErrors(objectClass: Class, configuration: ReadConfiguration, originalType: Type? = null): T? { + private fun readAndHandleErrors(objectClass: Class, configuration: ReadConfiguration, originalType: Type? = null, renameToCorruptedOnError: Boolean = true): T? { return readPossiblyCompressedIonFile(file) { input -> val result = try { ObjectSerializer.instance.serializer.readVersioned(objectClass, input, file, version, originalType = originalType, configuration = configuration) } catch (e: Exception) { - renameSilentlyToCorrupted() + if (renameToCorruptedOnError) { + renameSilentlyToCorrupted() + } // in tests log will throw error, renameSilentlyToCorrupted is called before LOG.error(e) return null } - if (result == null) { + if (result == null && renameToCorruptedOnError) { renameSilentlyToCorrupted() } return result diff --git a/platform/object-serializer/src/context.kt b/platform/object-serializer/src/context.kt index 617800bef752..8a88d90d8a88 100644 --- a/platform/object-serializer/src/context.kt +++ b/platform/object-serializer/src/context.kt @@ -6,7 +6,8 @@ import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream import com.intellij.util.SmartList data class ReadConfiguration(val allowAnySubTypes: Boolean = false, - val classLoader: ClassLoader? = null, + // loadClass for now doesn't support map or collection as host object + val loadClass: ((name: String, hostObject: Any) -> Class<*>?)? = null, val beanConstructed: BeanConstructed? = null) data class WriteConfiguration(val binary: Boolean = true, diff --git a/platform/object-serializer/src/primitiveBindings.kt b/platform/object-serializer/src/primitiveBindings.kt index 1d216acdc205..8e7cd5caf499 100644 --- a/platform/object-serializer/src/primitiveBindings.kt +++ b/platform/object-serializer/src/primitiveBindings.kt @@ -166,7 +166,7 @@ private class DoubleBinding : Binding { } } -private class StringBinding : Binding { +internal class StringBinding : Binding { override fun deserialize(context: ReadContext): Any { return context.reader.stringValue() } diff --git a/platform/object-serializer/testInternalSrc/InternalExposer.kt b/platform/object-serializer/testInternalSrc/InternalExposer.kt index 6a38000d829b..ac5b52a6ed6d 100644 --- a/platform/object-serializer/testInternalSrc/InternalExposer.kt +++ b/platform/object-serializer/testInternalSrc/InternalExposer.kt @@ -22,7 +22,7 @@ fun testThreadLocalPooledBlockAllocatorProvider() { allocated += PooledBlockAllocatorProvider.POOL_THRESHOLD provider.vendAllocator(PooledBlockAllocatorProvider.POOL_THRESHOLD).use { it.allocateBlock() } - assertThat(provider.byteSize).isEqualTo(allocated) + assertThat(provider.byteSize).isLessThanOrEqualTo(2049) provider.vendAllocator(PooledBlockAllocatorProvider.POOL_THRESHOLD + 1).use { it.allocateBlock() } assertThat(provider.byteSize).isLessThanOrEqualTo(allocated + 1) diff --git a/platform/object-serializer/testInternalSrc/TestApp.kt b/platform/object-serializer/testInternalSrc/TestApp.kt index b4b867aa6fe5..8f4b0dfedd34 100644 --- a/platform/object-serializer/testInternalSrc/TestApp.kt +++ b/platform/object-serializer/testInternalSrc/TestApp.kt @@ -13,7 +13,7 @@ class TestApp { companion object { @JvmStatic fun main(args: Array) { - val inputFile = Paths.get(args[0]) + val inputFile = Paths.get(args[0].trim()) val outFile = inputFile.parent.resolve(FileUtilRt.getNameWithoutExtension(inputFile.fileName.toString()) + "-text.ion") readPossiblyCompressedIonFile(inputFile) { input -> diff --git a/platform/object-serializer/testSnapshots/bean map.ion b/platform/object-serializer/testSnapshots/bean map.ion index f2ccbe30bc68..72de8f050d36 100644 --- a/platform/object-serializer/testSnapshots/bean map.ion +++ b/platform/object-serializer/testSnapshots/bean map.ion @@ -1,29 +1,24 @@ { '@id':0, - map:[ - ], + map:{ + }, beanMap:[ { '@id':1, - map:[ - A, - VB, - Z, - '123', - 'a-A', - '123', - foo, - bar - ], + map:{ + A:VB, + Z:'123', + 'a-A':'123', + foo:bar + }, beanMap:[ ] }, { '@id':2, - map:[ - 'some key', - 'some value' - ], + map:{ + 'some key':'some value' + }, beanMap:[ ] } diff --git a/platform/object-serializer/testSnapshots/empty map.ion b/platform/object-serializer/testSnapshots/empty map.ion new file mode 100644 index 000000000000..16db4349288a --- /dev/null +++ b/platform/object-serializer/testSnapshots/empty map.ion @@ -0,0 +1,4 @@ +{ + '@id':0, + map:0 +} \ No newline at end of file diff --git a/platform/object-serializer/testSnapshots/interface type for map value - allowSubTypes.ion b/platform/object-serializer/testSnapshots/interface type for map value - allowSubTypes.ion index e175c9839fe3..2da0aace90bf 100644 --- a/platform/object-serializer/testSnapshots/interface type for map value - allowSubTypes.ion +++ b/platform/object-serializer/testSnapshots/interface type for map value - allowSubTypes.ion @@ -1,10 +1,9 @@ { '@id':0, - shape:[ - first, - 'com.intellij.serialization.Circle'::{ + shape:{ + first:'com.intellij.serialization.Circle'::{ '@id':1, name:null } - ] + } } \ No newline at end of file diff --git a/platform/object-serializer/testSnapshots/map.ion b/platform/object-serializer/testSnapshots/map.ion index 6fa2fef32791..751f2f55048d 100644 --- a/platform/object-serializer/testSnapshots/map.ion +++ b/platform/object-serializer/testSnapshots/map.ion @@ -1,9 +1,8 @@ { '@id':0, - map:[ - foo, - bar - ], + map:{ + foo:bar + }, beanMap:[ ] } \ No newline at end of file diff --git a/platform/object-serializer/testSnapshots/parametrized type as map value.ion b/platform/object-serializer/testSnapshots/parametrized type as map value.ion index a7021ba11bde..b60cc5f4c24c 100644 --- a/platform/object-serializer/testSnapshots/parametrized type as map value.ion +++ b/platform/object-serializer/testSnapshots/parametrized type as map value.ion @@ -1,9 +1,8 @@ { '@id':0, - map:[ - bar, - [ + map:{ + bar:[ b ] - ] + } } \ No newline at end of file diff --git a/platform/object-serializer/testSrc/ListTest.kt b/platform/object-serializer/testSrc/ListTest.kt index c548a88e8e26..1fc7f4ec2a90 100644 --- a/platform/object-serializer/testSrc/ListTest.kt +++ b/platform/object-serializer/testSrc/ListTest.kt @@ -109,7 +109,7 @@ class ListTest { assertThat(file.file.readChars().trim()).isEqualToIgnoringNewLines(""" { version:42, - formatVersion:1, + formatVersion:2, data:[ foo, bar diff --git a/platform/object-serializer/testSrc/MapTest.kt b/platform/object-serializer/testSrc/MapTest.kt index 4a6b9e15f4c3..14b8f29b24a4 100644 --- a/platform/object-serializer/testSrc/MapTest.kt +++ b/platform/object-serializer/testSrc/MapTest.kt @@ -68,6 +68,17 @@ class MapTest { assertThat(deserializedBean.map.values.first()).isInstanceOf(Set::class.java) } + @Test + fun `empty map`() { + class TestBean { + @JvmField + val map: MutableMap> = THashMap() + } + + val bean = TestBean() + test(bean, defaultTestWriteConfiguration.copy(filter = SkipNullAndEmptySerializationFilter)) + } + @Test fun `bean map`() { val bean = TestMapBean() diff --git a/platform/object-serializer/testSrc/NonDefaultConstructorTest.kt b/platform/object-serializer/testSrc/NonDefaultConstructorTest.kt index 46320ae96f3e..addfe5358385 100644 --- a/platform/object-serializer/testSrc/NonDefaultConstructorTest.kt +++ b/platform/object-serializer/testSrc/NonDefaultConstructorTest.kt @@ -51,7 +51,7 @@ class NonDefaultConstructorTest { file.file.write(""" { version:42, - formatVersion:1, + formatVersion:2, data:{ } } diff --git a/platform/object-serializer/testSrc/ObjectSerializerTest.kt b/platform/object-serializer/testSrc/ObjectSerializerTest.kt index aae4d4f8c8ca..5dc0faa52fcf 100644 --- a/platform/object-serializer/testSrc/ObjectSerializerTest.kt +++ b/platform/object-serializer/testSrc/ObjectSerializerTest.kt @@ -188,7 +188,7 @@ private class Rectangle : Shape { internal enum class TestEnum { - RED, GREEN, BLUE + RED, BLUE } private class TestEnumBean { diff --git a/platform/platform-api/src/com/intellij/ide/ScreenReaderSupportHandler.java b/platform/platform-api/src/com/intellij/ide/ScreenReaderSupportHandler.java deleted file mode 100644 index 223dbac22c62..000000000000 --- a/platform/platform-api/src/com/intellij/ide/ScreenReaderSupportHandler.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.ide; - -import com.intellij.openapi.Disposable; -import com.intellij.util.ui.accessibility.ScreenReader; - -/** - * Keep {@link ScreenReader#isActive} in sync with {@link GeneralSettings#isSupportScreenReaders} - */ -public final class ScreenReaderSupportHandler implements Disposable { - public ScreenReaderSupportHandler() { - GeneralSettings generalSettings = GeneralSettings.getInstance(); - generalSettings.addPropertyChangeListener(GeneralSettings.PROP_SUPPORT_SCREEN_READERS, this, e -> ScreenReader.setActive((Boolean)e.getNewValue())); - ScreenReader.setActive(generalSettings.isSupportScreenReaders()); - } - - @Override - public void dispose() { - } -} diff --git a/platform/platform-api/src/com/intellij/ide/actions/ActionsCollector.java b/platform/platform-api/src/com/intellij/ide/actions/ActionsCollector.java index f11201193e5f..42d702454867 100644 --- a/platform/platform-api/src/com/intellij/ide/actions/ActionsCollector.java +++ b/platform/platform-api/src/com/intellij/ide/actions/ActionsCollector.java @@ -6,14 +6,10 @@ import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.project.Project; -import com.intellij.util.xmlb.annotations.MapAnnotation; -import com.intellij.util.xmlb.annotations.Tag; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.awt.event.InputEvent; -import java.util.HashMap; -import java.util.Map; /** * @author Konstantin Bulenkov @@ -54,16 +50,4 @@ public abstract class ActionsCollector { public abstract void record(@Nullable Project project, @Nullable AnAction action, @Nullable AnActionEvent event, @Nullable Language lang); public abstract void onActionConfiguredByActionId(@NotNull AnAction action, @NotNull String actionId); - - public abstract State getState(); - - public final static class State { - @Tag("counts") - @MapAnnotation(surroundWithTag = false, keyAttributeName = "action", valueAttributeName = "count") - public Map myValues = new HashMap<>(); - - @Tag("contextMenuCounts") - @MapAnnotation(surroundWithTag = false, keyAttributeName = "action", valueAttributeName = "count") - public Map myContextMenuValues = new HashMap<>(); - } } diff --git a/platform/platform-api/src/com/intellij/internal/statistic/eventLog/FeatureUsageUiEvents.kt b/platform/platform-api/src/com/intellij/internal/statistic/eventLog/FeatureUsageUiEvents.kt index 6f789872c497..7afeaa95b00c 100644 --- a/platform/platform-api/src/com/intellij/internal/statistic/eventLog/FeatureUsageUiEvents.kt +++ b/platform/platform-api/src/com/intellij/internal/statistic/eventLog/FeatureUsageUiEvents.kt @@ -3,6 +3,7 @@ package com.intellij.internal.statistic.eventLog import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.components.ServiceManager +import com.intellij.openapi.options.Configurable fun getUiEventLogger(): FeatureUsageUiEvents { if (ApplicationManager.getApplication() != null) { @@ -13,11 +14,11 @@ fun getUiEventLogger(): FeatureUsageUiEvents { } interface FeatureUsageUiEvents { - fun logSelectConfigurable(name: String, context: Class<*>) + fun logSelectConfigurable(configurable: Configurable) - fun logApplyConfigurable(name: String, context: Class<*>) + fun logApplyConfigurable(configurable: Configurable) - fun logResetConfigurable(name: String, context: Class<*>) + fun logResetConfigurable(configurable: Configurable) fun logShowDialog(name: String, context: Class<*>) @@ -25,13 +26,13 @@ interface FeatureUsageUiEvents { } object EmptyFeatureUsageUiEvents : FeatureUsageUiEvents { - override fun logSelectConfigurable(name: String, context: Class<*>) { + override fun logSelectConfigurable(configurable: Configurable) { } - override fun logApplyConfigurable(name: String, context: Class<*>) { + override fun logApplyConfigurable(configurable: Configurable) { } - override fun logResetConfigurable(name: String, context: Class<*>) { + override fun logResetConfigurable(configurable: Configurable) { } override fun logShowDialog(name: String, context: Class<*>) { diff --git a/platform/platform-api/src/com/intellij/openapi/wm/StatusBar.java b/platform/platform-api/src/com/intellij/openapi/wm/StatusBar.java index 34acc9b21b9f..29b33b0091c4 100644 --- a/platform/platform-api/src/com/intellij/openapi/wm/StatusBar.java +++ b/platform/platform-api/src/com/intellij/openapi/wm/StatusBar.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2019 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-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. package com.intellij.openapi.wm; import com.intellij.openapi.Disposable; @@ -110,8 +96,12 @@ public interface StatusBar extends StatusBarInfo, Disposable { StatusBar findChild(Component c); + @Nullable IdeFrame getFrame(); + @Nullable + Project getProject(); + void install(IdeFrame frame); class Anchors { diff --git a/platform/platform-api/src/com/intellij/openapi/wm/StatusBarCustomComponentFactory.java b/platform/platform-api/src/com/intellij/openapi/wm/StatusBarCustomComponentFactory.java deleted file mode 100644 index 511fed53674a..000000000000 --- a/platform/platform-api/src/com/intellij/openapi/wm/StatusBarCustomComponentFactory.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2000-2009 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.openapi.wm; - -import com.intellij.openapi.extensions.ExtensionPointName; -import org.jetbrains.annotations.NotNull; - -import javax.swing.*; -import java.util.EventListener; - -/** - * @deprecated use StatusBarWidget instead - */ -@Deprecated -public abstract class StatusBarCustomComponentFactory implements EventListener { - public static final ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.statusBarComponent"); - - public abstract T createComponent(@NotNull final StatusBar statusBar); - - public void disposeComponent(@NotNull StatusBar statusBar, @NotNull final T c) { - } -} diff --git a/platform/platform-impl/src/com/intellij/diagnostic/startUpPerformanceReporter/StartUpPerformanceReporter.kt b/platform/platform-impl/src/com/intellij/diagnostic/startUpPerformanceReporter/StartUpPerformanceReporter.kt index f2f68ab71e43..112bdf992c5d 100644 --- a/platform/platform-impl/src/com/intellij/diagnostic/startUpPerformanceReporter/StartUpPerformanceReporter.kt +++ b/platform/platform-impl/src/com/intellij/diagnostic/startUpPerformanceReporter/StartUpPerformanceReporter.kt @@ -225,12 +225,11 @@ private fun writeParallelActivities(activities: Map, } writer.array(fieldName) { + var skippedDuration = 0L for (item in activities) { val computedOwnDuration = ownDurations.get(item) val duration = if (computedOwnDuration == -1L) item.end - item.start else computedOwnDuration if (duration <= measureThreshold) { - continue - } - - if (measureThreshold == 0L && TimeUnit.NANOSECONDS.toMillis(duration) == 0L) { + skippedDuration += duration continue } @@ -273,6 +270,15 @@ private fun writeActivities(activities: List, writeItemTimeInfo(item, duration, offset, writer) } } + + if (skippedDuration > 0) { + writer.obj { + writer.writeStringField("name", "Other") + writer.writeNumberField("duration", TimeUnit.NANOSECONDS.toMillis(skippedDuration)) + writer.writeNumberField("start", TimeUnit.NANOSECONDS.toMillis(activities.last().start - offset)) + writer.writeNumberField("end", TimeUnit.NANOSECONDS.toMillis(activities.last().end - offset)) + } + } } } diff --git a/platform/platform-impl/src/com/intellij/diagnostic/startUpPerformanceReporter/serviceReporter.kt b/platform/platform-impl/src/com/intellij/diagnostic/startUpPerformanceReporter/serviceReporter.kt index 85bd0c0fd7f9..5402baa19599 100644 --- a/platform/platform-impl/src/com/intellij/diagnostic/startUpPerformanceReporter/serviceReporter.kt +++ b/platform/platform-impl/src/com/intellij/diagnostic/startUpPerformanceReporter/serviceReporter.kt @@ -5,6 +5,7 @@ import com.fasterxml.jackson.core.JsonGenerator import com.intellij.diagnostic.ActivityImpl import com.intellij.ide.plugins.IdeaPluginDescriptorImpl import com.intellij.ide.plugins.PluginManagerCore +import com.intellij.ide.plugins.cl.PluginClassLoader import com.intellij.util.containers.ObjectLongHashMap import com.intellij.util.io.jackson.obj import java.util.concurrent.TimeUnit @@ -75,7 +76,6 @@ internal fun writeServiceStats(writer: JsonGenerator) { writer.obj("stats") { writer.writeNumberField("plugin", plugins.size) - for (statItem in listOf(component, service)) { writer.obj(statItem.name) { writer.writeNumberField("app", statItem.app) @@ -83,5 +83,15 @@ internal fun writeServiceStats(writer: JsonGenerator) { writer.writeNumberField("module", statItem.module) } } + + writer.obj("loadedClasses") { + for (plugin in plugins) { + val classLoader = (plugin as IdeaPluginDescriptorImpl).pluginClassLoader as? PluginClassLoader ?: continue + val classCount = classLoader.loadedClassCount + if (classCount > 0) { + writer.writeNumberField(plugin.pluginId.idString, classCount) + } + } + } } } \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/ide/AppLifecycleListener.java b/platform/platform-impl/src/com/intellij/ide/AppLifecycleListener.java index c381e0124ba7..4aec35b8a9e3 100644 --- a/platform/platform-impl/src/com/intellij/ide/AppLifecycleListener.java +++ b/platform/platform-impl/src/com/intellij/ide/AppLifecycleListener.java @@ -3,7 +3,6 @@ package com.intellij.ide; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Ref; -import com.intellij.openapi.wm.IdeFrame; import com.intellij.util.messages.Topic; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -34,13 +33,6 @@ public interface AppLifecycleListener { default void appStarting(@Nullable Project projectFromCommandLine) { } - /** - * Called after an application frame is shown. - */ - default void appStarting(@Nullable Project projectFromCommandLine, IdeFrame frame) { - appStarting(projectFromCommandLine); - } - /** * Called when a project frame is closed. */ diff --git a/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.java b/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.java index 7b87f9d4ba5f..cbba6bf0359c 100644 --- a/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.java +++ b/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.java @@ -677,10 +677,13 @@ public final class IdeEventQueue extends EventQueue { return; } - if (e instanceof InputEvent) + if (e instanceof InputEvent && SystemInfoRt.isMac) { TouchBarsManager.onInputEvent((InputEvent)e); + } - if (dispatchByCustomDispatchers(e)) return; + if (dispatchByCustomDispatchers(e)) { + return; + } if (e instanceof InputMethodEvent) { if (SystemInfoRt.isMac && myKeyEventDispatcher.isWaitingForSecondKeyStroke()) { diff --git a/platform/platform-impl/src/com/intellij/ide/RecentDirectoryProjectsManager.java b/platform/platform-impl/src/com/intellij/ide/RecentDirectoryProjectsManager.java index b9cfbf77e389..0d9b5d93a23a 100644 --- a/platform/platform-impl/src/com/intellij/ide/RecentDirectoryProjectsManager.java +++ b/platform/platform-impl/src/com/intellij/ide/RecentDirectoryProjectsManager.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-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. package com.intellij.ide; import com.intellij.openapi.components.RoamingType; @@ -7,7 +7,6 @@ import com.intellij.openapi.components.Storage; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.platform.ProjectBaseDirectory; -import com.intellij.util.messages.MessageBus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.SystemIndependent; @@ -15,10 +14,6 @@ import org.jetbrains.annotations.SystemIndependent; // todo the only difference - usage of ProjectBaseDirectory Is it really make sense? @State(name = "RecentDirectoryProjectsManager", storages = @Storage(value = "recentProjectDirectories.xml", roamingType = RoamingType.DISABLED)) public class RecentDirectoryProjectsManager extends RecentProjectsManagerBase { - public RecentDirectoryProjectsManager(@NotNull MessageBus messageBus) { - super(messageBus); - } - @Override @Nullable @SystemIndependent diff --git a/platform/platform-impl/src/com/intellij/ide/RecentProjectsManagerBase.java b/platform/platform-impl/src/com/intellij/ide/RecentProjectsManagerBase.java index 7a246b0b1efc..d3faeb5eba03 100644 --- a/platform/platform-impl/src/com/intellij/ide/RecentProjectsManagerBase.java +++ b/platform/platform-impl/src/com/intellij/ide/RecentProjectsManagerBase.java @@ -2,7 +2,10 @@ package com.intellij.ide; import com.intellij.configurationStore.StorageUtilKt; +import com.intellij.diagnostic.Activity; +import com.intellij.diagnostic.StartUpMeasurer; import com.intellij.ide.impl.ProjectUtil; +import com.intellij.idea.SplashManager; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.application.ApplicationInfo; import com.intellij.openapi.application.ApplicationManager; @@ -24,14 +27,14 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.wm.IdeFrame; +import com.intellij.openapi.wm.WindowManager; import com.intellij.openapi.wm.impl.SystemDock; +import com.intellij.openapi.wm.impl.WindowManagerImpl; import com.intellij.openapi.wm.impl.welcomeScreen.RecentProjectPanel; import com.intellij.platform.PlatformProjectOpenProcessor; import com.intellij.project.ProjectKt; import com.intellij.util.*; import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.messages.MessageBus; -import com.intellij.util.messages.MessageBusConnection; import gnu.trove.THashMap; import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; @@ -170,12 +173,6 @@ public class RecentProjectsManagerBase extends RecentProjectsManager implements private boolean myBatchOpening; - protected RecentProjectsManagerBase(@NotNull MessageBus messageBus) { - MessageBusConnection connection = messageBus.connect(); - connection.subscribe(AppLifecycleListener.TOPIC, new MyAppLifecycleListener()); - connection.subscribe(ProjectManager.TOPIC, new MyProjectListener()); - } - @Override public State getState() { synchronized (myStateLock) { @@ -504,12 +501,11 @@ public class RecentProjectsManagerBase extends RecentProjectsManager implements @Nullable public Project doOpenProject(@NotNull @SystemIndependent String projectPath, - Project projectToClose, + @Nullable Project projectToClose, boolean forceOpenInNewFrame, @Nullable IdeFrame frame) { - VirtualFile dotIdea = LocalFileSystem.getInstance().refreshAndFindFileByIoFile( - new File(projectPath, Project.DIRECTORY_STORE_FOLDER)); - + VirtualFile dotIdea = LocalFileSystem.getInstance() + .refreshAndFindFileByPath(FileUtilRt.toSystemIndependentName(projectPath) + "/" + Project.DIRECTORY_STORE_FOLDER); if (dotIdea != null) { EnumSet options = EnumSet.of(PlatformProjectOpenProcessor.Option.REOPEN); if (forceOpenInNewFrame) options.add(PlatformProjectOpenProcessor.Option.FORCE_NEW_FRAME); @@ -524,18 +520,20 @@ public class RecentProjectsManagerBase extends RecentProjectsManager implements } } - private class MyProjectListener implements ProjectManagerListener { + static final class MyProjectListener implements ProjectManagerListener { + private final RecentProjectsManagerBase manager = getInstanceEx(); + @Override public void projectOpened(@NotNull final Project project) { - String path = getProjectPath(project); + String path = manager.getProjectPath(project); if (path != null) { - markPathRecent(path, project); + manager.markPathRecent(path, project); } - updateLastProjectPath(); + manager.updateLastProjectPath(); updateSystemDockMenu(); } - private void updateSystemDockMenu() { + private static void updateSystemDockMenu() { if (!ApplicationManager.getApplication().isHeadlessEnvironment()) { SystemDock.updateMenu(); } @@ -543,14 +541,14 @@ public class RecentProjectsManagerBase extends RecentProjectsManager implements @Override public void projectClosing(@NotNull Project project) { - String path = getProjectPath(project); + String path = manager.getProjectPath(project); if (path == null) { return; } - synchronized (myStateLock) { - myState.names.put(path, getProjectDisplayName(project)); - myNameCache.put(path, project.getName()); + synchronized (manager.myStateLock) { + manager.myState.names.put(path, manager.getProjectDisplayName(project)); + manager.myNameCache.put(path, project.getName()); } } @@ -559,9 +557,9 @@ public class RecentProjectsManagerBase extends RecentProjectsManager implements Project[] openProjects = ProjectManager.getInstance().getOpenProjects(); if (openProjects.length > 0) { Project openProject = openProjects[openProjects.length - 1]; - String path = getProjectPath(openProject); + String path = manager.getProjectPath(openProject); if (path != null) { - markPathRecent(path, openProject); + manager.markPathRecent(path, openProject); } } updateSystemDockMenu(); @@ -611,12 +609,11 @@ public class RecentProjectsManagerBase extends RecentProjectsManager implements } protected boolean willReopenProjectOnStart() { - return GeneralSettings.getInstance().isReopenLastProject() && getLastProjectPath() != null; + return getLastProjectPath() != null && GeneralSettings.getInstance().isReopenLastProject(); } - protected void doReopenLastProject(IdeFrame frame) { - GeneralSettings generalSettings = GeneralSettings.getInstance(); - if (!generalSettings.isReopenLastProject()) { + protected void doReopenLastProject(@Nullable IdeFrame frame) { + if (!GeneralSettings.getInstance().isReopenLastProject()) { return; } @@ -630,6 +627,12 @@ public class RecentProjectsManagerBase extends RecentProjectsManager implements } } + if (!openPaths.isEmpty() && frame == null) { + Activity activity = StartUpMeasurer.start("showFrame"); + frame = ((WindowManagerImpl)WindowManager.getInstance()).showFrame(SplashManager.getHideTask()); + activity.end(); + } + try { myBatchOpening = true; boolean usedFrame = false; @@ -681,33 +684,35 @@ public class RecentProjectsManagerBase extends RecentProjectsManager implements return myModCounter.get(); } - private final class MyAppLifecycleListener implements AppLifecycleListener { + static final class MyAppLifecycleListener implements AppLifecycleListener { + private final RecentProjectsManagerBase manager = getInstanceEx(); + @Override public void appFrameCreated(@NotNull List commandLineArgs, @NotNull final Ref willOpenProject) { - if (willReopenProjectOnStart()) { + if (manager.willReopenProjectOnStart()) { willOpenProject.set(Boolean.TRUE); } } @Override - public void appStarting(@Nullable Project projectFromCommandLine, IdeFrame frame) { + public void appStarting(@Nullable Project projectFromCommandLine) { if (projectFromCommandLine != null || JetBrainsProtocolHandler.appStartedWithCommand()) { return; } - doReopenLastProject(frame); + manager.doReopenLastProject(null); } @Override public void projectOpenFailed() { - updateLastProjectPath(); + manager.updateLastProjectPath(); } @Override public void projectFrameClosed() { // ProjectManagerListener.projectClosed cannot be used to call updateLastProjectPath, // because called even if project closed on app exit - updateLastProjectPath(); + manager.updateLastProjectPath(); } } diff --git a/platform/platform-impl/src/com/intellij/ide/ui/customization/CustomActionsSchema.java b/platform/platform-impl/src/com/intellij/ide/ui/customization/CustomActionsSchema.java index d1daa7698638..949ffea426a1 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/customization/CustomActionsSchema.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/customization/CustomActionsSchema.java @@ -24,12 +24,12 @@ import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.wm.ex.WindowManagerEx; import com.intellij.openapi.wm.impl.IdeFrameImpl; -import com.intellij.ui.mac.touchbar.TouchBarsManager; import com.intellij.util.ImageLoader; import com.intellij.util.ui.JBImageIcon; import org.jdom.Element; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; @@ -40,7 +40,7 @@ import java.util.List; import java.util.*; @State(name = "com.intellij.ide.ui.customization.CustomActionsSchema", storages = @Storage("customization.xml")) -public class CustomActionsSchema implements PersistentStateComponent { +public final class CustomActionsSchema implements PersistentStateComponent { private static final Logger LOG = Logger.getInstance(CustomActionsSchema.class); @NonNls private static final String ACTIONS_SCHEMA = "custom_actions_schema"; @@ -72,10 +72,13 @@ public class CustomActionsSchema implements PersistentStateComponent { myIdToName.put(IdeActions.GROUP_J2EE_VIEW_POPUP, ActionsTreeUtil.J2EE_POPUP); myIdToName.put(IdeActions.GROUP_NAVBAR_POPUP, "Navigation Bar"); myIdToName.put("NavBarToolBar", "Navigation Bar Toolbar"); - if (TouchBarsManager.isTouchBarAvailable()) - myIdToName.put(IdeActions.GROUP_TOUCHBAR, "Touch Bar"); - ArrayList> extList = new ArrayList<>(); + // todo is it safe to not check so early? + //if (TouchBarsManager.isTouchBarAvailable()) { + // myIdToName.put(IdeActions.GROUP_TOUCHBAR, "Touch Bar"); + //} + + List> extList = new ArrayList<>(); CustomizableActionGroupProvider.CustomizableActionGroupRegistrar registrar = (groupId, groupTitle) -> extList.add(Couple.of(groupId, groupTitle)); for (CustomizableActionGroupProvider provider : CustomizableActionGroupProvider.EP_NAME.getExtensions()) { @@ -87,6 +90,15 @@ public class CustomActionsSchema implements PersistentStateComponent { } } + public void touchBarAvailable(boolean value) { + if (value) { + myIdToName.put(IdeActions.GROUP_TOUCHBAR, "Touch Bar"); + } + else { + myIdToName.remove(IdeActions.GROUP_TOUCHBAR); + } + } + public static CustomActionsSchema getInstance() { return ServiceManager.getService(CustomActionsSchema.class); } @@ -159,9 +171,9 @@ public class CustomActionsSchema implements PersistentStateComponent { String activeName = element.getAttributeValue(ACTIVE); if (activeName != null) { for (Element toolbarElement : element.getChildren(ACTIONS_SCHEMA)) { - for (Object o : toolbarElement.getChildren("option")) { - if (Comparing.strEqual(((Element)o).getAttributeValue("name"), "myName") && - Comparing.strEqual(((Element)o).getAttributeValue("value"), activeName)) { + for (Element o : toolbarElement.getChildren("option")) { + if (Comparing.strEqual(o.getAttributeValue("name"), "myName") && + Comparing.strEqual(o.getAttributeValue("value"), activeName)) { schElement = toolbarElement; break; } @@ -243,12 +255,17 @@ public class CustomActionsSchema implements PersistentStateComponent { return element; } + @Nullable public AnAction getCorrectedAction(String id) { if (!myIdToName.containsKey(id)) { return ActionManager.getInstance().getAction(id); } + ActionGroup existing = myIdToActionGroup.get(id); - if (existing != null) return existing; + if (existing != null) { + return existing; + } + ActionGroup actionGroup = (ActionGroup)ActionManager.getInstance().getAction(id); if (actionGroup != null) { // if a plugin is disabled String name = myIdToName.get(id); @@ -269,7 +286,6 @@ public class CustomActionsSchema implements PersistentStateComponent { } } - public boolean isCorrectActionGroup(ActionGroup group, String defaultGroupName) { if (myActions.isEmpty()) { return false; @@ -294,6 +310,7 @@ public class CustomActionsSchema implements PersistentStateComponent { return true; } + @NotNull public List getChildActions(ActionUrl url) { ArrayList result = new ArrayList<>(); ArrayList groupPath = url.getGroupPath(); diff --git a/platform/platform-impl/src/com/intellij/ide/ui/customization/CustomizableActionsPanel.java b/platform/platform-impl/src/com/intellij/ide/ui/customization/CustomizableActionsPanel.java index 72a879a07854..ec63e0795b08 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/customization/CustomizableActionsPanel.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/customization/CustomizableActionsPanel.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-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. package com.intellij.ide.ui.customization; import com.intellij.icons.AllIcons; @@ -19,6 +19,7 @@ import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.ui.TextFieldWithBrowseButton; import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.SystemInfoRt; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; @@ -184,7 +185,9 @@ public class CustomizableActionsPanel { restorePathsAfterTreeOptimization(treePaths); CustomActionsSchema.getInstance().copyFrom(mySelectedSchema); CustomActionsSchema.setCustomizationSchemaForCurrentProjects(); - TouchBarsManager.reloadAll(); + if (SystemInfoRt.isMac) { + TouchBarsManager.reloadAll(); + } } private void restorePathsAfterTreeOptimization(final List treePaths) { diff --git a/platform/platform-impl/src/com/intellij/idea/IdeaApplication.java b/platform/platform-impl/src/com/intellij/idea/IdeaApplication.java index b62991a2f05a..f4b0156c2fcc 100644 --- a/platform/platform-impl/src/com/intellij/idea/IdeaApplication.java +++ b/platform/platform-impl/src/com/intellij/idea/IdeaApplication.java @@ -9,9 +9,11 @@ import com.intellij.ide.plugins.IdeaPluginDescriptor; import com.intellij.ide.plugins.MainRunner; import com.intellij.ide.plugins.PluginManager; import com.intellij.ide.plugins.PluginManagerCore; +import com.intellij.ide.ui.customization.CustomActionsSchema; import com.intellij.openapi.application.*; import com.intellij.openapi.application.ex.ApplicationManagerEx; import com.intellij.openapi.application.impl.ApplicationImpl; +import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogEarthquakeShaker; @@ -31,8 +33,10 @@ import com.intellij.ui.AppIcon; import com.intellij.ui.AppUIUtil; import com.intellij.ui.CustomProtocolHandler; import com.intellij.ui.mac.MacOSApplicationProvider; +import com.intellij.ui.mac.touchbar.TouchBarsManager; import com.intellij.util.ArrayUtilRt; import com.intellij.util.concurrency.AppExecutorUtil; +import com.intellij.util.ui.accessibility.ScreenReader; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -59,74 +63,7 @@ public final class IdeaApplication { public static void initApplication(@NotNull String[] rawArgs) { Activity initAppActivity = MainRunner.startupStart.endAndStart(Phases.INIT_APP); CompletableFuture> pluginDescriptorsFuture = new CompletableFuture<>(); - EventQueue.invokeLater(() -> { - String[] args = processProgramArguments(rawArgs); - - ApplicationStarter starter = createAppStarter(args, pluginDescriptorsFuture); - - Activity createAppActivity = StartUpMeasurer.start("create app"); - boolean headless = Main.isHeadless(); - ApplicationImpl app = new ApplicationImpl(Boolean.getBoolean(PluginManagerCore.IDEA_IS_INTERNAL_PROPERTY), false, headless, - Main.isCommandLine(), ApplicationManagerEx.IDEA_APPLICATION); - createAppActivity.end(); - - if (!headless) { - // todo investigate why in test mode dummy icon manager is not suitable - IconLoader.activate(); - IconLoader.setStrictGlobally(app.isInternal()); - } - - starter.premain(args); - - CompletableFuture registerComponentsFuture = pluginDescriptorsFuture - .thenCompose(pluginDescriptors -> { - CompletableFuture future = CompletableFuture.runAsync(() -> { - Activity activity = ParallelActivity.PREPARE_APP_INIT.start("add registry keys"); - RegistryKeyBean.addKeysFromPlugins(); - activity.end(); - - Activity busActivity = ParallelActivity.PREPARE_APP_INIT.start("add message bus listeners"); - ApplicationImpl.registerMessageBusListeners(app, pluginDescriptors, false); - busActivity.end(); - }, AppExecutorUtil.getAppExecutorService()); - - Activity activity = ParallelActivity.PREPARE_APP_INIT.start("app component registration"); - ((ApplicationImpl)ApplicationManager.getApplication()).registerComponents(pluginDescriptors); - activity.end(); - - return future; - }); - - if (!headless) { - SplashManager.showLicenseeInfoOnSplash(LOG); - } - - // this invokeLater() call is needed to place the app starting code on a freshly minted IdeEventQueue instance - Activity placeOnEventQueueActivity = initAppActivity.startChild(Phases.PLACE_ON_EVENT_QUEUE); - EventQueue.invokeLater(() -> { - placeOnEventQueueActivity.end(); - StartupUtil.installExceptionHandler(); - initAppActivity.end(); - try { - Activity activity = StartUpMeasurer.start(Phases.WAIT_PLUGIN_INIT); - registerComponentsFuture.get(); - activity.end(); - } - catch (InterruptedException | ExecutionException e) { - throw new CompletionException(e); - } - - app.load(null, SplashManager.getProgressIndicator()); - if (!headless) { - addActivateAndWindowsCliListeners(app); - } - ((TransactionGuardImpl)TransactionGuard.getInstance()).performUserActivity(() -> starter.main(args)); - - if (PluginManagerCore.isRunningFromSources()) { - AppExecutorUtil.getAppExecutorService().execute(() -> AppUIUtil.updateWindowIcon(JOptionPane.getRootFrame())); - } - }); - }); + EventQueue.invokeLater(() -> executeInitAppInEdt(rawArgs, initAppActivity, pluginDescriptorsFuture)); List plugins; try { @@ -139,6 +76,100 @@ public final class IdeaApplication { pluginDescriptorsFuture.complete(plugins); } + private static void executeInitAppInEdt(@NotNull String[] rawArgs, @NotNull Activity initAppActivity, + @NotNull CompletableFuture> pluginDescriptorsFuture) { + String[] args = processProgramArguments(rawArgs); + + ApplicationStarter starter = createAppStarter(args, pluginDescriptorsFuture); + + Activity createAppActivity = initAppActivity.startChild("create app"); + boolean headless = Main.isHeadless(); + ApplicationImpl app = new ApplicationImpl(Boolean.getBoolean(PluginManagerCore.IDEA_IS_INTERNAL_PROPERTY), false, headless, + Main.isCommandLine(), ApplicationManagerEx.IDEA_APPLICATION); + createAppActivity.end(); + + if (!headless) { + // todo investigate why in test mode dummy icon manager is not suitable + IconLoader.activate(); + IconLoader.setStrictGlobally(app.isInternal()); + + if (SystemInfoRt.isMac) { + Activity activity = initAppActivity.startChild("mac app init"); + MacOSApplicationProvider.initApplication(); + activity.end(); + } + } + + starter.premain(args); + + List> futures = new ArrayList<>(); + futures.add(registerRegistryAndMessageBusAndComponent(pluginDescriptorsFuture, app)); + + if (!headless) { + if (SystemInfoRt.isMac) { + // ensure that TouchBarsManager is loaded before WelcomeFrame/project + futures.add(AppExecutorUtil.getAppExecutorService().submit(() -> { + Activity activity = ParallelActivity.PREPARE_APP_INIT.start("mac touchbar"); + //noinspection ResultOfMethodCallIgnored + TouchBarsManager.isTouchBarAvailable(); + activity.end(); + })); + } + SplashManager.showLicenseeInfoOnSplash(LOG); + } + + // this invokeLater() call is needed to place the app starting code on a freshly minted IdeEventQueue instance + Activity placeOnEventQueueActivity = initAppActivity.startChild(Phases.PLACE_ON_EVENT_QUEUE); + EventQueue.invokeLater(() -> { + placeOnEventQueueActivity.end(); + StartupUtil.installExceptionHandler(); + initAppActivity.end(); + try { + Activity activity = StartUpMeasurer.start(Phases.WAIT_PLUGIN_INIT); + for (Future future : futures) { + future.get(); + } + activity.end(); + } + catch (InterruptedException | ExecutionException e) { + throw new CompletionException(e); + } + + app.load(null, SplashManager.getProgressIndicator()); + if (!headless) { + addActivateAndWindowsCliListeners(app); + } + ((TransactionGuardImpl)TransactionGuard.getInstance()).performUserActivity(() -> starter.main(args)); + + if (PluginManagerCore.isRunningFromSources()) { + AppExecutorUtil.getAppExecutorService().execute(() -> AppUIUtil.updateWindowIcon(JOptionPane.getRootFrame())); + } + }); + } + + @NotNull + private static CompletableFuture registerRegistryAndMessageBusAndComponent(@NotNull CompletableFuture> pluginDescriptorsFuture, + @NotNull ApplicationImpl app) { + return pluginDescriptorsFuture + .thenCompose(pluginDescriptors -> { + CompletableFuture future = CompletableFuture.runAsync(() -> { + Activity activity = ParallelActivity.PREPARE_APP_INIT.start("add registry keys"); + RegistryKeyBean.addKeysFromPlugins(); + activity.end(); + + Activity busActivity = ParallelActivity.PREPARE_APP_INIT.start("add message bus listeners"); + ApplicationImpl.registerMessageBusListeners(app, pluginDescriptors, false); + busActivity.end(); + }, AppExecutorUtil.getAppExecutorService()); + + Activity activity = ParallelActivity.PREPARE_APP_INIT.start("app component registration"); + ((ApplicationImpl)ApplicationManager.getApplication()).registerComponents(pluginDescriptors); + activity.end(); + + return future; + }); + } + private static void addActivateAndWindowsCliListeners(@NotNull ApplicationImpl app) { StartupUtil.addExternalInstanceListener(args -> { AtomicReference> ref = new AtomicReference<>(); @@ -187,19 +218,30 @@ public final class IdeaApplication { } @NotNull - private static ApplicationStarter createAppStarter(@NotNull String[] args, @Nullable Future pluginsLoaded) { + private static ApplicationStarter createAppStarter(@NotNull String[] args, @NotNull Future pluginsLoaded) { LOG.assertTrue(!ApplicationManagerEx.isAppLoaded()); - LoadingPhase.setCurrentPhase(LoadingPhase.SPLASH); - StartupUtil.patchSystem(LOG); - ApplicationStarter starter = getStarter(args, pluginsLoaded); - - if (Main.isHeadless() && !starter.isHeadless()) { - Main.showMessage("Startup Error", "Application cannot start in headless mode", true); - System.exit(Main.NO_GRAPHICS); + if (args.length <= 0) { + return new IdeStarter(); } - return starter; + + try { + pluginsLoaded.get(); + } + catch (InterruptedException | ExecutionException e) { + throw new CompletionException(e); + } + + ApplicationStarter starter = findStarter(args[0]); + if (starter != null) { + if (Main.isHeadless() && !starter.isHeadless()) { + Main.showMessage("Startup Error", "Application cannot start in headless mode", true); + System.exit(Main.NO_GRAPHICS); + } + return starter; + } + return new IdeStarter(); } /** @@ -230,30 +272,6 @@ public final class IdeaApplication { return ArrayUtilRt.toStringArray(arguments); } - @NotNull - private static ApplicationStarter getStarter(@NotNull String[] args, @Nullable Future pluginsLoaded) { - if (args.length > 0) { - if (pluginsLoaded == null) { - PluginManagerCore.getPlugins(); - } - else { - try { - pluginsLoaded.get(); - } - catch (InterruptedException | ExecutionException e) { - throw new CompletionException(e); - } - } - - ApplicationStarter starter = findStarter(args[0]); - if (starter != null) { - return starter; - } - } - - return new IdeStarter(); - } - @Nullable public static ApplicationStarter findStarter(@Nullable String key) { for (ApplicationStarter starter : ApplicationStarter.EP_NAME.getIterable(null)) { @@ -293,7 +311,7 @@ public final class IdeaApplication { String filename = args[0]; File file = new File(currentDirectory, filename); - if(file.exists()) { + if (file.exists()) { VirtualFile virtualFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file); if (virtualFile != null) { int line = -1; @@ -324,30 +342,26 @@ public final class IdeaApplication { @Override public void main(String[] args) { - Activity activity = StartUpMeasurer.start(Phases.FRAME_INITIALIZATION); - if (SystemInfoRt.isMac) { - MacOSApplicationProvider.initApplication(); - } + Activity frameInitActivity = StartUpMeasurer.start(Phases.FRAME_INITIALIZATION); - SystemDock.updateMenu(); - - RecentProjectsManager.getInstance(); // ensures that RecentProjectsManager app listener is added GcPauseWatcher.Companion.getInstance(); // Event queue should not be changed during initialization of application components. // It also cannot be changed before initialization of application components because IdeEventQueue uses other // application components. So it is proper to perform replacement only here. + Activity setWindowManagerActivity = frameInitActivity.startChild("set window manager"); Application app = ApplicationManager.getApplication(); WindowManagerImpl windowManager = (WindowManagerImpl)WindowManager.getInstance(); IdeEventQueue.getInstance().setWindowManager(windowManager); + setWindowManagerActivity.end(); List commandLineArgs = args == null || args.length == 0 ? Collections.emptyList() : Arrays.asList(args); Ref willOpenProject = new Ref<>(Boolean.FALSE); + Activity appFrameCreatedActivity = frameInitActivity.startChild("call appFrameCreated"); AppLifecycleListener lifecyclePublisher = app.getMessageBus().syncPublisher(AppLifecycleListener.TOPIC); lifecyclePublisher.appFrameCreated(commandLineArgs, willOpenProject); - - PluginManagerCore.dumpPluginClassStatistics(); + appFrameCreatedActivity.end(); // Temporary check until the jre implementation has been checked and bundled if (Registry.is("ide.popup.enablePopupType")) { @@ -356,29 +370,50 @@ public final class IdeaApplication { LoadingPhase.setCurrentPhase(LoadingPhase.FRAME_SHOWN); - Runnable beforeSetVisible = SplashManager.getHideTask(); - - IdeFrame frame = null; - if (JetBrainsProtocolHandler.getCommand() != null || !willOpenProject.get()) { - WelcomeFrame.showNow(beforeSetVisible); + if (!willOpenProject.get() || JetBrainsProtocolHandler.getCommand() != null) { + WelcomeFrame.showNow(SplashManager.getHideTask()); lifecyclePublisher.welcomeScreenDisplayed(); } - else { - frame = windowManager.showFrame(beforeSetVisible); - } - activity.end(); + frameInitActivity.end(); + + AppExecutorUtil.getAppExecutorService().execute(() -> LifecycleUsageTriggerCollector.onIdeStart()); - IdeFrame finalFrame = frame; TransactionGuard.submitTransaction(app, () -> { Project projectFromCommandLine = ourPerformProjectLoad ? loadProjectFromExternalCommandLine(commandLineArgs) : null; // The appStarting callback in RecentProjectsManagerBase will reopen the last project - app.getMessageBus().syncPublisher(AppLifecycleListener.TOPIC).appStarting(projectFromCommandLine, finalFrame); + app.getMessageBus().syncPublisher(AppLifecycleListener.TOPIC).appStarting(projectFromCommandLine); //noinspection SSBasedInspection - SwingUtilities.invokeLater(PluginManager::reportPluginError); + EventQueue.invokeLater(PluginManager::reportPluginError); + }); - LifecycleUsageTriggerCollector.onIdeStart(); + if (!app.isHeadlessEnvironment()) { + postOpenUiTasks(app); + } + } + + private static void postOpenUiTasks(@NotNull Application app) { + if (SystemInfoRt.isMac) { + AppExecutorUtil.getAppExecutorService().execute(() -> { + TouchBarsManager.onApplicationInitialized(); + CustomActionsSchema customActionSchema = ServiceManager.getServiceIfCreated(CustomActionsSchema.class); + if (customActionSchema != null) { + customActionSchema.touchBarAvailable(TouchBarsManager.isTouchBarAvailable()); + } + }); + } + + app.invokeLater(() -> { + Activity updateSystemDockActivity = StartUpMeasurer.start("system dock menu"); + SystemDock.updateMenu(); + updateSystemDockActivity.end(); + }); + app.invokeLater(() -> { + GeneralSettings generalSettings = GeneralSettings.getInstance(); + generalSettings.addPropertyChangeListener(GeneralSettings.PROP_SUPPORT_SCREEN_READERS, app, + e -> ScreenReader.setActive((Boolean)e.getNewValue())); + ScreenReader.setActive(generalSettings.isSupportScreenReaders()); }); } } diff --git a/platform/platform-impl/src/com/intellij/internal/statistic/actions/RecordStateStatisticsEventLogAction.java b/platform/platform-impl/src/com/intellij/internal/statistic/actions/RecordStateStatisticsEventLogAction.java index e557710e0071..a33f2d603689 100644 --- a/platform/platform-impl/src/com/intellij/internal/statistic/actions/RecordStateStatisticsEventLogAction.java +++ b/platform/platform-impl/src/com/intellij/internal/statistic/actions/RecordStateStatisticsEventLogAction.java @@ -1,9 +1,7 @@ // 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. package com.intellij.internal.statistic.actions; -import com.intellij.internal.statistic.eventLog.EventLogExternalSettingsService; import com.intellij.internal.statistic.eventLog.fus.FeatureUsageLogger; -import com.intellij.internal.statistic.service.fus.FUSWhitelist; import com.intellij.internal.statistic.service.fus.collectors.FUStateUsagesLogger; import com.intellij.notification.Notification; import com.intellij.notification.NotificationType; @@ -19,7 +17,6 @@ import org.jetbrains.annotations.NotNull; public class RecordStateStatisticsEventLogAction extends AnAction { private static final FUStateUsagesLogger myStatesLogger = new FUStateUsagesLogger(); - private static final EventLogExternalSettingsService myEventLogSettingsService = EventLogExternalSettingsService.getFeatureUsageSettings(); @Override public void actionPerformed(@NotNull AnActionEvent e) { @@ -31,19 +28,9 @@ public class RecordStateStatisticsEventLogAction extends AnAction { ProgressManager.getInstance().run(new Task.Backgroundable(project, "Collecting Feature Usages In Event Log", false) { @Override public void run(@NotNull ProgressIndicator indicator) { - final String serviceUrl = myEventLogSettingsService.getServiceUrl(); - if (serviceUrl == null) { - return; - } - - final FUSWhitelist whitelist = myEventLogSettingsService.getApprovedGroups(); - if (whitelist.isEmpty() && !ApplicationManager.getApplication().isInternal()) { - return; - } - FeatureUsageLogger.INSTANCE.rollOver(); - myStatesLogger.logApplicationStates(whitelist, true); - myStatesLogger.logProjectStates(project, whitelist, true); + myStatesLogger.logApplicationStates(); + myStatesLogger.logProjectStates(project); ApplicationManager.getApplication().invokeLater( () -> showNotification(project, "Finished collecting and recording events") diff --git a/platform/platform-impl/src/com/intellij/internal/statistic/collectors/fus/actions/persistence/ActionsCollectorImpl.java b/platform/platform-impl/src/com/intellij/internal/statistic/collectors/fus/actions/persistence/ActionsCollectorImpl.java index 1d00ad8b9533..3c243bc71f92 100644 --- a/platform/platform-impl/src/com/intellij/internal/statistic/collectors/fus/actions/persistence/ActionsCollectorImpl.java +++ b/platform/platform-impl/src/com/intellij/internal/statistic/collectors/fus/actions/persistence/ActionsCollectorImpl.java @@ -38,10 +38,7 @@ import java.util.function.Consumer; /** * @author Konstantin Bulenkov */ -@State(name = "ActionsCollector", storages = @Storage( - value = UsageStatisticsPersistenceComponent.USAGE_STATISTICS_XML, roamingType = RoamingType.DISABLED, deprecated = true) -) -public class ActionsCollectorImpl extends ActionsCollector implements PersistentStateComponent { +public class ActionsCollectorImpl extends ActionsCollector { private static final String GROUP = "actions"; public static final String DEFAULT_ID = "third.party"; @@ -142,18 +139,6 @@ public class ActionsCollectorImpl extends ActionsCollector implements Persistent return myXmlActionIds.contains(actionId); } - private final State myState = new State(); - - @Nullable - @Override - public State getState() { - return myState; - } - - @Override - public void loadState(@NotNull State state) { - } - @Override public void onActionConfiguredByActionId(@NotNull AnAction action, @NotNull String actionId) { if (canReportActionId(actionId)) { diff --git a/platform/platform-impl/src/com/intellij/internal/statistic/collectors/fus/actions/persistence/IntentionsCollector.java b/platform/platform-impl/src/com/intellij/internal/statistic/collectors/fus/actions/persistence/IntentionsCollector.java index 859a29dcfaa5..6e15f484fc45 100644 --- a/platform/platform-impl/src/com/intellij/internal/statistic/collectors/fus/actions/persistence/IntentionsCollector.java +++ b/platform/platform-impl/src/com/intellij/internal/statistic/collectors/fus/actions/persistence/IntentionsCollector.java @@ -24,22 +24,7 @@ import java.util.Map; /** * @author Konstantin Bulenkov */ -@State(name = "IntentionsCollector", storages = @Storage( - value = UsageStatisticsPersistenceComponent.USAGE_STATISTICS_XML, roamingType = RoamingType.DISABLED, deprecated = true) -) -public class IntentionsCollector implements PersistentStateComponent { - - private final State myState = new State(); - - @Nullable - @Override - public State getState() { - return myState; - } - - @Override - public void loadState(@NotNull State state) { - } +public class IntentionsCollector { public void record(@NotNull IntentionAction action, @NotNull Language language) { record(null, action, language); diff --git a/platform/platform-impl/src/com/intellij/internal/statistic/collectors/fus/actions/persistence/MainMenuCollector.java b/platform/platform-impl/src/com/intellij/internal/statistic/collectors/fus/actions/persistence/MainMenuCollector.java index 4c437d7d0888..2632b08f4277 100644 --- a/platform/platform-impl/src/com/intellij/internal/statistic/collectors/fus/actions/persistence/MainMenuCollector.java +++ b/platform/platform-impl/src/com/intellij/internal/statistic/collectors/fus/actions/persistence/MainMenuCollector.java @@ -28,27 +28,7 @@ import java.util.stream.Collectors; /** * @author Konstantin Bulenkov */ -@State( - name = "MainMenuCollector", - storages = { - @Storage(value = UsageStatisticsPersistenceComponent.USAGE_STATISTICS_XML, roamingType = RoamingType.DISABLED, deprecated = true), - @Storage(value = "statistics.main_menu.xml", roamingType = RoamingType.DISABLED, deprecated = true) - } -) -public class MainMenuCollector implements PersistentStateComponent { - - private final State myState = new State(); - - @Nullable - @Override - public State getState() { - return myState; - } - - @Override - public void loadState(@NotNull State state) { - } - +public class MainMenuCollector { public void record(@NotNull AnAction action) { try { final PluginInfo info = PluginInfoDetectorKt.getPluginInfo(action.getClass()); diff --git a/platform/platform-impl/src/com/intellij/internal/statistic/collectors/fus/actions/persistence/ToolWindowCollector.java b/platform/platform-impl/src/com/intellij/internal/statistic/collectors/fus/actions/persistence/ToolWindowCollector.java index 6c296bbb1b35..fb2e3c67867d 100644 --- a/platform/platform-impl/src/com/intellij/internal/statistic/collectors/fus/actions/persistence/ToolWindowCollector.java +++ b/platform/platform-impl/src/com/intellij/internal/statistic/collectors/fus/actions/persistence/ToolWindowCollector.java @@ -2,12 +2,10 @@ package com.intellij.internal.statistic.collectors.fus.actions.persistence; import com.intellij.facet.ui.FacetDependentToolWindow; -import com.intellij.internal.statistic.collectors.fus.ui.persistence.ShortcutsCollector; import com.intellij.internal.statistic.eventLog.FeatureUsageData; import com.intellij.internal.statistic.eventLog.validator.ValidationResultType; import com.intellij.internal.statistic.eventLog.validator.rules.EventContext; import com.intellij.internal.statistic.eventLog.validator.rules.impl.CustomWhiteListRule; -import com.intellij.internal.statistic.persistence.UsageStatisticsPersistenceComponent; import com.intellij.internal.statistic.service.fus.collectors.FUCounterUsageLogger; import com.intellij.internal.statistic.utils.PluginInfo; import com.intellij.internal.statistic.utils.PluginInfoDetectorKt; @@ -16,8 +14,6 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.wm.ToolWindowEP; import com.intellij.openapi.wm.ToolWindowWhitelistEP; import com.intellij.openapi.wm.ext.LibraryDependentToolWindow; -import com.intellij.util.xmlb.annotations.MapAnnotation; -import com.intellij.util.xmlb.annotations.Tag; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -33,13 +29,7 @@ import static com.intellij.openapi.wm.ToolWindowId.*; /** * @author Konstantin Bulenkov */ -@State( - name = "ToolWindowsCollector", - storages = { - @Storage(value = UsageStatisticsPersistenceComponent.USAGE_STATISTICS_XML, roamingType = RoamingType.DISABLED, deprecated = true), - } -) -public class ToolWindowCollector implements PersistentStateComponent { +public class ToolWindowCollector { public static ToolWindowCollector getInstance() { return ServiceManager.getService(ToolWindowCollector.class); @@ -81,10 +71,6 @@ public class ToolWindowCollector implements PersistentStateComponent myValues = new HashMap<>(); - } - - @com.intellij.openapi.components.State( - name = "ToolWindowCollector", - storages = { - @Storage(value = UsageStatisticsPersistenceComponent.USAGE_STATISTICS_XML, roamingType = RoamingType.DISABLED, deprecated = true), - } - ) - public static class OutdatedToolWindowCollector implements PersistentStateComponent { - - public static OutdatedToolWindowCollector getInstance() { - return ServiceManager.getService(OutdatedToolWindowCollector.class); - } - - @Nullable - @Override - public ToolWindowCollector.State getState() { - return new State(); - } - - @Override - public void loadState(@NotNull ToolWindowCollector.State state) { - } - } - public static class ToolWindowUtilValidator extends CustomWhiteListRule { @Override diff --git a/platform/platform-impl/src/com/intellij/internal/statistic/collectors/fus/ui/persistence/ShortcutsCollector.java b/platform/platform-impl/src/com/intellij/internal/statistic/collectors/fus/ui/persistence/ShortcutsCollector.java deleted file mode 100644 index 40080a4d42a3..000000000000 --- a/platform/platform-impl/src/com/intellij/internal/statistic/collectors/fus/ui/persistence/ShortcutsCollector.java +++ /dev/null @@ -1,45 +0,0 @@ -// 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. -package com.intellij.internal.statistic.collectors.fus.ui.persistence; - -import com.intellij.internal.statistic.persistence.UsageStatisticsPersistenceComponent; -import com.intellij.openapi.components.*; -import com.intellij.util.xmlb.annotations.MapAnnotation; -import com.intellij.util.xmlb.annotations.Tag; -import org.jetbrains.annotations.NotNull; - -import java.util.HashMap; -import java.util.Map; - -/** - * @author Konstantin Bulenkov - */ -@State( - name = "ShortcutsCollector", - storages = { - @Storage(value = UsageStatisticsPersistenceComponent.USAGE_STATISTICS_XML, roamingType = RoamingType.DISABLED, deprecated = true), - @Storage(value = "statistics.shortcuts.xml", roamingType = RoamingType.DISABLED, deprecated = true) - } -) -public class ShortcutsCollector implements PersistentStateComponent { - public final static class MyState { - @Tag("counts") - @MapAnnotation(surroundWithTag = false, keyAttributeName = "shortcut", valueAttributeName = "count") - public final Map myValues = new HashMap<>(); - } - - private final MyState myState = new MyState(); - - @Override - @NotNull - public MyState getState() { - return myState; - } - - @Override - public void loadState(@NotNull final MyState state) { - } - - public static ShortcutsCollector getInstance() { - return ServiceManager.getService(ShortcutsCollector.class); - } -} diff --git a/platform/platform-impl/src/com/intellij/internal/statistic/collectors/fus/ui/persistence/ToolbarClicksCollector.java b/platform/platform-impl/src/com/intellij/internal/statistic/collectors/fus/ui/persistence/ToolbarClicksCollector.java index bf9f09b40d00..bf0a154428b4 100644 --- a/platform/platform-impl/src/com/intellij/internal/statistic/collectors/fus/ui/persistence/ToolbarClicksCollector.java +++ b/platform/platform-impl/src/com/intellij/internal/statistic/collectors/fus/ui/persistence/ToolbarClicksCollector.java @@ -2,58 +2,21 @@ package com.intellij.internal.statistic.collectors.fus.ui.persistence; import com.intellij.internal.statistic.collectors.fus.actions.persistence.ActionsCollectorImpl; -import com.intellij.internal.statistic.persistence.UsageStatisticsPersistenceComponent; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.DataContext; -import com.intellij.openapi.components.*; -import com.intellij.util.xmlb.annotations.MapAnnotation; -import com.intellij.util.xmlb.annotations.Tag; import org.jetbrains.annotations.NotNull; import java.awt.event.InputEvent; -import java.util.HashMap; -import java.util.Map; /** * @author Konstantin Bulenkov */ -@State( - name = "ToolbarClicksCollector", - storages = { - @Storage(value = UsageStatisticsPersistenceComponent.USAGE_STATISTICS_XML, roamingType = RoamingType.DISABLED, deprecated = true), - @Storage(value = "statistics.toolbar.clicks.xml", roamingType = RoamingType.DISABLED, deprecated = true) - } -) -public class ToolbarClicksCollector implements PersistentStateComponent { - - public final static class ClicksState { - @Tag("counts") - @MapAnnotation(surroundWithTag = false, keyAttributeName = "action", valueAttributeName = "count") - public Map myValues = new HashMap<>(); - } - - private final ClicksState myState = new ClicksState(); - - @Override - public ClicksState getState() { - return myState; - } - - @Override - public void loadState(@NotNull final ClicksState state) { - } +public class ToolbarClicksCollector { public static void record(@NotNull AnAction action, String place, @NotNull InputEvent inputEvent, @NotNull DataContext dataContext) { - ToolbarClicksCollector collector = getInstance(); - if (collector != null) { - AnActionEvent event = AnActionEvent.createFromInputEvent( - inputEvent, place, null, dataContext, false, true); - ActionsCollectorImpl.record("toolbar", event.getProject(), action, event, null); - } - } - - public static ToolbarClicksCollector getInstance() { - return ServiceManager.getService(ToolbarClicksCollector.class); + AnActionEvent event = AnActionEvent.createFromInputEvent( + inputEvent, place, null, dataContext, false, true); + ActionsCollectorImpl.record("toolbar", event.getProject(), action, event, null); } } \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/internal/statistic/eventLog/EventLogExternalSettingsService.java b/platform/platform-impl/src/com/intellij/internal/statistic/eventLog/EventLogExternalSettingsService.java index 143a7b6eea50..89725402fca4 100644 --- a/platform/platform-impl/src/com/intellij/internal/statistic/eventLog/EventLogExternalSettingsService.java +++ b/platform/platform-impl/src/com/intellij/internal/statistic/eventLog/EventLogExternalSettingsService.java @@ -2,7 +2,6 @@ package com.intellij.internal.statistic.eventLog; import com.intellij.facet.frameworks.SettingsConnectionService; -import com.intellij.internal.statistic.persistence.ApprovedGroupsCacheConfigurable; import com.intellij.internal.statistic.service.fus.FUSWhitelist; import com.intellij.internal.statistic.service.fus.FUStatisticsWhiteListGroupsService; import com.intellij.internal.statistic.utils.StatisticsUploadAssistant; @@ -15,9 +14,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; -import java.util.Date; -import java.util.concurrent.TimeUnit; - import static com.intellij.util.ObjectUtils.notNull; public class EventLogExternalSettingsService extends SettingsConnectionService implements EventLogSettingsService { @@ -25,8 +21,6 @@ public class EventLogExternalSettingsService extends SettingsConnectionService i private static final String APPROVED_GROUPS_SERVICE = "white-list-service"; private static final String DICTIONARY_SERVICE = "dictionary-service"; private static final String PERCENT_TRAFFIC = "percent-traffic"; - private static final long ACCEPTED_CACHE_AGE_MS = TimeUnit.MILLISECONDS.convert(7, TimeUnit.DAYS); - private static final long DONT_REQUIRE_UPDATE_AGE_MS = TimeUnit.MILLISECONDS.convert(1, TimeUnit.DAYS); /** * Use {@link EventLogExternalSettingsService#getFeatureUsageSettings()} @@ -42,11 +36,6 @@ public class EventLogExternalSettingsService extends SettingsConnectionService i return new EventLogExternalSettingsService("FUS"); } - @TestOnly - protected EventLogExternalSettingsService() { - super(null, null); - } - public EventLogExternalSettingsService(@NotNull String recorderId) { super(getConfigUrl(recorderId, false), null); } @@ -90,25 +79,6 @@ public class EventLogExternalSettingsService extends SettingsConnectionService i return getSettingValue(DICTIONARY_SERVICE); } - @NotNull - public FUSWhitelist getApprovedGroups() { - return getApprovedGroups(ApprovedGroupsCacheConfigurable.getInstance()); - } - - @NotNull - public FUSWhitelist getApprovedGroups(ApprovedGroupsCacheConfigurable cache) { - final BuildNumber currentBuild = getCurrentBuild(); - final Date currentDate = new Date(); - final FUSWhitelist cachedGroups = cache.getCachedGroups(currentDate, DONT_REQUIRE_UPDATE_AGE_MS, currentBuild); - if (cachedGroups != null) return cachedGroups; - - final FUSWhitelist groups = getWhitelistedGroups(); - if (groups != null) { - return cache.cacheGroups(currentDate, groups, currentBuild); - } - return notNull(cache.getCachedGroups(currentDate, ACCEPTED_CACHE_AGE_MS), FUSWhitelist.empty()); - } - @Override @NotNull public LogEventFilter getEventFilter() { @@ -125,7 +95,7 @@ public class EventLogExternalSettingsService extends SettingsConnectionService i protected FUSWhitelist getWhitelistedGroups() { final String productUrl = getWhiteListProductUrl(); if (productUrl == null) return null; - return FUStatisticsWhiteListGroupsService.getApprovedGroups(productUrl, getCurrentBuild()); + return FUStatisticsWhiteListGroupsService.getApprovedGroups(productUrl); } @Nullable diff --git a/platform/platform-impl/src/com/intellij/internal/statistic/eventLog/LogEventFilter.kt b/platform/platform-impl/src/com/intellij/internal/statistic/eventLog/LogEventFilter.kt index cb9af3d1474c..7050fe7986aa 100644 --- a/platform/platform-impl/src/com/intellij/internal/statistic/eventLog/LogEventFilter.kt +++ b/platform/platform-impl/src/com/intellij/internal/statistic/eventLog/LogEventFilter.kt @@ -10,7 +10,7 @@ interface LogEventFilter { class LogEventWhitelistFilter(val whitelist: FUSWhitelist) : LogEventFilter { override fun accepts(event: LogEvent): Boolean { - return whitelist.accepts(event.group.id, event.group.version) + return whitelist.accepts(event.group.id, event.group.version, event.build) } } diff --git a/platform/platform-impl/src/com/intellij/internal/statistic/eventLog/fus/FeatureUsageEventLoggerProvider.kt b/platform/platform-impl/src/com/intellij/internal/statistic/eventLog/fus/FeatureUsageEventLoggerProvider.kt index 0bb0bfb048a4..be55e416cad5 100644 --- a/platform/platform-impl/src/com/intellij/internal/statistic/eventLog/fus/FeatureUsageEventLoggerProvider.kt +++ b/platform/platform-impl/src/com/intellij/internal/statistic/eventLog/fus/FeatureUsageEventLoggerProvider.kt @@ -6,7 +6,7 @@ import com.intellij.internal.statistic.utils.StatisticsUploadAssistant import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.util.registry.Registry -class FeatureUsageEventLoggerProvider : StatisticsEventLoggerProvider("FUS", 18) { +class FeatureUsageEventLoggerProvider : StatisticsEventLoggerProvider("FUS", 20) { override fun isRecordEnabled(): Boolean { return !ApplicationManager.getApplication().isUnitTestMode && Registry.`is`("feature.usage.event.log.collect.and.upload") && diff --git a/platform/platform-impl/src/com/intellij/internal/statistic/eventLog/fus/FeatureUsageUiEventsImpl.kt b/platform/platform-impl/src/com/intellij/internal/statistic/eventLog/fus/FeatureUsageUiEventsImpl.kt index 288c2c553629..9bf58a9668ad 100644 --- a/platform/platform-impl/src/com/intellij/internal/statistic/eventLog/fus/FeatureUsageUiEventsImpl.kt +++ b/platform/platform-impl/src/com/intellij/internal/statistic/eventLog/fus/FeatureUsageUiEventsImpl.kt @@ -4,8 +4,11 @@ package com.intellij.internal.statistic.eventLog.fus import com.intellij.internal.statistic.eventLog.FeatureUsageData import com.intellij.internal.statistic.eventLog.FeatureUsageUiEvents import com.intellij.internal.statistic.service.fus.collectors.FUCounterUsageLogger +import com.intellij.openapi.options.Configurable +import com.intellij.openapi.options.ex.ConfigurableWrapper import com.intellij.openapi.ui.DialogWrapper +private const val SETTINGS = "ui.settings" private const val DIALOGS = "ui.dialogs" class FeatureUsageUiEventsImpl : FeatureUsageUiEvents { @@ -18,13 +21,29 @@ class FeatureUsageUiEventsImpl : FeatureUsageUiEvents { private val CLOSE_CANCEL_DIALOG_DATA = FeatureUsageData().addData("type", "close").addData("code", DialogWrapper.CANCEL_EXIT_CODE) private val CLOSE_CUSTOM_DIALOG_DATA = FeatureUsageData().addData("type", "close").addData("code", DialogWrapper.NEXT_USER_EXIT_CODE) - override fun logSelectConfigurable(name: String, context: Class<*>) { + override fun logSelectConfigurable(configurable: Configurable) { + if (FeatureUsageLogger.isEnabled()) { + logSettingsEvent(configurable, SELECT_CONFIGURABLE_DATA) + } } - override fun logApplyConfigurable(name: String, context: Class<*>) { + override fun logApplyConfigurable(configurable: Configurable) { + if (FeatureUsageLogger.isEnabled()) { + logSettingsEvent(configurable, APPLY_CONFIGURABLE_DATA) + } } - override fun logResetConfigurable(name: String, context: Class<*>) { + override fun logResetConfigurable(configurable: Configurable) { + if (FeatureUsageLogger.isEnabled()) { + logSettingsEvent(configurable, RESET_CONFIGURABLE_DATA) + } + } + + private fun logSettingsEvent(configurable: Configurable, data: FeatureUsageData) { + val base: Any? = if (configurable is ConfigurableWrapper) configurable.configurable else configurable + base?.let { + FUCounterUsageLogger.getInstance().logEvent(SETTINGS, base::class.java.name, data) + } } override fun logShowDialog(name: String, context: Class<*>) { diff --git a/platform/platform-impl/src/com/intellij/internal/statistic/service/fus/FUSWhitelist.java b/platform/platform-impl/src/com/intellij/internal/statistic/service/fus/FUSWhitelist.java index 63ab046f437d..efc715054760 100644 --- a/platform/platform-impl/src/com/intellij/internal/statistic/service/fus/FUSWhitelist.java +++ b/platform/platform-impl/src/com/intellij/internal/statistic/service/fus/FUSWhitelist.java @@ -1,10 +1,8 @@ // 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. package com.intellij.internal.statistic.service.fus; -import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.xmlb.annotations.Attribute; -import com.intellij.util.xmlb.annotations.Tag; -import com.intellij.util.xmlb.annotations.XMap; +import com.intellij.openapi.util.BuildNumber; +import com.intellij.openapi.util.text.StringUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -14,18 +12,18 @@ import java.util.Map; import java.util.Objects; public class FUSWhitelist { - private Map> myGroups; + private Map myGroups; public FUSWhitelist() { } - private FUSWhitelist(@NotNull Map> groups) { + private FUSWhitelist(@NotNull Map groups) { myGroups = groups; } @NotNull - public static FUSWhitelist create(@NotNull Map> groupsToVersion) { - return new FUSWhitelist(groupsToVersion); + public static FUSWhitelist create(@NotNull Map groups) { + return new FUSWhitelist(groups); } @NotNull @@ -33,29 +31,17 @@ public class FUSWhitelist { return new FUSWhitelist(Collections.emptyMap()); } - @XMap(propertyElementName = "groups", keyAttributeName = "id", entryTagName = "group") - public Map> getGroups() { - return myGroups; - } - - public void setGroups(Map> groups) { - myGroups = groups; - } - - public boolean accepts(@NotNull String groupId, @Nullable String version) { - final int parsed = tryToParse(version, -1); - if (parsed < 0) { - return false; - } - return accepts(groupId, parsed); - } - - public boolean accepts(@NotNull String groupId, int version) { + public boolean accepts(@NotNull String groupId, @Nullable String version, @NotNull String build) { if (!myGroups.containsKey(groupId)) { return false; } - final List ranges = myGroups.get(groupId); - return ranges.isEmpty() || ContainerUtil.find(ranges, range -> range.contains(version)) != null; + + final int parsedVersion = tryToParse(version, -1); + if (parsedVersion < 0) { + return false; + } + final GroupFilterCondition condition = myGroups.get(groupId); + return condition.accepts(build, parsedVersion); } public int getSize() { @@ -91,38 +77,104 @@ public class FUSWhitelist { return Objects.hash(myGroups); } - @Tag("version") - public static class VersionRange { - private int myFrom; - private int myTo; + public static class GroupFilterCondition { + private final List builds; + private final List versions; - public VersionRange() { + public GroupFilterCondition(@NotNull List builds, @NotNull List versions) { + this.builds = builds; + this.versions = versions; + } + + public boolean accepts(@NotNull String build, int version) { + if (!isValid()) { + return false; + } + return acceptsBuild(build) && acceptsVersion(version); + } + + private boolean acceptsBuild(@NotNull String build) { + if (builds.isEmpty()) return true; + + final BuildNumber number = BuildNumber.fromString(build); + return number != null && builds.stream().anyMatch(b -> b.contains(number)); + } + + private boolean acceptsVersion(int version) { + if (versions.isEmpty()) return true; + return version > 0 && versions.stream().anyMatch(v -> v.contains(version)); + } + + private boolean isValid() { + return !builds.isEmpty() || !versions.isEmpty(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + GroupFilterCondition condition = (GroupFilterCondition)o; + return Objects.equals(builds, condition.builds) && + Objects.equals(versions, condition.versions); + } + + @Override + public int hashCode() { + return Objects.hash(builds, versions); + } + } + + public static class BuildRange { + private final BuildNumber myFrom; + private final BuildNumber myTo; + + public BuildRange(@Nullable BuildNumber from, @Nullable BuildNumber to) { + myFrom = from; + myTo = to; + } + + @NotNull + public static BuildRange create(@Nullable String from, @Nullable String to) { + return new BuildRange( + StringUtil.isNotEmpty(from) ? BuildNumber.fromString(from) : null, + StringUtil.isNotEmpty(to) ? BuildNumber.fromString(to) : null + ); + } + + public boolean contains(@NotNull BuildNumber build) { + return (myTo == null || myTo.compareTo(build) > 0) && (myFrom == null || myFrom.compareTo(build) <= 0); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + BuildRange range = (BuildRange)o; + return Objects.equals(myFrom, range.myFrom) && + Objects.equals(myTo, range.myTo); + } + + @Override + public int hashCode() { + return Objects.hash(myFrom, myTo); + } + } + + public static class VersionRange { + private final int myFrom; + private final int myTo; + + public VersionRange(int from, int to) { + myFrom = from; + myTo = to; } @NotNull public static VersionRange create(@Nullable String from, @Nullable String to) { - final VersionRange range = new VersionRange(); - range.setFrom(from == null ? 0 : tryToParse(from, Integer.MAX_VALUE)); - range.setTo(to == null ? Integer.MAX_VALUE : tryToParse(to, 0)); - return range; - } - - @Attribute("from") - public int getFrom() { - return myFrom; - } - - public void setFrom(int from) { - myFrom = from; - } - - @Attribute("to") - public int getTo() { - return myTo; - } - - public void setTo(int to) { - myTo = to; + return new VersionRange( + from == null ? 0 : tryToParse(from, Integer.MAX_VALUE), + to == null ? Integer.MAX_VALUE : tryToParse(to, 0) + ); } public boolean contains(int current) { diff --git a/platform/platform-impl/src/com/intellij/internal/statistic/service/fus/FUStatisticsWhiteListGroupsService.java b/platform/platform-impl/src/com/intellij/internal/statistic/service/fus/FUStatisticsWhiteListGroupsService.java index 328f293be6b2..0a08ce018d77 100644 --- a/platform/platform-impl/src/com/intellij/internal/statistic/service/fus/FUStatisticsWhiteListGroupsService.java +++ b/platform/platform-impl/src/com/intellij/internal/statistic/service/fus/FUStatisticsWhiteListGroupsService.java @@ -4,6 +4,8 @@ package com.intellij.internal.statistic.service.fus; import com.google.common.annotations.VisibleForTesting; import com.google.gson.GsonBuilder; import com.intellij.internal.statistic.eventLog.EventLogExternalSettingsService; +import com.intellij.internal.statistic.service.fus.FUSWhitelist.BuildRange; +import com.intellij.internal.statistic.service.fus.FUSWhitelist.GroupFilterCondition; import com.intellij.internal.statistic.service.fus.FUSWhitelist.VersionRange; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.BuildNumber; @@ -14,9 +16,9 @@ import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.util.*; -import java.util.stream.Collectors; -import static com.intellij.util.containers.ContainerUtil.*; +import static com.intellij.util.containers.ContainerUtil.map; +import static java.util.Collections.emptyList; /** *

    @@ -47,10 +49,9 @@ public class FUStatisticsWhiteListGroupsService { * @return null if error happened during groups fetching */ @Nullable - public static FUSWhitelist getApprovedGroups(@NotNull String serviceUrl, @NotNull BuildNumber current) { - String content = getFUSWhiteListContent(serviceUrl); - - return content != null ? parseApprovedGroups(content, current) : null; + public static FUSWhitelist getApprovedGroups(@NotNull String serviceUrl) { + final String content = getFUSWhiteListContent(serviceUrl); + return content != null ? parseApprovedGroups(content) : null; } @Nullable @@ -107,22 +108,26 @@ public class FUStatisticsWhiteListGroupsService { @VisibleForTesting @NotNull - public static FUSWhitelist parseApprovedGroups(String content, @NotNull BuildNumber build) { - WLGroups groups = parseWhiteListContent(content); - + public static FUSWhitelist parseApprovedGroups(@Nullable String content) { + final WLGroups groups = parseWhiteListContent(content); if (groups == null) { return FUSWhitelist.empty(); } - final Map> result = groups.groups.stream(). - filter(group -> group.accepts(build)). - collect(Collectors.toMap(group -> group.id, group -> toVersionRanges(group.versions))); - return FUSWhitelist.create(result); + final Map groupToCondition = new HashMap<>(); + for (WLGroup group : groups.groups) { + if (group.isValid()) { + groupToCondition.put(group.id, toCondition(group.builds, group.versions)); + } + } + return FUSWhitelist.create(groupToCondition); } @NotNull - private static List toVersionRanges(@Nullable ArrayList versions) { - return versions == null || versions.isEmpty() ? emptyList() : map(versions, version -> VersionRange.create(version.from, version.to)); + private static GroupFilterCondition toCondition(@Nullable List builds, @Nullable List versions) { + final List buildRanges = builds != null ? map(builds, b -> BuildRange.create(b.from, b.to)) : emptyList(); + final List versionRanges = versions != null ? map(versions, v -> VersionRange.create(v.from, v.to)) : emptyList(); + return new GroupFilterCondition(buildRanges, versionRanges); } public static class WLGroups { diff --git a/platform/platform-impl/src/com/intellij/internal/statistic/service/fus/collectors/FUCounterUsageLogger.java b/platform/platform-impl/src/com/intellij/internal/statistic/service/fus/collectors/FUCounterUsageLogger.java index eb41e0d9fe09..f96d5cf80586 100644 --- a/platform/platform-impl/src/com/intellij/internal/statistic/service/fus/collectors/FUCounterUsageLogger.java +++ b/platform/platform-impl/src/com/intellij/internal/statistic/service/fus/collectors/FUCounterUsageLogger.java @@ -27,7 +27,8 @@ public class FUCounterUsageLogger { */ private static final String REGISTERED = "registered"; private static final String[] GENERAL_GROUPS = new String[]{ - "lifecycle", "performance", "actions", "ui.dialogs", "toolwindow", "intentions", "toolbar", "run.configuration.exec", + "lifecycle", "performance", "actions", "ui.dialogs", "ui.settings", + "toolwindow", "intentions", "toolbar", "run.configuration.exec", "file.types.usage", "productivity", "live.templates", "completion.postfix" }; diff --git a/platform/platform-impl/src/com/intellij/internal/statistic/service/fus/collectors/FUStateUsagesLogger.java b/platform/platform-impl/src/com/intellij/internal/statistic/service/fus/collectors/FUStateUsagesLogger.java index 2ec6d36a2cba..fbfc0b9eead3 100644 --- a/platform/platform-impl/src/com/intellij/internal/statistic/service/fus/collectors/FUStateUsagesLogger.java +++ b/platform/platform-impl/src/com/intellij/internal/statistic/service/fus/collectors/FUStateUsagesLogger.java @@ -2,12 +2,9 @@ package com.intellij.internal.statistic.service.fus.collectors; import com.intellij.internal.statistic.beans.MetricEvent; -import com.intellij.internal.statistic.eventLog.EventLogExternalSettingsService; import com.intellij.internal.statistic.eventLog.EventLogGroup; import com.intellij.internal.statistic.eventLog.FeatureUsageData; import com.intellij.internal.statistic.eventLog.fus.FeatureUsageLogger; -import com.intellij.internal.statistic.service.fus.FUSWhitelist; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -27,33 +24,19 @@ public class FUStateUsagesLogger implements UsagesCollectorConsumer { public static FUStateUsagesLogger create() { return new FUStateUsagesLogger(); } public void logProjectStates(@NotNull Project project) { - logProjectStates(project, EventLogExternalSettingsService.getFeatureUsageSettings().getApprovedGroups(), false); - } - - public void logApplicationStates() { - logApplicationStates(EventLogExternalSettingsService.getFeatureUsageSettings().getApprovedGroups(), false); - } - - public void logProjectStates(@NotNull Project project, @NotNull FUSWhitelist whitelist, boolean recordAll) { - if (!whitelist.isEmpty() || ApplicationManager.getApplication().isInternal()) { - synchronized (LOCK) { - for (ProjectUsagesCollector usagesCollector : ProjectUsagesCollector.getExtensions(this)) { - if (recordAll || whitelist.accepts(usagesCollector.getGroupId(), usagesCollector.getVersion())) { - final EventLogGroup group = new EventLogGroup(usagesCollector.getGroupId(), usagesCollector.getVersion()); - logUsagesAsStateEvents(project, group, usagesCollector.getData(project), usagesCollector.getMetrics(project)); - } - } + synchronized (LOCK) { + for (ProjectUsagesCollector usagesCollector : ProjectUsagesCollector.getExtensions(this)) { + final EventLogGroup group = new EventLogGroup(usagesCollector.getGroupId(), usagesCollector.getVersion()); + logUsagesAsStateEvents(project, group, usagesCollector.getData(project), usagesCollector.getMetrics(project)); } } } - public void logApplicationStates(@NotNull FUSWhitelist whitelist, boolean recordAll) { + public void logApplicationStates() { synchronized (LOCK) { for (ApplicationUsagesCollector usagesCollector : ApplicationUsagesCollector.getExtensions(this)) { - if (recordAll || whitelist.accepts(usagesCollector.getGroupId(), usagesCollector.getVersion())) { - final EventLogGroup group = new EventLogGroup(usagesCollector.getGroupId(), usagesCollector.getVersion()); - logUsagesAsStateEvents(null, group, usagesCollector.getData(), usagesCollector.getMetrics()); - } + final EventLogGroup group = new EventLogGroup(usagesCollector.getGroupId(), usagesCollector.getVersion()); + logUsagesAsStateEvents(null, group, usagesCollector.getData(), usagesCollector.getMetrics()); } } } diff --git a/platform/platform-impl/src/com/intellij/internal/statistic/service/fus/collectors/LegacyApplicationUsageTriggers.java b/platform/platform-impl/src/com/intellij/internal/statistic/service/fus/collectors/LegacyApplicationUsageTriggers.java deleted file mode 100644 index 941b52324eb3..000000000000 --- a/platform/platform-impl/src/com/intellij/internal/statistic/service/fus/collectors/LegacyApplicationUsageTriggers.java +++ /dev/null @@ -1,66 +0,0 @@ -// 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. -package com.intellij.internal.statistic.service.fus.collectors; - -import com.intellij.internal.statistic.persistence.UsageStatisticsPersistenceComponent; -import com.intellij.openapi.components.*; -import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.xmlb.annotations.Property; -import com.intellij.util.xmlb.annotations.XCollection; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -@State(name = "StatisticsApplicationUsages", storages = @Storage( - value = UsageStatisticsPersistenceComponent.USAGE_STATISTICS_XML, roamingType = RoamingType.DISABLED, deprecated = true) -) -public class LegacyApplicationUsageTriggers implements PersistentStateComponent { - State myState = new State(); - - @Nullable - @Override - public State getState() { - return myState; - } - - @Override - public void loadState(@NotNull State state) { - } - - public static void cleanup() { - ServiceManager.getService(LegacyApplicationUsageTriggers.class); - ServiceManager.getService(LegacyUsageTrigger.class); - LegacyFUSApplicationUsageTrigger.cleanup(); - } - - public final static class State { - @Property(surroundWithTag = false) - @XCollection - List groups = ContainerUtil.newSmartList(); - } - - public final static class CounterState { - @Property(surroundWithTag = false) - @XCollection - Map counts = new HashMap<>(); - } - - @com.intellij.openapi.components.State(name = "UsageTrigger", storages = @Storage( - value = UsageStatisticsPersistenceComponent.USAGE_STATISTICS_XML, roamingType = RoamingType.DISABLED, deprecated = true) - ) - private static class LegacyUsageTrigger implements PersistentStateComponent { - CounterState myState = new CounterState(); - - @Nullable - @Override - public CounterState getState() { - return myState; - } - - @Override - public void loadState(@NotNull CounterState state) { - } - } -} diff --git a/platform/platform-impl/src/com/intellij/internal/statistic/service/fus/collectors/LegacyFUSApplicationUsageTrigger.java b/platform/platform-impl/src/com/intellij/internal/statistic/service/fus/collectors/LegacyFUSApplicationUsageTrigger.java deleted file mode 100644 index 3f0f97bcc9c2..000000000000 --- a/platform/platform-impl/src/com/intellij/internal/statistic/service/fus/collectors/LegacyFUSApplicationUsageTrigger.java +++ /dev/null @@ -1,36 +0,0 @@ -// 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. -package com.intellij.internal.statistic.service.fus.collectors; - -import com.intellij.internal.statistic.persistence.UsageStatisticsPersistenceComponent; -import com.intellij.openapi.components.*; -import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.xmlb.annotations.Transient; -import org.jetbrains.annotations.NotNull; - -import java.util.List; - -@State(name = "FUSApplicationUsageTrigger", - storages = @Storage(value = UsageStatisticsPersistenceComponent.USAGE_STATISTICS_XML, roamingType = RoamingType.DISABLED, deprecated = true) -) -final public class LegacyFUSApplicationUsageTrigger implements PersistentStateComponent { - private final State myState = new State(); - - final static class State { - @Transient - List sessions = ContainerUtil.newSmartList(); - } - - public static void cleanup() { - ServiceManager.getService(LegacyFUSApplicationUsageTrigger.class); - } - - @Override - public State getState() { - return myState; - } - - @Override - public void loadState(@NotNull final State state) { - } - -} diff --git a/platform/platform-impl/src/com/intellij/internal/statistic/updater/StatisticsJobsScheduler.java b/platform/platform-impl/src/com/intellij/internal/statistic/updater/StatisticsJobsScheduler.java index d06db0ad5738..a0f5a0a912a9 100644 --- a/platform/platform-impl/src/com/intellij/internal/statistic/updater/StatisticsJobsScheduler.java +++ b/platform/platform-impl/src/com/intellij/internal/statistic/updater/StatisticsJobsScheduler.java @@ -11,7 +11,6 @@ import com.intellij.internal.statistic.eventLog.StatisticsEventLoggerProvider; import com.intellij.internal.statistic.eventLog.validator.SensitiveDataValidator; import com.intellij.internal.statistic.service.fus.collectors.FUStateUsagesLogger; import com.intellij.internal.statistic.service.fus.collectors.FUStatisticsPersistence; -import com.intellij.internal.statistic.service.fus.collectors.LegacyApplicationUsageTriggers; import com.intellij.internal.statistic.service.fus.collectors.LegacyFUSProjectUsageTrigger; import com.intellij.internal.statistic.utils.StatisticsUploadAssistant; import com.intellij.notification.impl.NotificationsConfigurationImpl; @@ -129,7 +128,6 @@ public class StatisticsJobsScheduler implements ApplicationInitializedListener { private static void runLegacyDataCleanupService() { JobScheduler.getScheduler().schedule(() -> { FUStatisticsPersistence.clearLegacyStates(); - LegacyApplicationUsageTriggers.cleanup(); }, 1, TimeUnit.MINUTES); } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/ex/FocusChangeListener.java b/platform/platform-impl/src/com/intellij/openapi/editor/ex/FocusChangeListener.java index e50c4a7c265f..9c7e3b27fd3f 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/ex/FocusChangeListener.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/ex/FocusChangeListener.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2009 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-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. package com.intellij.openapi.editor.ex; import com.intellij.openapi.editor.Editor; @@ -26,7 +12,8 @@ import java.util.EventListener; */ public interface FocusChangeListener extends EventListener { void focusGained(@NotNull Editor editor); - void focusLost(@NotNull Editor editor); + default void focusLost(@NotNull Editor editor) { + } default void focusLost(@NotNull Editor editor, @SuppressWarnings("unused") @NotNull FocusEvent event) { focusLost(editor); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorFactoryImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorFactoryImpl.java index e228045299c2..28cf9df0f465 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorFactoryImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorFactoryImpl.java @@ -29,7 +29,6 @@ import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.project.impl.ProjectLifecycleListener; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.ui.mac.touchbar.TouchBarsManager; import com.intellij.util.EventDispatcher; import com.intellij.util.SmartList; import com.intellij.util.containers.ContainerUtil; @@ -194,7 +193,6 @@ public class EditorFactoryImpl extends EditorFactory { myEditors.add(editor); myEditorEventMulticaster.registerEditor(editor); myEditorFactoryEventDispatcher.getMulticaster().editorCreated(new EditorFactoryEvent(this, editor)); - TouchBarsManager.registerEditor(editor); if (LOG.isDebugEnabled()) { LOG.debug("number of Editors after create: " + myEditors.size()); @@ -219,7 +217,6 @@ public class EditorFactoryImpl extends EditorFactory { } } } - TouchBarsManager.releaseEditor(editor); } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java index f035651ecb3d..0c937e5b2b3a 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java @@ -1930,7 +1930,9 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi myHeaderPanel.revalidate(); myHeaderPanel.repaint(); - TouchBarsManager.onUpdateEditorHeader(this, header); + if (SystemInfoRt.isMac) { + TouchBarsManager.onUpdateEditorHeader(this, header); + } } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java index 127651275d3e..adaf1ed894a5 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java @@ -5,7 +5,6 @@ import com.intellij.ProjectTopics; import com.intellij.featureStatistics.fusCollectors.LifecycleUsageTriggerCollector; import com.intellij.ide.IdeBundle; import com.intellij.ide.IdeEventQueue; -import com.intellij.ide.plugins.PluginManagerCore; import com.intellij.ide.ui.UISettings; import com.intellij.ide.ui.UISettingsListener; import com.intellij.injected.editor.VirtualFileWindow; @@ -1513,7 +1512,6 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Persis LifecycleUsageTriggerCollector.onProjectOpenFinished(myProject, time); LOG.info("Project opening took " + time + " ms"); - PluginManagerCore.dumpPluginClassStatistics(); } }, myProject.getDisposed()); // group 1 diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/IdeDocumentHistoryImpl.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/IdeDocumentHistoryImpl.java index bffed1815932..540a942d85fe 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/IdeDocumentHistoryImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/IdeDocumentHistoryImpl.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-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. package com.intellij.openapi.fileEditor.impl; import com.intellij.ide.ui.UISettings; @@ -52,7 +52,10 @@ import java.lang.ref.Reference; import java.lang.ref.WeakReference; import java.util.*; -@State(name = "IdeDocumentHistory", storages = @Storage(StoragePathMacros.WORKSPACE_FILE)) +@State(name = "IdeDocumentHistory", storages = { + @Storage(StoragePathMacros.PRODUCT_WORKSPACE_FILE), + @Storage(value = StoragePathMacros.WORKSPACE_FILE, deprecated = true) +}) public class IdeDocumentHistoryImpl extends IdeDocumentHistory implements Disposable, PersistentStateComponent { private static final Logger LOG = Logger.getInstance(IdeDocumentHistoryImpl.class); diff --git a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/ConfigurableEditor.java b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/ConfigurableEditor.java index 997126b2adfc..33a1a5bc51b7 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/ConfigurableEditor.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/ConfigurableEditor.java @@ -72,7 +72,7 @@ class ConfigurableEditor extends AbstractEditor implements AnActionListener, AWT if (myConfigurable != null) { ConfigurableCardPanel.reset(myConfigurable); updateCurrent(myConfigurable, true); - FeatureUsageUiEventsKt.getUiEventLogger().logResetConfigurable(getConfigurableEventId(myConfigurable), myConfigurable.getClass()); + FeatureUsageUiEventsKt.getUiEventLogger().logResetConfigurable(myConfigurable); } } }; @@ -247,7 +247,7 @@ class ConfigurableEditor extends AbstractEditor implements AnActionListener, AWT updateCurrent(configurable, false); postUpdateCurrent(configurable); if (configurable != null) { - FeatureUsageUiEventsKt.getUiEventLogger().logSelectConfigurable(getConfigurableEventId(configurable), configurable.getClass()); + FeatureUsageUiEventsKt.getUiEventLogger().logSelectConfigurable(configurable); } }); return Promises.toPromise(callback); @@ -318,8 +318,7 @@ class ConfigurableEditor extends AbstractEditor implements AnActionListener, AWT if (configurable != null) { try { configurable.apply(); - final String key = getConfigurableEventId(configurable); - FeatureUsageUiEventsKt.getUiEventLogger().logApplyConfigurable(key, configurable.getClass()); + FeatureUsageUiEventsKt.getUiEventLogger().logApplyConfigurable(configurable); } catch (ConfigurationException exception) { return exception; @@ -327,9 +326,4 @@ class ConfigurableEditor extends AbstractEditor implements AnActionListener, AWT } return null; } - - @NotNull - private static String getConfigurableEventId(@NotNull Configurable configurable) { - return "ide.settings." + ConvertUsagesUtil.escapeDescriptorName(StringUtil.notNullize(configurable.getDisplayName())); - } } diff --git a/platform/platform-impl/src/com/intellij/openapi/ui/impl/DialogWrapperPeerImpl.java b/platform/platform-impl/src/com/intellij/openapi/ui/impl/DialogWrapperPeerImpl.java index be35d6779f61..7f96e60c9f61 100644 --- a/platform/platform-impl/src/com/intellij/openapi/ui/impl/DialogWrapperPeerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/ui/impl/DialogWrapperPeerImpl.java @@ -421,9 +421,12 @@ public class DialogWrapperPeerImpl extends DialogWrapperPeer { myDialog.getWindow().setAutoRequestFocus(!Registry.is("suppress.focus.stealing")); - final Disposable tb = TouchBarsManager.showDialogWrapperButtons(myDialog.getContentPane()); - if (tb != null) - myDisposeActions.add(() -> Disposer.dispose(tb)); + if (SystemInfoRt.isMac) { + final Disposable tb = TouchBarsManager.showDialogWrapperButtons(myDialog.getContentPane()); + if (tb != null) { + myDisposeActions.add(() -> Disposer.dispose(tb)); + } + } try { myDialog.show(); @@ -433,7 +436,8 @@ public class DialogWrapperPeerImpl extends DialogWrapperPeer { commandProcessor.leaveModal(); if (perProjectModality) { LaterInvocator.leaveModal(project, myDialog.getWindow()); - } else { + } + else { LaterInvocator.leaveModal(myDialog); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeFrameImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeFrameImpl.java index 8d5b3b7bd0c5..63eb85ecaa29 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeFrameImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeFrameImpl.java @@ -2,7 +2,6 @@ package com.intellij.openapi.wm.impl; import com.intellij.diagnostic.IdeMessagePanel; -import com.intellij.ide.DataManager; import com.intellij.ide.ui.LafManager; import com.intellij.ide.ui.LafManagerListener; import com.intellij.ide.ui.UISettings; @@ -11,7 +10,6 @@ import com.intellij.notification.impl.IdeNotificationArea; import com.intellij.openapi.MnemonicHelper; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.DataProvider; -import com.intellij.openapi.actionSystem.ex.ActionManagerEx; import com.intellij.openapi.actionSystem.impl.MouseGestureManager; import com.intellij.openapi.application.*; import com.intellij.openapi.application.impl.LaterInvocator; @@ -63,7 +61,7 @@ import java.util.Set; * @author Anton Katilin * @author Vladimir Kondratyev */ -public class IdeFrameImpl extends JFrame implements IdeFrameEx, AccessibleContextAccessor, DataProvider { +public final class IdeFrameImpl extends JFrame implements IdeFrameEx, AccessibleContextAccessor, DataProvider { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.wm.impl.IdeFrameImpl"); public static final String NORMAL_STATE_BOUNDS = "normalBounds"; @@ -87,11 +85,11 @@ public class IdeFrameImpl extends JFrame implements IdeFrameEx, AccessibleContex private boolean ready; private Image mySelfie; - public IdeFrameImpl(ActionManagerEx actionManager, DataManager dataManager) { + public IdeFrameImpl() { super(); updateTitle(); - myRootPane = createRootPane(actionManager, dataManager); + myRootPane = new IdeRootPane(this); setRootPane(myRootPane); setBackground(UIUtil.getPanelBackground()); LafManager.getInstance().addLafManagerListener(myLafListener = src -> setBackground(UIUtil.getPanelBackground())); @@ -110,7 +108,7 @@ public class IdeFrameImpl extends JFrame implements IdeFrameEx, AccessibleContex Dimension size = ScreenUtil.getMainScreenBounds().getSize(); size.width = Math.min(1400, size.width - 20); - size.height= Math.min(1000, size.height - 40); + size.height = Math.min(1000, size.height - 40); setSize(size); setLocationRelativeTo(null); setMinimumSize(new Dimension(340, getMinimumSize().height)); @@ -154,7 +152,9 @@ public class IdeFrameImpl extends JFrame implements IdeFrameEx, AccessibleContex // to show window thumbnail under Macs // http://lists.apple.com/archives/java-dev/2009/Dec/msg00240.html - if (SystemInfoRt.isMac) setIconImage(null); + if (SystemInfoRt.isMac) { + setIconImage(null); + } MouseGestureManager.getInstance().add(this); @@ -258,11 +258,6 @@ public class IdeFrameImpl extends JFrame implements IdeFrameEx, AccessibleContex PowerSupplyKit.checkPowerSupply(); } - @NotNull - private IdeRootPane createRootPane(ActionManagerEx actionManager, DataManager dataManager) { - return new IdeRootPane(actionManager, dataManager, this); - } - @NotNull @Override public Insets getInsets() { 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 4d2e5cf78e22..26639b498a84 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 @@ -310,7 +310,9 @@ public class IdeMenuBar extends JMenuBar implements IdeEventQueue.EventDispatche return component; } - void updateMenuActions() { updateMenuActions(false); } + void updateMenuActions() { + updateMenuActions(false); + } void updateMenuActions(boolean forceRebuild) { myNewVisibleActions.clear(); diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeRootPane.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeRootPane.java index 38d94da10c14..74b1bf7dc7ef 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeRootPane.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeRootPane.java @@ -18,7 +18,9 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.SystemInfoRt; -import com.intellij.openapi.wm.*; +import com.intellij.openapi.wm.IdeFrame; +import com.intellij.openapi.wm.IdeRootPaneNorthExtension; +import com.intellij.openapi.wm.StatusBar; import com.intellij.openapi.wm.ex.IdeFrameEx; import com.intellij.openapi.wm.impl.customFrameDecorations.header.CustomHeader; import com.intellij.openapi.wm.impl.customFrameDecorations.header.MainFrameHeader; @@ -44,7 +46,7 @@ import java.util.List; * @author Anton Katilin * @author Vladimir Kondratyev */ -public class IdeRootPane extends JRootPane implements UISettingsListener, Disposable { +public final class IdeRootPane extends JRootPane implements UISettingsListener, Disposable { /** * Toolbar and status bar. */ @@ -60,7 +62,6 @@ public class IdeRootPane extends JRootPane implements UISettingsListener, Dispos */ private ToolWindowsPane myToolWindowsPane; private JBPanel myContentPane; - private final ActionManager myActionManager; private final boolean myGlassPaneInitialized; @@ -69,9 +70,9 @@ public class IdeRootPane extends JRootPane implements UISettingsListener, Dispos private boolean myFullScreen; private MainFrameHeader myCustomFrameTitlePane; - private boolean myDecoratedMenu = false; + private final boolean myDecoratedMenu; - IdeRootPane(ActionManagerEx actionManager, DataManager dataManager, final IdeFrame frame) { + IdeRootPane(@NotNull IdeFrame frame) { if (SystemInfoRt.isWindows && (UIUtil.isUnderDarcula() || UIUtil.isUnderIntelliJLaF()) && frame instanceof IdeFrameImpl) { //setUI(DarculaRootPaneUI.createUI(this)); try { @@ -81,20 +82,16 @@ public class IdeRootPane extends JRootPane implements UISettingsListener, Dispos Logger.getInstance(IdeRootPane.class).error(e); } } - myActionManager = actionManager; myContentPane.add(myNorthPanel, BorderLayout.NORTH); + // listen to mouse motion events for a11y myContentPane.addMouseMotionListener(new MouseMotionAdapter() { - }); // listen to mouse motion events for a11y + }); createStatusBar(frame); - updateStatusBarVisibility(); - - myContentPane.add(myStatusBar, BorderLayout.SOUTH); - - IdeMenuBar menu = new IdeMenuBar(actionManager, dataManager); + IdeMenuBar menu = new IdeMenuBar(ActionManagerEx.getInstanceEx(), DataManager.getInstance()); myDecoratedMenu = IdeFrameDecorator.isCustomDecoration() && frame instanceof IdeFrameEx; if (!isDecoratedMenu() && !WindowManagerImpl.isFloatingMenuBarSupported()) { @@ -240,9 +237,10 @@ public class IdeRootPane extends JRootPane implements UISettingsListener, Dispos menuBar.repaint(); } - private JComponent createToolbar() { + private static JComponent createToolbar() { ActionGroup group = (ActionGroup)CustomActionsSchema.getInstance().getCorrectedAction(IdeActions.GROUP_MAIN_TOOLBAR); - final ActionToolbar toolBar = myActionManager.createActionToolbar( + ActionManagerEx actionManager = ActionManagerEx.getInstanceEx(); + final ActionToolbar toolBar = actionManager.createActionToolbar( ActionPlaces.MAIN_TOOLBAR, group, true @@ -252,59 +250,36 @@ public class IdeRootPane extends JRootPane implements UISettingsListener, Dispos DefaultActionGroup menuGroup = new DefaultActionGroup(); menuGroup.add(new ViewToolbarAction()); menuGroup.add(new CustomizeUIAction()); - PopupHandler.installUnknownPopupHandler(toolBar.getComponent(), menuGroup, myActionManager); + PopupHandler.installUnknownPopupHandler(toolBar.getComponent(), menuGroup, actionManager); return toolBar.getComponent(); } - private void createStatusBar(IdeFrame frame) { + private void createStatusBar(@NotNull IdeFrame frame) { myStatusBar = new IdeStatusBarImpl(); Disposer.register(this, myStatusBar); myStatusBar.install(frame); - myMemoryWidget = new MemoryUsagePanel(); - - for (final StatusBarCustomComponentFactory componentFactory : StatusBarCustomComponentFactory.EP_NAME.getExtensions()) { - final JComponent c = componentFactory.createComponent(myStatusBar); - myStatusBar.addWidget(new CustomStatusBarWidget() { - @Override - public JComponent getComponent() { - return c; - } - - @Override - @NotNull - public String ID() { - return c.getClass().getSimpleName(); - } - - @Override - public WidgetPresentation getPresentation(@NotNull PlatformType type) { - return null; - } - - @Override - public void install(@NotNull StatusBar statusBar) { - } - - @Override - public void dispose() { - componentFactory.disposeComponent(myStatusBar, c); - } - }, StatusBar.Anchors.before(MemoryUsagePanel.WIDGET_ID)); - } - - myStatusBar.addWidget(myMemoryWidget); + setMemoryIndicatorVisible(UISettings.getInstance().getShowMemoryIndicator()); myStatusBar.addWidget(new IdeMessagePanel(frame, MessagePool.getInstance()), StatusBar.Anchors.before(MemoryUsagePanel.WIDGET_ID)); - setMemoryIndicatorVisible(UISettings.getInstance().getShowMemoryIndicator()); + updateStatusBarVisibility(); + myContentPane.add(myStatusBar, BorderLayout.SOUTH); } - private void setMemoryIndicatorVisible(final boolean visible) { - if (myMemoryWidget != null) { - myMemoryWidget.setShowing(visible); - myStatusBar.setBorder(BorderFactory.createEmptyBorder(1, 0, 0, visible ? 0 : 6)); + private void setMemoryIndicatorVisible(boolean visible) { + if (myMemoryWidget == null) { + if (!visible) { + myStatusBar.setBorder(BorderFactory.createEmptyBorder(1, 0, 0, 6)); + return; + } + + myMemoryWidget = new MemoryUsagePanel(); + myStatusBar.addWidget(myMemoryWidget); } + + myMemoryWidget.setShowing(visible); + myStatusBar.setBorder(BorderFactory.createEmptyBorder(1, 0, 0, visible ? 0 : 6)); } @Nullable diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ProjectFrameBounds.kt b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ProjectFrameBounds.kt index eb76afccbfc3..941a7e5ade19 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ProjectFrameBounds.kt +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ProjectFrameBounds.kt @@ -14,7 +14,10 @@ import org.jdom.Element import java.awt.Frame import java.awt.Rectangle -@State(name = "ProjectFrameBounds", storages = [(Storage(StoragePathMacros.WORKSPACE_FILE))]) +@State(name = "ProjectFrameBounds", storages = [ + Storage(StoragePathMacros.PRODUCT_WORKSPACE_FILE), + Storage(StoragePathMacros.WORKSPACE_FILE, deprecated = true) +]) class ProjectFrameBounds(private val project: Project) : PersistentStateComponent, ModificationTracker { companion object { @JvmStatic diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/SystemDock.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/SystemDock.java index fa91bf1c1982..6689a780f63d 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/SystemDock.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/SystemDock.java @@ -12,7 +12,7 @@ import com.intellij.ui.win.WinDockDelegate; /** * @author Denis Fokin */ -public class SystemDock { +public final class SystemDock { private static final Delegate ourDelegate; static { diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/TestWindowManager.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/TestWindowManager.java index 3c7663799957..beec2dc2235c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/TestWindowManager.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/TestWindowManager.java @@ -1,9 +1,7 @@ // 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. package com.intellij.openapi.wm.impl; -import com.intellij.ide.DataManager; import com.intellij.openapi.Disposable; -import com.intellij.openapi.actionSystem.ex.ActionManagerEx; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.TaskInfo; import com.intellij.openapi.project.Project; @@ -95,7 +93,7 @@ public final class TestWindowManager extends WindowManagerEx { @Override public final IdeFrameImpl allocateFrame(@NotNull Project project) { - return new IdeFrameImpl(ActionManagerEx.getInstanceEx(), DataManager.getInstance()); + return new IdeFrameImpl(); } @Override @@ -201,6 +199,12 @@ public final class TestWindowManager extends WindowManagerEx { private static final class DummyStatusBar implements StatusBarEx { private final Map myWidgetMap = new HashMap<>(); + @Nullable + @Override + public Project getProject() { + return null; + } + @Override public Dimension getSize() { return new Dimension(0, 0); diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/WindowManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/WindowManagerImpl.java index 98195702fe63..16b55bdd2453 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/WindowManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/WindowManagerImpl.java @@ -4,7 +4,6 @@ package com.intellij.openapi.wm.impl; import com.intellij.ide.DataManager; import com.intellij.ide.RecentProjectsManagerBase; import com.intellij.ide.impl.DataManagerImpl; -import com.intellij.openapi.actionSystem.ex.ActionManagerEx; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.PersistentStateComponent; @@ -81,17 +80,14 @@ public final class WindowManagerImpl extends WindowManagerEx implements Persiste final FrameInfo myDefaultFrameInfo = new FrameInfo(); private final WindowAdapter myActivationListener; - private final DataManager myDataManager; - private final ActionManagerEx myActionManager; /** * invoked by reflection */ - public WindowManagerImpl(DataManager dataManager, ActionManagerEx actionManager) { - myDataManager = dataManager; - myActionManager = actionManager; - if (myDataManager instanceof DataManagerImpl) { - ((DataManagerImpl)myDataManager).setWindowManager(this); + public WindowManagerImpl() { + DataManager dataManager = DataManager.getInstance(); + if (dataManager instanceof DataManagerImpl) { + ((DataManagerImpl)dataManager).setWindowManager(this); } final Application application = ApplicationManager.getApplication(); @@ -434,7 +430,7 @@ public final class WindowManagerImpl extends WindowManagerEx implements Persiste // this method is called when there is some opened project (IDE will not open Welcome Frame, but project) public IdeFrame showFrame(@Nullable Runnable beforeSetVisible) { - final IdeFrameImpl frame = new IdeFrameImpl(myActionManager, myDataManager); + final IdeFrameImpl frame = new IdeFrameImpl(); myProjectToFrame.put(null, frame); Rectangle frameBounds = validateFrameBounds(myDefaultFrameInfo.getBounds()); @@ -471,7 +467,7 @@ public final class WindowManagerImpl extends WindowManagerEx implements Persiste IdeFrameImpl frame = myProjectToFrame.remove(null); if (frame == null) { - frame = new IdeFrameImpl(myActionManager, myDataManager); + frame = new IdeFrameImpl(); } final FrameInfo frameInfo = ProjectFrameBounds.getInstance(project).getRawFrameInfo(); diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/IdeStatusBarImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/IdeStatusBarImpl.java index 52dc91767fd0..2fa65dc1e9c3 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/IdeStatusBarImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/IdeStatusBarImpl.java @@ -6,6 +6,7 @@ import com.intellij.notification.impl.IdeNotificationArea; import com.intellij.openapi.Disposable; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.TaskInfo; +import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.ui.popup.BalloonHandler; import com.intellij.openapi.ui.popup.ListPopup; @@ -522,6 +523,9 @@ public class IdeStatusBarImpl extends JComponent implements Accessible, StatusBa @Override public boolean dispatch(@NotNull AWTEvent e) { if (e instanceof MouseEvent) { + if (myRightPanel == null) { + return false; + } Component component = ((MouseEvent)e).getComponent(); if (component == null) { return false; @@ -733,6 +737,12 @@ public class IdeStatusBarImpl extends JComponent implements Accessible, StatusBa return myFrame; } + @Nullable + @Override + public Project getProject() { + return myFrame == null ? null : myFrame.getProject(); + } + @Override public AccessibleContext getAccessibleContext() { if (accessibleContext == null) { diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/MemoryUsagePanel.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/MemoryUsagePanel.java index 370da7ba7c20..84e7ae52cdfc 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/MemoryUsagePanel.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/MemoryUsagePanel.java @@ -27,7 +27,7 @@ import java.util.concurrent.TimeUnit; import static com.intellij.openapi.util.io.FileUtilRt.MEGABYTE; -public class MemoryUsagePanel extends JButton implements CustomStatusBarWidget, UISettingsListener, Activatable { +public final class MemoryUsagePanel extends JButton implements CustomStatusBarWidget, UISettingsListener, Activatable { public static final String WIDGET_ID = "Memory"; private static final int INDENT = 6; diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/ToolWindowsWidget.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/ToolWindowsWidget.java index 1c03164f3c3b..4fba98f1a98c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/ToolWindowsWidget.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/ToolWindowsWidget.java @@ -113,7 +113,7 @@ class ToolWindowsWidget extends JLabel implements CustomStatusBarWidget, StatusB } if (myAlarm.getActiveRequestCount() == 0) { myAlarm.addRequest(() -> { - final IdeFrameImpl frame = ComponentUtil.getParentOfType((Class)IdeFrameImpl.class, (Component)this); + final IdeFrameImpl frame = ComponentUtil.getParentOfType(IdeFrameImpl.class, this); if (frame == null) return; List toolWindows = new ArrayList<>(); @@ -229,8 +229,7 @@ class ToolWindowsWidget extends JLabel implements CustomStatusBarWidget, StatusB } private boolean isActive() { - return myStatusBar != null && myStatusBar.getFrame() != null && myStatusBar.getFrame().getProject() != null && Registry - .is("ide.windowSystem.showTooWindowButtonsSwitcher"); + return myStatusBar != null && myStatusBar.getProject() != null && Registry.is("ide.windowSystem.showTooWindowButtonsSwitcher"); } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/WelcomeFrame.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/WelcomeFrame.java index de3b7bddd2f4..b904a214ff78 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/WelcomeFrame.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/WelcomeFrame.java @@ -18,6 +18,7 @@ import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.project.ProjectManagerListener; import com.intellij.openapi.util.DimensionService; import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.SystemInfoRt; import com.intellij.openapi.wm.*; import com.intellij.openapi.wm.impl.IdeFrameImpl; import com.intellij.openapi.wm.impl.IdeGlassPaneImpl; @@ -174,7 +175,9 @@ public class WelcomeFrame extends JFrame implements IdeFrame, AccessibleContextA ((JFrame)frame).setVisible(true); IdeMenuBar.installAppMenuIfNeeded((JFrame)frame); ourInstance = frame; - ourTouchbar = TouchBarsManager.showDialogWrapperButtons(frame.getComponent()); + if (SystemInfoRt.isMac) { + ourTouchbar = TouchBarsManager.showDialogWrapperButtons(frame.getComponent()); + } } public static void showIfNoProjectOpened() { diff --git a/platform/platform-impl/src/com/intellij/platform/PlatformProjectOpenProcessor.java b/platform/platform-impl/src/com/intellij/platform/PlatformProjectOpenProcessor.java index 502d0974f234..53ef4c8ad18d 100644 --- a/platform/platform-impl/src/com/intellij/platform/PlatformProjectOpenProcessor.java +++ b/platform/platform-impl/src/com/intellij/platform/PlatformProjectOpenProcessor.java @@ -198,7 +198,9 @@ public class PlatformProjectOpenProcessor extends ProjectOpenProcessor implement () -> refResult.set(prepareAndOpenProject(virtualFile, options, finalBaseDir, finalDummyProject, finalDummyProjectName)), "Loading Project...", true, null, frame.getComponent() ); - if (progressCompleted) result = refResult.get(); + if (progressCompleted) { + result = refResult.get(); + } } if (result == null || result.first == null) { diff --git a/platform/platform-impl/src/com/intellij/ui/AppUIUtil.java b/platform/platform-impl/src/com/intellij/ui/AppUIUtil.java index 8830b0e2cb1e..fbc142b7478d 100644 --- a/platform/platform-impl/src/com/intellij/ui/AppUIUtil.java +++ b/platform/platform-impl/src/com/intellij/ui/AppUIUtil.java @@ -51,7 +51,6 @@ import javax.swing.text.html.StyleSheet; import java.awt.*; import java.awt.event.ActionEvent; import java.io.File; -import java.io.IOException; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.net.URL; @@ -68,7 +67,7 @@ import static javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED; public final class AppUIUtil { private static final String VENDOR_PREFIX = "jetbrains-"; private static List ourIcons = null; - private static boolean ourMacDocIconSet = false; + private static volatile boolean ourMacDocIconSet = false; @NotNull private static Logger getLogger() { @@ -112,7 +111,7 @@ public final class AppUIUtil { if (!SystemInfoRt.isMac) { window.setIconImages(images); } - else if (PluginManagerCore.isRunningFromSources() && !ourMacDocIconSet) { + else if (!ourMacDocIconSet && PluginManagerCore.isRunningFromSources()) { MacAppIcon.setDockIcon(ImageUtil.toBufferedImage(images.get(0))); ourMacDocIconSet = true; } @@ -121,7 +120,7 @@ public final class AppUIUtil { public static boolean isWindowIconAlreadyExternallySet() { if (SystemInfoRt.isMac) { - return !PluginManagerCore.isRunningFromSources(); + return ourMacDocIconSet || !PluginManagerCore.isRunningFromSources(); } // todo[tav] 'jbre.win.app.icon.supported' is defined by JBRE, remove when OpenJDK supports it as well diff --git a/platform/platform-impl/src/com/intellij/ui/ColorPicker.java b/platform/platform-impl/src/com/intellij/ui/ColorPicker.java index e25bb59ba59d..43fcd8d84610 100644 --- a/platform/platform-impl/src/com/intellij/ui/ColorPicker.java +++ b/platform/platform-impl/src/com/intellij/ui/ColorPicker.java @@ -355,21 +355,19 @@ public class ColorPicker extends JPanel implements ColorListener, DocumentListen } public static void showColorPickerPopup(@Nullable Color currentColor, @NotNull ColorListener listener) { - LightCalloutPopup dialog = new LightCalloutPopup(); - - JPanel panel = new ColorPickerBuilder() + LightCalloutPopup popup = new ColorPickerBuilder() .setOriginalColor(currentColor) .addSaturationBrightnessComponent() .addColorAdjustPanel(new MaterialGraphicalColorPipetteProvider()) .addColorValuePanel().withFocus() //.addSeparator() //.addCustomComponent(MaterialColorPaletteProvider.INSTANCE) - .addColorListener(listener) + .addColorListener(listener, false) .focusWhenDisplay(true) .setFocusCycleRoot(true) .build(); - dialog.show(panel, null, MouseInfo.getPointerInfo().getLocation()); + popup.show(MouseInfo.getPointerInfo().getLocation()); } private JComponent buildTopPanel(boolean enablePipette) throws ParseException { diff --git a/platform/platform-impl/src/com/intellij/ui/colorpicker/ColorPickerBuilder.kt b/platform/platform-impl/src/com/intellij/ui/colorpicker/ColorPickerBuilder.kt index 6fe119cb1e18..5b606f793e94 100644 --- a/platform/platform-impl/src/com/intellij/ui/colorpicker/ColorPickerBuilder.kt +++ b/platform/platform-impl/src/com/intellij/ui/colorpicker/ColorPickerBuilder.kt @@ -48,7 +48,7 @@ class ColorPickerBuilder { private var focusCycleRoot = false private var focusedComponentIndex = -1 private val actionMap = mutableMapOf() - private val colorListeners = mutableListOf() + private val colorListeners = mutableListOf() fun setOriginalColor(originalColor: Color?) = apply { this.originalColor = originalColor } @@ -112,9 +112,13 @@ class ColorPickerBuilder { fun addKeyAction(keyStroke: KeyStroke, action: Action) = apply { actionMap[keyStroke] = action } - fun addColorListener(colorListener: ColorListener) = apply { colorListeners.add(colorListener) } + fun addColorListener(colorListener: ColorListener) = addColorListener(colorListener, true) - fun build(): JPanel { + fun addColorListener(colorListener: ColorListener, invokeOnEveryColorChange: Boolean) = apply { + colorListeners.add(ColorListenerInfo(colorListener, invokeOnEveryColorChange)) + } + + fun build(): LightCalloutPopup { if (componentsToBuild.isEmpty()) { throw IllegalStateException("The Color Picker should have at least one picking component.") } @@ -158,12 +162,16 @@ class ColorPickerBuilder { panel.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(keyStroke, key) } - colorListeners.forEach { model.addListener(it) } + colorListeners.forEach { model.addListener(it.colorListener, it.invokeOnEveryColorChange) } - return panel + return LightCalloutPopup(panel, + closedCallback = { model.onClose() }, + cancelCallBack = { model.onCancel() }) } } private class MyFocusTraversalPolicy(val defaultComponent: Component?) : LayoutFocusTraversalPolicy() { override fun getDefaultComponent(aContainer: Container?): Component? = defaultComponent } + +private data class ColorListenerInfo(val colorListener: ColorListener, val invokeOnEveryColorChange: Boolean) diff --git a/platform/platform-impl/src/com/intellij/ui/colorpicker/ColorPickerModel.kt b/platform/platform-impl/src/com/intellij/ui/colorpicker/ColorPickerModel.kt index b78ca1f76dd0..bbdf94242dab 100644 --- a/platform/platform-impl/src/com/intellij/ui/colorpicker/ColorPickerModel.kt +++ b/platform/platform-impl/src/com/intellij/ui/colorpicker/ColorPickerModel.kt @@ -15,6 +15,7 @@ */ package com.intellij.ui.colorpicker +import com.intellij.openapi.application.ApplicationManager import com.intellij.ui.picker.ColorListener import java.awt.Color @@ -23,6 +24,7 @@ val DEFAULT_PICKER_COLOR = Color(0xFF, 0xFF, 0xFF, 0xFF) class ColorPickerModel(originalColor: Color = DEFAULT_PICKER_COLOR) { private val listeners = mutableSetOf() + private val instantListeners = mutableSetOf() var color: Color = originalColor private set @@ -31,6 +33,22 @@ class ColorPickerModel(originalColor: Color = DEFAULT_PICKER_COLOR) { color = newColor Color.RGBtoHSB(color.red, color.green, color.blue, hsb) + instantListeners.forEach { it.colorChanged(color, source) } + } + + fun onClose() { + ApplicationManager.getApplication().invokeLater { + listeners.forEach { it.colorChanged(color, this) } + } + } + + fun onCancel() { + //todo[kb] at the moment there is no any good way to close the color picker popup. Cancel outside triggers onCancel + onClose() + } + + fun applyColorToSource(newColor: Color, source: Any? = null) { + setColor(newColor, source) listeners.forEach { it.colorChanged(color, source) } } @@ -52,7 +70,17 @@ class ColorPickerModel(originalColor: Color = DEFAULT_PICKER_COLOR) { val brightness get() = hsb[2] - fun addListener(listener: ColorListener) = listeners.add(listener) + fun addListener(listener: ColorListener) = addListener(listener, true) - fun removeListener(listener: ColorListener) = listeners.remove(listener) + fun addListener(listener: ColorListener, invokeOnEveryColorChange: Boolean) { + listeners.add(listener) + if (invokeOnEveryColorChange) { + instantListeners.add(listener) + } + } + + fun removeListener(listener: ColorListener) { + listeners.remove(listener) + instantListeners.remove(listener) + } } diff --git a/platform/platform-impl/src/com/intellij/ui/colorpicker/LightCalloutPopup.kt b/platform/platform-impl/src/com/intellij/ui/colorpicker/LightCalloutPopup.kt index 5d398979d123..0e70e9e438e8 100644 --- a/platform/platform-impl/src/com/intellij/ui/colorpicker/LightCalloutPopup.kt +++ b/platform/platform-impl/src/com/intellij/ui/colorpicker/LightCalloutPopup.kt @@ -33,7 +33,7 @@ import javax.swing.JComponent * * The popup is automatically dismissed when the user clicks outside. */ -class LightCalloutPopup( +class LightCalloutPopup(val content: JComponent, val closedCallback: (() -> Unit)? = null, val cancelCallBack: (() -> Unit)? = null, val beforeShownCallback: (() -> Unit)? = null @@ -50,8 +50,7 @@ class LightCalloutPopup( */ @JvmOverloads fun show( - content: JComponent, - parentComponent: JComponent?, + parentComponent: JComponent? = null, location: Point, position: Balloon.Position = Balloon.Position.below ) { diff --git a/platform/platform-impl/src/com/intellij/ui/mac/MacOSApplicationProvider.java b/platform/platform-impl/src/com/intellij/ui/mac/MacOSApplicationProvider.java index 19fcce1d202b..09fb6caa008b 100644 --- a/platform/platform-impl/src/com/intellij/ui/mac/MacOSApplicationProvider.java +++ b/platform/platform-impl/src/com/intellij/ui/mac/MacOSApplicationProvider.java @@ -20,7 +20,6 @@ import com.intellij.openapi.util.SystemInfoRt; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.ui.mac.foundation.Foundation; import com.intellij.ui.mac.foundation.ID; -import com.intellij.ui.mac.touchbar.TouchBarsManager; import com.sun.jna.Callback; import org.jetbrains.annotations.NotNull; @@ -80,8 +79,6 @@ public final class MacOSApplicationProvider { if (JnaLoader.isLoaded()) { installAutoUpdateMenu(); } - - TouchBarsManager.onApplicationInitialized(); } private static void installAutoUpdateMenu() { diff --git a/platform/platform-impl/src/com/intellij/ui/mac/touchbar/ProjectData.java b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/ProjectData.java index 06d1803a471f..0cfc78b4255d 100644 --- a/platform/platform-impl/src/com/intellij/ui/mac/touchbar/ProjectData.java +++ b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/ProjectData.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-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. package com.intellij.ui.mac.touchbar; import com.intellij.execution.ExecutionListener; @@ -188,24 +188,19 @@ class ProjectData { return null; } - EditorData registerEditor(@NotNull Editor editor) { - ApplicationManager.getApplication().assertIsDispatchThread(); - - final EditorData result = new EditorData(editor); - myEditors.put(editor, result); - return result; + void registerEditor(@NotNull Editor editor) { + myEditors.put(editor, new EditorData(editor)); } EditorData getEditorData(@NotNull Editor editor) { - ApplicationManager.getApplication().assertIsDispatchThread(); return myEditors.get(editor); } void removeEditor(@NotNull Editor editor) { - ApplicationManager.getApplication().assertIsDispatchThread(); - - if (myEditors.isEmpty()) // already cleared + // already cleared + if (myEditors.isEmpty()) { return; + } final EditorData removed = myEditors.remove(editor); if (removed == null) { @@ -216,7 +211,7 @@ class ProjectData { removed.release(); } - private void _fillBarContainer(@NotNull BarContainer container) { + private static void _fillBarContainer(@NotNull BarContainer container) { ApplicationManager.getApplication().assertIsDispatchThread(); final @NotNull BarType type = container.getType(); diff --git a/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TouchBarsManager.java b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TouchBarsManager.java index cfe9d91bf7d5..da2bae1d67fb 100644 --- a/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TouchBarsManager.java +++ b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TouchBarsManager.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-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. package com.intellij.ui.mac.touchbar; import com.intellij.execution.ExecutionListener; @@ -15,6 +15,9 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.EditorFactory; +import com.intellij.openapi.editor.event.EditorFactoryEvent; +import com.intellij.openapi.editor.event.EditorFactoryListener; import com.intellij.openapi.editor.ex.EditorEx; import com.intellij.openapi.editor.ex.FocusChangeListener; import com.intellij.openapi.project.Project; @@ -23,6 +26,7 @@ import com.intellij.openapi.project.ProjectManagerListener; import com.intellij.openapi.startup.StartupManager; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.SystemInfoRt; import com.intellij.openapi.wm.ToolWindowId; import com.intellij.ui.mac.TouchbarDataKeys; import com.intellij.ui.popup.list.ListPopupImpl; @@ -40,59 +44,48 @@ import java.awt.event.MouseWheelEvent; import java.util.List; import java.util.*; -public class TouchBarsManager { +public final class TouchBarsManager { private static final Logger LOG = Logger.getInstance(TouchBarsManager.class); private static final StackTouchBars ourStack = new StackTouchBars(); private static final Map ourProjectData = new HashMap<>(); // NOTE: probably it is better to use api of UserDataHolder private static final Map ourTemporaryBars = new HashMap<>(); + private static volatile boolean isInitialized; + public static void onApplicationInitialized() { - ApplicationManager.getApplication().executeOnPooledThread(TouchBarsManager::_onApplicationInitialized); - } - private static void _onApplicationInitialized() { - if (!isTouchBarAvailable()) + if (!isTouchBarAvailable()) { return; + } + + LOG.assertTrue(!isInitialized); + + for (Project project : ProjectManager.getInstance().getOpenProjects()) { + registerProject(project); + } + + for (Editor editor : EditorFactory.getInstance().getAllEditors()) { + registerEditor(editor); + } + + isInitialized = true; + + EditorFactory.getInstance().addEditorFactoryListener(new EditorFactoryListener() { + @Override + public void editorCreated(@NotNull EditorFactoryEvent event) { + registerEditor(event.getEditor()); + } + + @Override + public void editorReleased(@NotNull EditorFactoryEvent event) { + releaseEditor(event.getEditor()); + } + }, ApplicationManager.getApplication()); ApplicationManager.getApplication().getMessageBus().connect().subscribe(ProjectManager.TOPIC, new ProjectManagerListener() { @Override public void projectOpened(@NotNull Project project) { - ApplicationManager.getApplication().assertIsDispatchThread(); - // System.out.println("opened project " + project + ", set default touchbar"); - - final ProjectData pd = new ProjectData(project); - synchronized (ourProjectData) { - final ProjectData prev = ourProjectData.put(project, pd); - if (prev != null) { - LOG.error("previous project data wasn't removed: " + project); - prev.releaseAll(); - } - } - - StartupManager.getInstance(project).registerPostStartupActivity(() -> pd.get(BarType.DEFAULT).show()); - - project.getMessageBus().connect().subscribe(ExecutionManager.EXECUTION_TOPIC, new ExecutionListener() { - @Override - public void processStarted(@NotNull String executorId, @NotNull ExecutionEnvironment env, @NotNull ProcessHandler handler) { - ApplicationManager.getApplication().invokeLater(TouchBarsManager::_updateCurrentTouchbar); - } - @Override - public void processTerminated(@NotNull String executorId, @NotNull ExecutionEnvironment env, @NotNull ProcessHandler handler, int exitCode) { - // TODO: probably, need to remove debugger-panel from stack completely - final String twid = env.getExecutor().getToolWindowId(); - ourStack.pop(topContainer -> { - if (topContainer.getType() != BarType.DEBUGGER) - return false; - - if (!ToolWindowId.DEBUG.equals(twid) && !ToolWindowId.RUN_DASHBOARD.equals(twid) && !ToolWindowId.SERVICES.equals(twid)) - return false; - - // System.out.println("processTerminated, dbgSessionsCount=" + pd.getDbgSessions()); - return !_hasAnyActiveSession(project, handler) || pd.getDbgSessions() <= 0; - }); - ApplicationManager.getApplication().invokeLater(TouchBarsManager::_updateCurrentTouchbar); - } - }); + registerProject(project); } @Override @@ -117,11 +110,59 @@ public class TouchBarsManager { _initExecutorsGroup(); } - public static boolean isTouchBarAvailable() { return NST.isAvailable(); } + private static void registerProject(@NotNull Project project) { + if (project.isDisposed()) { + return; + } + + // System.out.println("opened project " + project + ", set default touchbar"); + + final ProjectData projectData = new ProjectData(project); + synchronized (ourProjectData) { + final ProjectData prev = ourProjectData.put(project, projectData); + if (prev != null) { + LOG.error("previous project data wasn't removed: " + project); + prev.releaseAll(); + } + } + + StartupManager.getInstance(project).registerPostStartupActivity(() -> projectData.get(BarType.DEFAULT).show()); + + project.getMessageBus().connect().subscribe(ExecutionManager.EXECUTION_TOPIC, new ExecutionListener() { + @Override + public void processStarted(@NotNull String executorId, @NotNull ExecutionEnvironment env, @NotNull ProcessHandler handler) { + ApplicationManager.getApplication().invokeLater(TouchBarsManager::_updateCurrentTouchbar); + } + + @Override + public void processTerminated(@NotNull String executorId, @NotNull ExecutionEnvironment env, @NotNull ProcessHandler handler, int exitCode) { + // TODO: probably, need to remove debugger-panel from stack completely + final String twid = env.getExecutor().getToolWindowId(); + ourStack.pop(topContainer -> { + if (topContainer.getType() != BarType.DEBUGGER) { + return false; + } + + if (!ToolWindowId.DEBUG.equals(twid) && !ToolWindowId.RUN_DASHBOARD.equals(twid) && !ToolWindowId.SERVICES.equals(twid)) { + return false; + } + + // System.out.println("processTerminated, dbgSessionsCount=" + pd.getDbgSessions()); + return !_hasAnyActiveSession(project, handler) || projectData.getDbgSessions() <= 0; + }); + ApplicationManager.getApplication().invokeLater(TouchBarsManager::_updateCurrentTouchbar); + } + }); + } + + public static boolean isTouchBarAvailable() { + return SystemInfoRt.isMac && NST.isAvailable(); + } public static void reloadAll() { - if (!isTouchBarAvailable()) + if (!isInitialized || !isTouchBarAvailable()) { return; + } synchronized (ourProjectData) { ourProjectData.forEach((p, pd)->pd.reloadAll()); @@ -130,13 +171,15 @@ public class TouchBarsManager { } public static void onInputEvent(InputEvent e) { - if (!isTouchBarAvailable()) + if (!isInitialized || !isTouchBarAvailable()) { return; + } // NOTE: skip wheel-events, because scrolling by touchpad produces mouse-wheel events with pressed modifier, example: // MouseWheelEvent[MOUSE_WHEEL,(890,571),absolute(0,0),button=0,modifiers=SHIFT,extModifiers=SHIFT,clickCount=0,scrollType=WHEEL_UNIT_SCROLL,scrollAmount=1,wheelRotation=0,preciseWheelRotation=0.1] on frame0 - if (e instanceof MouseWheelEvent) + if (e instanceof MouseWheelEvent) { return; + } ourStack.updateKeyMask(e.getModifiersEx() & ProjectData.getUsedKeyMask()); } @@ -193,7 +236,8 @@ public class TouchBarsManager { } } } - } else if (e.getID() == FocusEvent.FOCUS_LOST) { + } + else if (e.getID() == FocusEvent.FOCUS_LOST) { final BarContainer nonModalDialogParent = _findByParentComponent(src, ourTemporaryBars.values(), bc -> bc.isNonModalDialog()); if (nonModalDialogParent != null) { // System.out.println("non-modal dialog window '" + nonModalDialogParent.getParentComponent() + "' lost focus: " + e); @@ -219,103 +263,102 @@ public class TouchBarsManager { } } - public static void registerEditor(@NotNull Editor editor) { - if (!isTouchBarAvailable()) + private static void registerEditor(@NotNull Editor editor) { + final Project project = editor.getProject(); + if (project == null || project.isDisposed()) { return; + } - final Project proj = editor.getProject(); - if (proj == null || proj.isDisposed()) - return; - - final ProjectData pd; + ProjectData projectData; synchronized (ourProjectData) { - pd = ourProjectData.get(proj); - if (pd == null) { + projectData = ourProjectData.get(project); + if (projectData == null) { // System.out.println("can't find project data to register editor: " + editor + ", project: " + proj); return; } - pd.registerEditor(editor); + projectData.registerEditor(editor); } - if (editor instanceof EditorEx) + if (editor instanceof EditorEx) { ((EditorEx)editor).addFocusListener(new FocusChangeListener() { - @Override - public void focusGained(@NotNull Editor editor) { - // System.out.println("reset optional-context of default because editor window gained focus: " + editor); - pd.get(BarType.DEFAULT).setOptionalContextVisible(null); + @Override + public void focusGained(@NotNull Editor editor) { + // System.out.println("reset optional-context of default because editor window gained focus: " + editor); + projectData.get(BarType.DEFAULT).setOptionalContextVisible(null); - final boolean hasDebugSession = pd.getDbgSessions() > 0; - if (!hasDebugSession) { - // System.out.println("elevate default because editor window gained focus: " + editor); - // StackTouchBars.changeReason = "elevate default because editor gained focus"; - ourStack.elevateContainer(pd.get(BarType.DEFAULT)); + final boolean hasDebugSession = projectData.getDbgSessions() > 0; + if (!hasDebugSession) { + // System.out.println("elevate default because editor window gained focus: " + editor); + // StackTouchBars.changeReason = "elevate default because editor gained focus"; + ourStack.elevateContainer(projectData.get(BarType.DEFAULT)); + } } - } - @Override - public void focusLost(@NotNull Editor editor) {} - }); + }); + } } - public static void releaseEditor(@NotNull Editor editor) { - if (!isTouchBarAvailable()) + private static void releaseEditor(@NotNull Editor editor) { + final Project project = editor.getProject(); + if (project == null) { return; + } - final Project proj = editor.getProject(); - if (proj == null) - return; synchronized (ourProjectData) { - final ProjectData pd = ourProjectData.get(proj); - if (pd == null) + final ProjectData pd = ourProjectData.get(project); + if (pd == null) { return; + } pd.removeEditor(editor); } } public static void onUpdateEditorHeader(@NotNull Editor editor, JComponent header) { - if (!isTouchBarAvailable()) + if (!isInitialized || !isTouchBarAvailable()) { return; + } - final Project proj = editor.getProject(); - if (proj == null) + Project project = editor.getProject(); + if (project == null) { return; + } synchronized (ourProjectData) { - final ProjectData pd = ourProjectData.get(proj); - if (pd == null) { - LOG.error("can't find project data to update header of editor: " + editor + ", project: " + proj); + ProjectData projectData = ourProjectData.get(project); + if (projectData == null) { + LOG.error("can't find project data to update header of editor: " + editor + ", project: " + project); return; } - final ProjectData.EditorData ed = pd.getEditorData(editor); - if (ed == null) { - LOG.error("can't find editor-data to update header of editor: " + editor + ", project: " + proj); + ProjectData.EditorData editorData = projectData.getEditorData(editor); + if (editorData == null) { + LOG.error("can't find editor-data to update header of editor: " + editor + ", project: " + project); return; } // System.out.printf("onUpdateEditorHeader: editor='%s', header='%s'\n", editor, header); - final ActionGroup actions = header instanceof DataProvider ? TouchbarDataKeys.ACTIONS_KEY.getData((DataProvider)header) : null; if (header == null) { // System.out.println("set null header"); - ed.editorHeader = null; - if (ed.containerSearch != null) - ourStack.removeContainer(ed.containerSearch); - } else { + editorData.editorHeader = null; + if (editorData.containerSearch != null) + ourStack.removeContainer(editorData.containerSearch); + } + else { // System.out.println("set header: " + header); // System.out.println("\t\tparent: " + header.getParent()); - ed.editorHeader = header; + editorData.editorHeader = header; - if (ed.containerSearch == null || ed.actionsSearch != actions) { - if (ed.containerSearch != null) { - ourStack.removeContainer(ed.containerSearch); - ed.containerSearch.release(); + if (editorData.containerSearch == null || editorData.actionsSearch != actions) { + if (editorData.containerSearch != null) { + ourStack.removeContainer(editorData.containerSearch); + editorData.containerSearch.release(); } if (actions != null) { - ed.containerSearch = new BarContainer(BarType.EDITOR_SEARCH, TouchBar.buildFromGroup("editor_search_" + header, actions, true, true), null, header); - ourStack.showContainer(ed.containerSearch); + editorData.containerSearch = new BarContainer(BarType.EDITOR_SEARCH, TouchBar.buildFromGroup("editor_search_" + header, actions, true, true), null, header); + ourStack.showContainer(editorData.containerSearch); } } } @@ -343,13 +386,14 @@ public class TouchBarsManager { } public static @Nullable Disposable showDialogWrapperButtons(@NotNull Container contentPane) { - if (!isTouchBarAvailable()) + if (!isTouchBarAvailable()) { return null; + } final ModalityState ms = Utils.getCurrentModalityState(); final BarType btype = ModalityState.NON_MODAL.equals(ms) ? BarType.DIALOG : BarType.MODAL_DIALOG; - BarContainer bc = null; - TouchBar tb = null; + BarContainer bc; + TouchBar tb; final Map jbuttons = new HashMap<>(); final Map actions = new HashMap<>(); diff --git a/platform/platform-impl/src/com/intellij/ui/messages/SheetMessage.java b/platform/platform-impl/src/com/intellij/ui/messages/SheetMessage.java index d318800cfd0b..afd1db340067 100755 --- a/platform/platform-impl/src/com/intellij/ui/messages/SheetMessage.java +++ b/platform/platform-impl/src/com/intellij/ui/messages/SheetMessage.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-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. package com.intellij.ui.messages; import com.apple.eawt.FullScreenUtilities; @@ -19,8 +19,6 @@ import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.lang.ref.WeakReference; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import static com.intellij.openapi.wm.IdeFocusManager.getGlobalInstance; @@ -163,12 +161,14 @@ class SheetMessage implements Disposable { } private void _showTouchBar() { - if (!TouchBarsManager.isTouchBarAvailable()) + if (!TouchBarsManager.isTouchBarAvailable()) { return; + } final Disposable tb = TouchBarsManager.showDialogWrapperButtons(myController.getSheetPanel()); - if (tb != null) + if (tb != null) { Disposer.register(this, tb); + } } private static void maximizeIfNeeded(final Window owner) { diff --git a/platform/platform-resources/src/META-INF/LangExtensions.xml b/platform/platform-resources/src/META-INF/LangExtensions.xml index 1c5c54cdec94..107fc2a17f72 100644 --- a/platform/platform-resources/src/META-INF/LangExtensions.xml +++ b/platform/platform-resources/src/META-INF/LangExtensions.xml @@ -358,7 +358,7 @@ - + diff --git a/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml b/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml index d0de0c36ae1c..4f2e6361d99c 100644 --- a/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml +++ b/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml @@ -197,7 +197,6 @@ - diff --git a/platform/platform-resources/src/META-INF/PlatformExtensions.xml b/platform/platform-resources/src/META-INF/PlatformExtensions.xml index c9b08f8f4bf2..3c298e734953 100644 --- a/platform/platform-resources/src/META-INF/PlatformExtensions.xml +++ b/platform/platform-resources/src/META-INF/PlatformExtensions.xml @@ -220,18 +220,11 @@ serviceImplementation="com.intellij.openapi.roots.ui.FileAppearanceServiceImpl"/> - - - - - - - diff --git a/platform/platform-resources/src/componentSets/PlatformLangComponents.xml b/platform/platform-resources/src/componentSets/PlatformLangComponents.xml index effcd5f85ee7..cf067407af3c 100644 --- a/platform/platform-resources/src/componentSets/PlatformLangComponents.xml +++ b/platform/platform-resources/src/componentSets/PlatformLangComponents.xml @@ -37,5 +37,7 @@ + + diff --git a/platform/platform-resources/src/componentSets/UICore.xml b/platform/platform-resources/src/componentSets/UICore.xml index 07589e799285..d1a193e183ac 100644 --- a/platform/platform-resources/src/componentSets/UICore.xml +++ b/platform/platform-resources/src/componentSets/UICore.xml @@ -30,9 +30,6 @@ com.intellij.configurationStore.SaveAndSyncHandlerImpl com.intellij.configurationStore.HeadlessSaveAndSyncHandler - - com.intellij.ide.ScreenReaderSupportHandler - com.intellij.ide.FrameStateManager com.intellij.ide.FrameStateManagerImpl diff --git a/platform/platform-tests/testSrc/com/intellij/internal/statistics/ApprovedGroupsCacheTest.kt b/platform/platform-tests/testSrc/com/intellij/internal/statistics/ApprovedGroupsCacheTest.kt deleted file mode 100644 index a7575357ccde..000000000000 --- a/platform/platform-tests/testSrc/com/intellij/internal/statistics/ApprovedGroupsCacheTest.kt +++ /dev/null @@ -1,140 +0,0 @@ -// 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. -package com.intellij.internal.statistics - -import com.intellij.internal.statistic.persistence.ApprovedGroupsCacheConfigurable -import com.intellij.internal.statistic.service.fus.FUSWhitelist -import com.intellij.openapi.util.BuildNumber -import com.intellij.testFramework.UsefulTestCase -import com.intellij.testFramework.fixtures.CodeInsightTestFixture -import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory -import junit.framework.TestCase -import org.junit.Test -import java.util.* - -class ApprovedGroupsCacheTest : UsefulTestCase() { - - private var myFixture: CodeInsightTestFixture? = null - private val build: BuildNumber = BuildNumber.fromString("183") - - override fun setUp() { - super.setUp() - - val factory = IdeaTestFixtureFactory.getFixtureFactory() - val fixtureBuilder = factory.createFixtureBuilder("ApprovedGroupsCacheTest") - myFixture = IdeaTestFixtureFactory.getFixtureFactory().createCodeInsightFixture(fixtureBuilder.fixture) - myFixture?.setUp() - } - - override fun tearDown() { - super.tearDown() - try { - myFixture?.tearDown() - } - catch (e: Throwable) { - addSuppressedException(e) - } - finally { - myFixture = null - } - } - - @Test - fun testCacheValues() { - val cache = ApprovedGroupsCacheConfigurable.getInstance() - val date = Date() - val whitelist = WhitelistBuilder().add("firstGroup").add("secondGroup").build() - cache.cacheGroups(date, whitelist, build) - assertEquals(whitelist, cache.getCachedGroups(date, 100)) - } - - @Test - fun testCacheValuesWithVersion() { - val cache = ApprovedGroupsCacheConfigurable.getInstance() - val date = Date() - val whitelist = WhitelistBuilder(). - add("firstGroup", FUSWhitelist.VersionRange.create("2", "5")). - add("secondGroup", FUSWhitelist.VersionRange.create("1", "2"), FUSWhitelist.VersionRange.create("3", null)).build() - cache.cacheGroups(date, whitelist, build) - assertEquals(whitelist, cache.getCachedGroups(date, 100)) - } - - @Test - fun testCacheUpdatesValues() { - val cache = ApprovedGroupsCacheConfigurable.getInstance() - val date = Date() - cache.cacheGroups(date, WhitelistBuilder().add("firstGroup").add("secondGroup").build(), build) - - val latestWhitelist = WhitelistBuilder().add("thirdGroup").build() - cache.cacheGroups(Date(date.time + 1), latestWhitelist, build) - assertEquals(latestWhitelist, cache.getCachedGroups(date, 100)) - } - - @Test - fun testCacheUpdatesValuesWithVersion() { - val cache = ApprovedGroupsCacheConfigurable.getInstance() - val date = Date() - cache.cacheGroups(date, WhitelistBuilder(). - add("firstGroup", FUSWhitelist.VersionRange.create("2", null)). - add("secondGroup", FUSWhitelist.VersionRange.create(null, "5")).build(), build) - - val latestWhitelist = WhitelistBuilder().add("thirdGroup", FUSWhitelist.VersionRange.create("5", null)).build() - cache.cacheGroups(Date(date.time + 1), latestWhitelist, build) - assertEquals(latestWhitelist, cache.getCachedGroups(date, 100)) - } - - @Test - fun testDoestReturnStaleValues() { - val cache = ApprovedGroupsCacheConfigurable.getInstance() - val date = Date() - cache.cacheGroups(date, WhitelistBuilder().add("firstGroup").add("secondGroup").build(), build) - TestCase.assertNull(cache.getCachedGroups(Date(date.time + 101), 100)) - } - - @Test - fun testDoestReturnStaleValuesWithVersion() { - val cache = ApprovedGroupsCacheConfigurable.getInstance() - val date = Date() - val whitelist = WhitelistBuilder(). - add("firstGroup", FUSWhitelist.VersionRange.create(null, null)). - add("secondGroup").build() - cache.cacheGroups(date, whitelist, build) - TestCase.assertNull(cache.getCachedGroups(Date(date.time + 101), 100)) - } - - @Test - fun testCacheValuesByBuild() { - val cache = ApprovedGroupsCacheConfigurable.getInstance() - val date = Date() - val whitelist = WhitelistBuilder().add("firstGroup").add("secondGroup").build() - cache.cacheGroups(date, whitelist, build) - assertEquals(whitelist, cache.getCachedGroups(date, 100, build)) - } - - @Test - fun testCacheValuesByBuildWithVersion() { - val cache = ApprovedGroupsCacheConfigurable.getInstance() - val date = Date() - val whitelist = WhitelistBuilder(). - add("firstGroup", FUSWhitelist.VersionRange.create("5", "10"), FUSWhitelist.VersionRange.create("11", "13")). - add("secondGroup", FUSWhitelist.VersionRange.create(null, null)).build() - cache.cacheGroups(date, whitelist, build) - assertEquals(whitelist, cache.getCachedGroups(date, 100, build)) - } - - @Test - fun testDoestReturnValuesOutdatedByBuild() { - val cache = ApprovedGroupsCacheConfigurable.getInstance() - val date = Date() - cache.cacheGroups(date, WhitelistBuilder().add("firstGroup").add("secondGroup").build(), build) - TestCase.assertNull(cache.getCachedGroups(date, 100, BuildNumber.fromString("191"))) - } - - @Test - fun testDoestReturnValuesOutdatedByBuildWithVersion() { - val cache = ApprovedGroupsCacheConfigurable.getInstance() - val date = Date() - val whitelist = WhitelistBuilder().add("firstGroup", FUSWhitelist.VersionRange.create("5", "10")).add("secondGroup").build() - cache.cacheGroups(date, whitelist, build) - TestCase.assertNull(cache.getCachedGroups(date, 100, BuildNumber.fromString("191"))) - } -} \ No newline at end of file diff --git a/platform/platform-tests/testSrc/com/intellij/internal/statistics/EventLogExternalSettingsServiceTest.kt b/platform/platform-tests/testSrc/com/intellij/internal/statistics/EventLogExternalSettingsServiceTest.kt deleted file mode 100644 index f56717434523..000000000000 --- a/platform/platform-tests/testSrc/com/intellij/internal/statistics/EventLogExternalSettingsServiceTest.kt +++ /dev/null @@ -1,177 +0,0 @@ -// 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. -package com.intellij.internal.statistics - -import com.intellij.internal.statistic.eventLog.EventLogExternalSettingsService -import com.intellij.internal.statistic.persistence.ApprovedGroupsCacheConfigurable -import com.intellij.internal.statistic.service.fus.FUSWhitelist -import com.intellij.openapi.util.BuildNumber -import com.intellij.testFramework.UsefulTestCase -import com.intellij.testFramework.fixtures.CodeInsightTestFixture -import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory -import com.intellij.util.containers.ContainerUtil -import org.junit.Assert -import org.junit.Test -import java.util.* - -class EventLogExternalSettingsServiceTest : UsefulTestCase() { - private var myFixture: CodeInsightTestFixture? = null - - override fun setUp() { - super.setUp() - - val factory = IdeaTestFixtureFactory.getFixtureFactory() - val fixtureBuilder = factory.createFixtureBuilder("ApprovedGroupsCacheTest") - myFixture = IdeaTestFixtureFactory.getFixtureFactory().createCodeInsightFixture(fixtureBuilder.fixture) - myFixture?.setUp() - } - - override fun tearDown() { - super.tearDown() - try { - myFixture?.tearDown() - } - catch (e: Throwable) { - addSuppressedException(e) - } - finally { - myFixture = null - } - } - - @Test - fun testCachedGroupsForActualCache() { - assertEquals( - WhitelistBuilder().add("cachedGroup1").add("cachedGroup2").build(), - WorkingEventLogExternalSettingsService().getApprovedGroups(ActualCache()) - ) - } - - @Test - fun testCachedGroupsForActualCacheWithVersionFrom() { - val whitelist = WhitelistBuilder().add( - "cachedGroup1", - FUSWhitelist.VersionRange.create("2", "5"), - FUSWhitelist.VersionRange.create(null, "5"), - FUSWhitelist.VersionRange.create(null, null) - ).add( - "cachedGroup2", - FUSWhitelist.VersionRange.create("4", "5"), - FUSWhitelist.VersionRange.create("4", null) - ).build() - assertEquals(whitelist, WorkingEventLogExternalSettingsServiceWithVersion().getApprovedGroups(ActualCacheWithVersions())) - } - - @Test - fun testReturnActualGroupsForNullableCache() { - assertEquals( - WhitelistBuilder().add("actualGroup1").add("actualGroup2").build(), - WorkingEventLogExternalSettingsService().getApprovedGroups(StaleCache()) - ) - } - - @Test - fun testReturnActualGroupsForNullableCacheWithVersions() { - val whitelist = WhitelistBuilder().add( - "actualGroup1", - FUSWhitelist.VersionRange.create(null, "5"), - FUSWhitelist.VersionRange.create(null, null) - ).add( - "actualGroup2", - FUSWhitelist.VersionRange.create("6", null) - ).build() - val approvedGroups = WorkingEventLogExternalSettingsServiceWithVersion().getApprovedGroups(StaleCache()) - assertEquals(whitelist, approvedGroups) - } - - @Test - fun testCachedGroupsForActualCacheAndBrokenService() { - assertEquals( - WhitelistBuilder().add("cachedGroup1").add("cachedGroup2").build(), - BrokenEventLogExternalSettingsService().getApprovedGroups(ActualCache()) - ) - } - - @Test - fun testCachedGroupsForActualCacheAndBrokenServiceWithVersion() { - val whitelist = WhitelistBuilder().add( - "cachedGroup1", - FUSWhitelist.VersionRange.create("2", "5"), - FUSWhitelist.VersionRange.create(null, "5"), - FUSWhitelist.VersionRange.create(null, null) - ).add( - "cachedGroup2", - FUSWhitelist.VersionRange.create("4", "5"), - FUSWhitelist.VersionRange.create("4", null) - ).build() - assertEquals(whitelist, BrokenEventLogExternalSettingsService().getApprovedGroups(ActualCacheWithVersions())) - } - - @Test - fun testEmptySetInCaseOfNothing() { - Assert.assertTrue(BrokenEventLogExternalSettingsService().getApprovedGroups(StaleCache()).isEmpty()) - } - - private class BrokenEventLogExternalSettingsService : EventLogExternalSettingsService() { - override fun getWhitelistedGroups(): FUSWhitelist? { - return null - } - } - - private class WorkingEventLogExternalSettingsService : EventLogExternalSettingsService() { - override fun getWhitelistedGroups(): FUSWhitelist? { - return FUSWhitelist.create(mutableMapOf( - Pair("actualGroup1", ContainerUtil.emptyList()), - Pair("actualGroup2", ContainerUtil.emptyList()) - )) - } - } - - private class WorkingEventLogExternalSettingsServiceWithVersion : EventLogExternalSettingsService() { - override fun getWhitelistedGroups(): FUSWhitelist? { - return WhitelistBuilder().add( - "actualGroup1", - FUSWhitelist.VersionRange.create(null, "5"), - FUSWhitelist.VersionRange.create(null, null) - ).add( - "actualGroup2", - FUSWhitelist.VersionRange.create("6", null) - ).build() - } - } - - private class ActualCache : ApprovedGroupsCacheConfigurable() { - override fun getCachedGroups(date: Date, - cacheActualDuration: Long, - currentBuild: BuildNumber?): FUSWhitelist { - return FUSWhitelist.create(mutableMapOf( - Pair("cachedGroup1", ContainerUtil.emptyList()), - Pair("cachedGroup2", ContainerUtil.emptyList()) - )) - } - } - - private class ActualCacheWithVersions : ApprovedGroupsCacheConfigurable() { - override fun getCachedGroups(date: Date, - cacheActualDuration: Long, - currentBuild: BuildNumber?): FUSWhitelist { - return WhitelistBuilder().add( - "cachedGroup1", - FUSWhitelist.VersionRange.create("2", "5"), - FUSWhitelist.VersionRange.create(null, "5"), - FUSWhitelist.VersionRange.create(null, null) - ).add( - "cachedGroup2", - FUSWhitelist.VersionRange.create("4", "5"), - FUSWhitelist.VersionRange.create("4", null) - ).build() - } - } - - private class StaleCache : ApprovedGroupsCacheConfigurable() { - override fun getCachedGroups(date: Date, - cacheActualDuration: Long, - currentBuild: BuildNumber?): FUSWhitelist? { - return null - } - } -} \ No newline at end of file diff --git a/platform/platform-tests/testSrc/com/intellij/internal/statistics/FUSTestUtils.kt b/platform/platform-tests/testSrc/com/intellij/internal/statistics/FUSTestUtils.kt index cb0e123485f8..a853fc158deb 100644 --- a/platform/platform-tests/testSrc/com/intellij/internal/statistics/FUSTestUtils.kt +++ b/platform/platform-tests/testSrc/com/intellij/internal/statistics/FUSTestUtils.kt @@ -2,26 +2,59 @@ package com.intellij.internal.statistics import com.intellij.internal.statistic.service.fus.FUSWhitelist -import com.intellij.util.containers.ContainerUtil +import com.intellij.internal.statistic.service.fus.FUSWhitelist.BuildRange +import com.intellij.internal.statistic.service.fus.FUSWhitelist.VersionRange +import com.intellij.openapi.util.BuildNumber class WhitelistBuilder { - val groups: MutableMap> = HashMap() + private val groupIds: MutableSet = HashSet() + private val groupVersions: MutableMap> = HashMap() + private val groupBuilds: MutableMap> = HashMap() - fun add(id: String): WhitelistBuilder { - groups[id] = ContainerUtil.emptyList() + fun addVersion(id: String, from: Int, to: Int): WhitelistBuilder { + if (!groupVersions.containsKey(id)) { + groupIds.add(id) + groupVersions[id] = mutableListOf() + } + groupVersions[id]!!.add(VersionRange(from, to)) return this } - fun add(id: String, vararg versions: FUSWhitelist.VersionRange): WhitelistBuilder { - val versionsList = mutableListOf() - for (version in versions) { - versionsList.add(version) + fun addVersion(id: String, from: String?, to: String?): WhitelistBuilder { + if (!groupVersions.containsKey(id)) { + groupIds.add(id) + groupVersions[id] = mutableListOf() } - groups[id] = versionsList + groupVersions[id]!!.add(VersionRange.create(from, to)) + return this + } + + fun addBuild(id: String, from: BuildNumber?, to: BuildNumber?): WhitelistBuilder { + if (!groupBuilds.containsKey(id)) { + groupIds.add(id) + groupBuilds[id] = mutableListOf() + } + groupBuilds[id]!!.add(BuildRange(from, to)) + return this + } + + fun addBuild(id: String, from: String?, to: String?): WhitelistBuilder { + if (!groupBuilds.containsKey(id)) { + groupIds.add(id) + groupBuilds[id] = mutableListOf() + } + groupBuilds[id]!!.add(BuildRange.create(from, to)) return this } fun build(): FUSWhitelist { - return FUSWhitelist.create(groups) + val result = HashMap() + for (groupId in groupIds) { + groupBuilds.getOrDefault(groupId, emptyList()) + val builds: List = groupBuilds.getOrDefault(groupId, emptyList()) + val versions: List = groupVersions.getOrDefault(groupId, emptyList()) + result[groupId] = FUSWhitelist.GroupFilterCondition(builds, versions) + } + return FUSWhitelist.create(result) } } \ No newline at end of file diff --git a/platform/platform-tests/testSrc/com/intellij/internal/statistics/FeatureEventLogWhitelistFilterTest.kt b/platform/platform-tests/testSrc/com/intellij/internal/statistics/FeatureEventLogWhitelistFilterTest.kt deleted file mode 100644 index 462972bb5f23..000000000000 --- a/platform/platform-tests/testSrc/com/intellij/internal/statistics/FeatureEventLogWhitelistFilterTest.kt +++ /dev/null @@ -1,458 +0,0 @@ -// 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. -package com.intellij.internal.statistics - -import com.intellij.internal.statistic.eventLog.* -import com.intellij.internal.statistic.service.fus.FUSWhitelist -import com.intellij.openapi.util.io.FileUtil -import org.junit.Test -import kotlin.test.assertEquals - -class FeatureEventLogWhitelistFilterTest { - - @Test - fun `test empty whitelist`() { - val all = ArrayList() - all.add(newEvent("recorder-id", "first")) - all.add(newEvent("recorder-id-1", "second")) - all.add(newEvent("recorder-id-2", "third")) - - testWhitelistFilter(FUSWhitelist.empty(), all, ArrayList()) - } - - @Test - fun `test whitelist without versions`() { - val first = newEvent("recorder-id", "first") - val second = newEvent("recorder-id-1", "second") - val third = newEvent("recorder-id", "third") - - val all = ArrayList() - all.add(first) - all.add(second) - all.add(third) - val filtered = ArrayList() - filtered.add(first) - filtered.add(third) - - val whitelist = WhitelistBuilder() - whitelist.add("recorder-id") - testWhitelistFilter(whitelist.build(), all, filtered) - } - - @Test - fun `test whitelist with multi groups`() { - val first = newEvent("recorder-id", "first") - val second = newEvent("recorder-id-1", "second") - val third = newEvent("recorder-id-2", "third") - - val all = ArrayList() - all.add(first) - all.add(second) - all.add(third) - val filtered = ArrayList() - filtered.add(first) - filtered.add(third) - - val whitelist = WhitelistBuilder() - whitelist.add("recorder-id") - whitelist.add("recorder-id-2") - testWhitelistFilter(whitelist.build(), all, filtered) - } - - @Test - fun `test whitelist all groups`() { - val first = newEvent("recorder-id", "first") - val second = newEvent("recorder-id-1", "second") - val third = newEvent("recorder-id-2", "third") - - val all = ArrayList() - all.add(first) - all.add(second) - all.add(third) - val filtered = ArrayList() - filtered.add(first) - filtered.add(second) - filtered.add(third) - - val whitelist = WhitelistBuilder() - whitelist.add("recorder-id") - whitelist.add("recorder-id-1") - whitelist.add("recorder-id-2") - testWhitelistFilter(whitelist.build(), all, filtered) - } - - @Test - fun `test whitelist with versions from`() { - val first = newEvent("recorder-id", "first", groupVersion = "1") - val second = newEvent("recorder-id", "third", groupVersion = "3") - - val all = ArrayList() - all.add(first) - all.add(second) - val filtered = ArrayList() - filtered.add(second) - - val whitelist = WhitelistBuilder() - whitelist.add("recorder-id", FUSWhitelist.VersionRange.create("2", null)) - testWhitelistFilter(whitelist.build(), all, filtered) - } - - @Test - fun `test whitelist with versions exact from`() { - val first = newEvent("recorder-id", "first", groupVersion = "1") - val second = newEvent("recorder-id", "third", groupVersion = "2") - - val all = ArrayList() - all.add(first) - all.add(second) - val filtered = ArrayList() - filtered.add(second) - - val whitelist = WhitelistBuilder() - whitelist.add("recorder-id", FUSWhitelist.VersionRange.create("2", null)) - testWhitelistFilter(whitelist.build(), all, filtered) - } - - @Test - fun `test whitelist with versions to`() { - val first = newEvent("recorder-id", "first", groupVersion = "1") - val second = newEvent("recorder-id", "third", groupVersion = "4") - - val all = ArrayList() - all.add(first) - all.add(second) - val filtered = ArrayList() - filtered.add(first) - - val whitelist = WhitelistBuilder() - whitelist.add("recorder-id", FUSWhitelist.VersionRange.create(null, "3")) - testWhitelistFilter(whitelist.build(), all, filtered) - } - - @Test - fun `test whitelist with versions exact to`() { - val first = newEvent("recorder-id", "first", groupVersion = "1") - val second = newEvent("recorder-id", "third", groupVersion = "4") - - val all = ArrayList() - all.add(first) - all.add(second) - val filtered = ArrayList() - filtered.add(first) - - val whitelist = WhitelistBuilder() - whitelist.add("recorder-id", FUSWhitelist.VersionRange.create(null, "4")) - testWhitelistFilter(whitelist.build(), all, filtered) - } - - @Test - fun `test whitelist with accept all versions`() { - val first = newEvent("recorder-id", "first", groupVersion = "1") - val second = newEvent("recorder-id", "third", groupVersion = "4") - - val all = ArrayList() - all.add(first) - all.add(second) - val filtered = ArrayList() - filtered.add(first) - filtered.add(second) - - val whitelist = WhitelistBuilder() - whitelist.add("recorder-id", FUSWhitelist.VersionRange.create(null, null)) - testWhitelistFilter(whitelist.build(), all, filtered) - } - - @Test - fun `test whitelist with empty versions list`() { - val first = newEvent("recorder-id", "first", groupVersion = "1") - val second = newEvent("recorder-id", "third", groupVersion = "4") - - val all = ArrayList() - all.add(first) - all.add(second) - val filtered = ArrayList() - filtered.add(first) - filtered.add(second) - - val whitelist = WhitelistBuilder() - whitelist.add("recorder-id") - testWhitelistFilter(whitelist.build(), all, filtered) - } - - @Test - fun `test whitelist with versions from and to`() { - val first = newEvent("recorder-id", "first", groupVersion = "2") - val second = newEvent("recorder-id", "third", groupVersion = "4") - - val all = ArrayList() - all.add(first) - all.add(second) - val filtered = ArrayList() - filtered.add(first) - filtered.add(second) - - val whitelist = WhitelistBuilder() - whitelist.add("recorder-id", FUSWhitelist.VersionRange.create("1", "5")) - testWhitelistFilter(whitelist.build(), all, filtered) - } - - @Test - fun `test whitelist with versions exact from and to`() { - val first = newEvent("recorder-id", "first", groupVersion = "1") - val second = newEvent("recorder-id", "third", groupVersion = "4") - - val all = ArrayList() - all.add(first) - all.add(second) - val filtered = ArrayList() - filtered.add(first) - filtered.add(second) - - val whitelist = WhitelistBuilder() - whitelist.add("recorder-id", FUSWhitelist.VersionRange.create("1", "5")) - testWhitelistFilter(whitelist.build(), all, filtered) - } - - @Test - fun `test whitelist with versions from and exact to`() { - val first = newEvent("recorder-id", "first", groupVersion = "2") - val second = newEvent("recorder-id", "third", groupVersion = "4") - - val all = ArrayList() - all.add(first) - all.add(second) - val filtered = ArrayList() - filtered.add(first) - - val whitelist = WhitelistBuilder() - whitelist.add("recorder-id", FUSWhitelist.VersionRange.create("1", "4")) - testWhitelistFilter(whitelist.build(), all, filtered) - } - - @Test - fun `test whitelist with complimentary multi versions`() { - val first = newEvent("recorder-id", "first", groupVersion = "2") - val second = newEvent("recorder-id", "third", groupVersion = "4") - - val all = ArrayList() - all.add(first) - all.add(second) - val filtered = ArrayList() - filtered.add(first) - filtered.add(second) - - val whitelist = WhitelistBuilder() - whitelist.add("recorder-id", FUSWhitelist.VersionRange.create("1", "4"), FUSWhitelist.VersionRange.create("4", "5")) - testWhitelistFilter(whitelist.build(), all, filtered) - } - - @Test - fun `test whitelist with intersected multi versions`() { - val first = newEvent("recorder-id", "first", groupVersion = "2") - val second = newEvent("recorder-id", "third", groupVersion = "4") - - val all = ArrayList() - all.add(first) - all.add(second) - val filtered = ArrayList() - filtered.add(first) - filtered.add(second) - - val whitelist = WhitelistBuilder() - whitelist.add("recorder-id", FUSWhitelist.VersionRange.create("1", "4"), FUSWhitelist.VersionRange.create("3", "5")) - testWhitelistFilter(whitelist.build(), all, filtered) - } - - @Test - fun `test whitelist with incomplete multi versions`() { - val first = newEvent("recorder-id", "first", groupVersion = "2") - val second = newEvent("recorder-id", "third", groupVersion = "4") - - val all = ArrayList() - all.add(first) - all.add(second) - val filtered = ArrayList() - filtered.add(first) - - val whitelist = WhitelistBuilder() - whitelist.add("recorder-id", FUSWhitelist.VersionRange.create(null, "3"), FUSWhitelist.VersionRange.create("5", null)) - testWhitelistFilter(whitelist.build(), all, filtered) - } - - @Test - fun `test whitelist with range and all range version`() { - val first = newEvent("recorder-id", "first", groupVersion = "2") - val second = newEvent("recorder-id", "third", groupVersion = "4") - - val all = ArrayList() - all.add(first) - all.add(second) - val filtered = ArrayList() - filtered.add(first) - filtered.add(second) - - val whitelist = WhitelistBuilder() - whitelist.add("recorder-id", FUSWhitelist.VersionRange.create(null, "2"), FUSWhitelist.VersionRange.create(null, null)) - testWhitelistFilter(whitelist.build(), all, filtered) - } - - @Test - fun `test filter snapshot builds`() { - val first = newEvent("recorder-id", "first", build = "999.9999") - val second = newEvent("recorder-id-1", "second", build = "999.0") - val third = newEvent("recorder-id", "third", build = "999.9999") - - val all = ArrayList() - all.add(first) - all.add(second) - all.add(third) - val filtered = ArrayList() - filtered.add(first) - filtered.add(third) - - testSnapshotBuilderFilter(all, filtered) - } - - @Test - fun `test filter none snapshot builds`() { - val first = newEvent("recorder-id", "first", build = "999.9999") - val second = newEvent("recorder-id-1", "second", build = "999.01") - val third = newEvent("recorder-id", "third", build = "999.9999") - - val all = ArrayList() - all.add(first) - all.add(second) - all.add(third) - testSnapshotBuilderFilter(all, all) - } - - @Test - fun `test filter all snapshot builds`() { - val first = newEvent("recorder-id", "first", build = "999.00") - val second = newEvent("recorder-id-1", "second", build = "999.0") - val third = newEvent("recorder-id", "third", build = "999.0") - - val all = ArrayList() - all.add(first) - all.add(second) - all.add(third) - testSnapshotBuilderFilter(all, ArrayList()) - } - - @Test - fun `test filter group id and snapshot builds`() { - val first = newEvent("recorder-id", "first", build = "999.9999") - val second = newEvent("recorder-id-1", "second", build = "999.9999") - val third = newEvent("recorder-id", "third", build = "999.0") - - val all = ArrayList() - all.add(first) - all.add(second) - all.add(third) - val filtered = ArrayList() - filtered.add(first) - - val whitelist = WhitelistBuilder() - whitelist.add("recorder-id") - testWhitelistAndSnapshotBuildFilter(whitelist.build(), all, filtered) - } - - @Test - fun `test filter group id, version from and snapshot builds`() { - val first = newEvent("recorder-id", "first", build = "999.9999", groupVersion = "3") - val second = newEvent("recorder-id", "second", build = "999.9999", groupVersion = "2") - val third = newEvent("recorder-id", "third", build = "999.0", groupVersion = "4") - val forth = newEvent("recorder-id-1", "forth", build = "999.9999", groupVersion = "5") - val fifth = newEvent("recorder-id-2", "fifth", build = "999.9999", groupVersion = "1") - - val all = ArrayList() - all.add(first) - all.add(second) - all.add(third) - all.add(forth) - all.add(fifth) - val filtered = ArrayList() - filtered.add(first) - - val whitelist = WhitelistBuilder() - whitelist.add("recorder-id", FUSWhitelist.VersionRange.create("3", null)) - testWhitelistAndSnapshotBuildFilter(whitelist.build(), all, filtered) - } - - @Test - fun `test filter group id, version to and snapshot builds`() { - val first = newEvent("recorder-id", "first", build = "999.9999", groupVersion = "3") - val second = newEvent("recorder-id", "second", build = "999.9999", groupVersion = "2") - val third = newEvent("recorder-id", "third", build = "999.0", groupVersion = "4") - val forth = newEvent("recorder-id-1", "forth", build = "999.9999", groupVersion = "5") - val fifth = newEvent("recorder-id-2", "fifth", build = "999.9999", groupVersion = "1") - - val all = ArrayList() - all.add(first) - all.add(second) - all.add(third) - all.add(forth) - all.add(fifth) - val filtered = ArrayList() - filtered.add(second) - - val whitelist = WhitelistBuilder() - whitelist.add("recorder-id", FUSWhitelist.VersionRange.create(null, "3")) - testWhitelistAndSnapshotBuildFilter(whitelist.build(), all, filtered) - } - - @Test - fun `test filter group id, version from and to and snapshot builds`() { - val first = newEvent("recorder-id", "first", build = "999.9999", groupVersion = "3") - val second = newEvent("recorder-id", "second", build = "999.9999", groupVersion = "2") - val third = newEvent("recorder-id", "third", build = "999.0", groupVersion = "4") - val forth = newEvent("recorder-id-1", "forth", build = "999.9999", groupVersion = "5") - val fifth = newEvent("recorder-id-2", "fifth", build = "999.9999", groupVersion = "1") - - val all = ArrayList() - all.add(first) - all.add(second) - all.add(third) - all.add(forth) - all.add(fifth) - val filtered = ArrayList() - filtered.add(second) - - val whitelist = WhitelistBuilder() - whitelist.add("recorder-id", FUSWhitelist.VersionRange.create("1", "3")) - testWhitelistAndSnapshotBuildFilter(whitelist.build(), all, filtered) - } - - private fun testWhitelistFilter(whitelist: FUSWhitelist, all: List, filtered: List) { - testEventLogFilter(all, filtered, LogEventWhitelistFilter(whitelist)) - } - - private fun testWhitelistAndSnapshotBuildFilter(whitelist: FUSWhitelist, all: List, filtered: List) { - testEventLogFilter(all, filtered, LogEventCompositeFilter(LogEventWhitelistFilter(whitelist), LogEventSnapshotBuildFilter)) - } - - private fun testSnapshotBuilderFilter(all: List, filtered: List) { - testEventLogFilter(all, filtered, LogEventSnapshotBuildFilter) - } - - private fun testEventLogFilter(all: List, filtered: List, filter: LogEventFilter) { - val records = ArrayList() - if (!filtered.isEmpty()) { - records.add(LogEventRecord(filtered)) - } - val expected = LogEventRecordRequest("recorder-id", "IU", "user-id", records, false) - - val log = FileUtil.createTempFile("feature-event-log", ".log") - try { - val out = StringBuilder() - for (event in all) { - out.append(LogEventSerializer.toString(event)).append("\n") - } - FileUtil.writeToFile(log, out.toString()) - val actual = LogEventRecordRequest.create(log, "recorder-id", "IU", "user-id", 600, filter, false) - assertEquals(expected, actual) - } - finally { - FileUtil.delete(log) - } - } -} \ No newline at end of file diff --git a/platform/platform-tests/testSrc/com/intellij/internal/statistics/FeatureUsageDataTest.kt b/platform/platform-tests/testSrc/com/intellij/internal/statistics/FeatureUsageDataTest.kt index 1c7d0d75ae55..2ef2b3143a17 100644 --- a/platform/platform-tests/testSrc/com/intellij/internal/statistics/FeatureUsageDataTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/internal/statistics/FeatureUsageDataTest.kt @@ -5,10 +5,11 @@ import com.intellij.internal.statistic.eventLog.FeatureUsageData import com.intellij.internal.statistic.service.fus.collectors.FUStateUsagesLogger import com.intellij.openapi.actionSystem.ActionPlaces import com.intellij.openapi.util.Version +import com.intellij.testFramework.PlatformTestCase import org.junit.Assert import org.junit.Test -class FeatureUsageDataTest { +class FeatureUsageDataTest : PlatformTestCase() { @Test fun `test empty data`() { diff --git a/platform/platform-tests/testSrc/com/intellij/internal/statistics/FeatureUsageEventLoggerTest.kt b/platform/platform-tests/testSrc/com/intellij/internal/statistics/FeatureUsageEventLoggerTest.kt index ecd1bea48444..a36a328e54b7 100644 --- a/platform/platform-tests/testSrc/com/intellij/internal/statistics/FeatureUsageEventLoggerTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/internal/statistics/FeatureUsageEventLoggerTest.kt @@ -2,6 +2,7 @@ package com.intellij.internal.statistics import com.intellij.internal.statistic.eventLog.* +import com.intellij.testFramework.PlatformTestCase import org.junit.Test import java.io.File import java.util.* @@ -9,7 +10,7 @@ import java.util.concurrent.TimeUnit import kotlin.test.assertEquals import kotlin.test.assertTrue -class FeatureUsageEventLoggerTest { +class FeatureUsageEventLoggerTest : PlatformTestCase() { @Test fun testSingleEvent() { diff --git a/platform/platform-tests/testSrc/com/intellij/internal/statistics/MetricEventTest.kt b/platform/platform-tests/testSrc/com/intellij/internal/statistics/MetricEventTest.kt index 432e262f83c5..9e4beedc0af4 100644 --- a/platform/platform-tests/testSrc/com/intellij/internal/statistics/MetricEventTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/internal/statistics/MetricEventTest.kt @@ -5,10 +5,11 @@ package com.intellij.internal.statistics import com.intellij.internal.statistic.beans.* import com.intellij.internal.statistic.eventLog.FeatureUsageData +import com.intellij.testFramework.PlatformTestCase import org.junit.Assert import org.junit.Test -class MetricEventTest { +class MetricEventTest : PlatformTestCase() { @Test fun `test create new metric`() { @@ -188,9 +189,9 @@ class MetricEventTest { val obj = MetricEventTestObj() val default = MetricEventTestObj() - addIfDiffers(result, obj, default, {o -> o.strValue}, "metric.string") - addIfDiffers(result, obj, default, {o -> o.intValue}, "metric.int") - addIfDiffers(result, obj, default, {o -> o.floatValue}, "metric.float") + addIfDiffers(result, obj, default, { o -> o.strValue }, "metric.string") + addIfDiffers(result, obj, default, { o -> o.intValue }, "metric.int") + addIfDiffers(result, obj, default, { o -> o.floatValue }, "metric.float") Assert.assertTrue(result.isEmpty()) } @@ -202,9 +203,9 @@ class MetricEventTest { val default = MetricEventTestObj() - addIfDiffers(result, obj, default, {o -> o.strValue}, "metric.string") - addIfDiffers(result, obj, default, {o -> o.intValue}, "metric.int") - addIfDiffers(result, obj, default, {o -> o.floatValue}, "metric.float") + addIfDiffers(result, obj, default, { o -> o.strValue }, "metric.string") + addIfDiffers(result, obj, default, { o -> o.intValue }, "metric.int") + addIfDiffers(result, obj, default, { o -> o.floatValue }, "metric.float") Assert.assertTrue(result.size == 1) for (event in result) { Assert.assertTrue(event.eventId == "metric.string") @@ -221,9 +222,9 @@ class MetricEventTest { val default = MetricEventTestObj() val data = FeatureUsageData().addPlace("MainMenu") - addIfDiffers(result, obj, default, {o -> o.strValue}, "metric.string", data) - addIfDiffers(result, obj, default, {o -> o.intValue}, "metric.int", data) - addIfDiffers(result, obj, default, {o -> o.floatValue}, "metric.float", data) + addIfDiffers(result, obj, default, { o -> o.strValue }, "metric.string", data) + addIfDiffers(result, obj, default, { o -> o.intValue }, "metric.int", data) + addIfDiffers(result, obj, default, { o -> o.floatValue }, "metric.float", data) Assert.assertTrue(result.size == 1) for (event in result) { Assert.assertTrue(event.eventId == "metric.string") @@ -240,9 +241,9 @@ class MetricEventTest { val default = MetricEventTestObj() - addIfDiffers(result, obj, default, {o -> o.strValue}, "metric.string") - addIfDiffers(result, obj, default, {o -> o.intValue}, "metric.int") - addIfDiffers(result, obj, default, {o -> o.floatValue}, "metric.float") + addIfDiffers(result, obj, default, { o -> o.strValue }, "metric.string") + addIfDiffers(result, obj, default, { o -> o.intValue }, "metric.int") + addIfDiffers(result, obj, default, { o -> o.floatValue }, "metric.float") Assert.assertTrue(result.size == 1) for (event in result) { Assert.assertTrue(event.eventId == "metric.int") @@ -258,10 +259,10 @@ class MetricEventTest { val default = MetricEventTestObj() - addIfDiffers(result, obj, default, {o -> o.strValue}, "metric.string") - addIfDiffers(result, obj, default, {o -> o.intValue}, "metric.int") - addIfDiffers(result, obj, default, {o -> o.floatValue}, "metric.float") - addIfDiffers(result, obj, default, {o -> o.boolValue}, "metric.bool") + addIfDiffers(result, obj, default, { o -> o.strValue }, "metric.string") + addIfDiffers(result, obj, default, { o -> o.intValue }, "metric.int") + addIfDiffers(result, obj, default, { o -> o.floatValue }, "metric.float") + addIfDiffers(result, obj, default, { o -> o.boolValue }, "metric.bool") Assert.assertTrue(result.size == 1) for (event in result) { Assert.assertTrue(event.eventId == "metric.float") @@ -279,9 +280,9 @@ class MetricEventTest { val default = MetricEventTestObj() - addIfDiffers(result, obj, default, {o -> o.strValue}, "metric.string") - addIfDiffers(result, obj, default, {o -> o.intValue}, "metric.int") - addIfDiffers(result, obj, default, {o -> o.floatValue}, "metric.float") + addIfDiffers(result, obj, default, { o -> o.strValue }, "metric.string") + addIfDiffers(result, obj, default, { o -> o.intValue }, "metric.int") + addIfDiffers(result, obj, default, { o -> o.floatValue }, "metric.float") Assert.assertTrue(result.size == 3) for (event in result) { Assert.assertTrue(event.eventId in listOf("metric.string", "metric.int", "metric.float")) @@ -304,9 +305,9 @@ class MetricEventTest { val default = MetricEventTestObj() val data = FeatureUsageData().addPlace("MainMenu") - addIfDiffers(result, obj, default, {o -> o.strValue}, "metric.string", data) - addIfDiffers(result, obj, default, {o -> o.intValue}, "metric.int", data) - addIfDiffers(result, obj, default, {o -> o.floatValue}, "metric.float", data) + addIfDiffers(result, obj, default, { o -> o.strValue }, "metric.string", data) + addIfDiffers(result, obj, default, { o -> o.intValue }, "metric.int", data) + addIfDiffers(result, obj, default, { o -> o.floatValue }, "metric.float", data) Assert.assertTrue(result.size == 3) for (event in result) { Assert.assertTrue(event.eventId in listOf("metric.string", "metric.int", "metric.float")) @@ -325,7 +326,7 @@ class MetricEventTest { val obj = MetricEventTestObj() val default = MetricEventTestObj() - addBoolIfDiffers(result, obj, default, {o -> o.boolValue}, "metric.bool") + addBoolIfDiffers(result, obj, default, { o -> o.boolValue }, "metric.bool") Assert.assertTrue(result.isEmpty()) } @@ -337,7 +338,7 @@ class MetricEventTest { val default = MetricEventTestObj() - addBoolIfDiffers(result, obj, default, {o -> o.boolValue}, "metric.bool") + addBoolIfDiffers(result, obj, default, { o -> o.boolValue }, "metric.bool") Assert.assertTrue(result.size == 1) for (event in result) { Assert.assertTrue(event.eventId == "metric.bool") @@ -354,7 +355,7 @@ class MetricEventTest { val default = MetricEventTestObj() val data = FeatureUsageData().addPlace("MainMenu") - addBoolIfDiffers(result, obj, default, {o -> o.boolValue}, "metric.bool", data) + addBoolIfDiffers(result, obj, default, { o -> o.boolValue }, "metric.bool", data) Assert.assertTrue(result.size == 1) for (event in result) { Assert.assertTrue(event.eventId == "metric.bool") @@ -369,7 +370,7 @@ class MetricEventTest { val obj = MetricEventTestObj() val default = MetricEventTestObj() - addCounterIfDiffers(result, obj, default, {o -> o.intValue}, "metric.count") + addCounterIfDiffers(result, obj, default, { o -> o.intValue }, "metric.count") Assert.assertTrue(result.isEmpty()) } @@ -381,7 +382,7 @@ class MetricEventTest { val default = MetricEventTestObj() - addCounterIfDiffers(result, obj, default, {o -> o.intValue}, "metric.count") + addCounterIfDiffers(result, obj, default, { o -> o.intValue }, "metric.count") Assert.assertTrue(result.size == 1) for (event in result) { Assert.assertTrue(event.eventId == "metric.count") @@ -398,7 +399,7 @@ class MetricEventTest { val default = MetricEventTestObj() val data = FeatureUsageData().addPlace("MainMenu") - addCounterIfDiffers(result, obj, default, {o -> o.intValue}, "metric.count", data) + addCounterIfDiffers(result, obj, default, { o -> o.intValue }, "metric.count", data) Assert.assertTrue(result.size == 1) for (event in result) { Assert.assertTrue(event.eventId == "metric.count") @@ -413,7 +414,7 @@ class MetricEventTest { val obj = MetricEventTestObj() val default = MetricEventTestObj() - addCounterRangeIfDiffers(result, obj, default, {o -> o.intValue}, "metric.range") + addCounterRangeIfDiffers(result, obj, default, { o -> o.intValue }, "metric.range") Assert.assertTrue(result.isEmpty()) } @@ -425,7 +426,7 @@ class MetricEventTest { val default = MetricEventTestObj() - addCounterRangeIfDiffers(result, obj, default, {o -> o.intValue}, "metric.range") + addCounterRangeIfDiffers(result, obj, default, { o -> o.intValue }, "metric.range") Assert.assertTrue(result.size == 1) for (event in result) { Assert.assertTrue(event.eventId == "metric.range") @@ -443,7 +444,7 @@ class MetricEventTest { val default = MetricEventTestObj() val data = FeatureUsageData().addPlace("MainMenu") - addCounterRangeIfDiffers(result, obj, default, {o -> o.intValue}, "metric.range", data) + addCounterRangeIfDiffers(result, obj, default, { o -> o.intValue }, "metric.range", data) Assert.assertTrue(result.size == 1) for (event in result) { Assert.assertTrue(event.eventId == "metric.range") @@ -459,7 +460,7 @@ class MetricEventTest { val obj = MetricEventTestObj() val default = MetricEventTestObj() - addCounterRangeIfDiffers(result, obj, default, {o -> o.intValue}, "metric.range", listOf(1, 5, 10)) + addCounterRangeIfDiffers(result, obj, default, { o -> o.intValue }, "metric.range", listOf(1, 5, 10)) Assert.assertTrue(result.isEmpty()) } @@ -471,7 +472,7 @@ class MetricEventTest { val default = MetricEventTestObj() - addCounterRangeIfDiffers(result, obj, default, {o -> o.intValue}, "metric.range", listOf(1, 5, 10)) + addCounterRangeIfDiffers(result, obj, default, { o -> o.intValue }, "metric.range", listOf(1, 5, 10)) Assert.assertTrue(result.size == 1) for (event in result) { Assert.assertTrue(event.eventId == "metric.range") @@ -489,7 +490,7 @@ class MetricEventTest { val default = MetricEventTestObj() val data = FeatureUsageData().addPlace("MainMenu") - addCounterRangeIfDiffers(result, obj, default, {o -> o.intValue}, "metric.range", listOf(1, 5, 10), data) + addCounterRangeIfDiffers(result, obj, default, { o -> o.intValue }, "metric.range", listOf(1, 5, 10), data) Assert.assertTrue(result.size == 1) for (event in result) { Assert.assertTrue(event.eventId == "metric.range") diff --git a/platform/platform-tests/testSrc/com/intellij/internal/statistics/MetricEventUnitTest.kt b/platform/platform-tests/testSrc/com/intellij/internal/statistics/MetricEventUnitTest.kt index 910379a878ca..c6912fe388d6 100644 --- a/platform/platform-tests/testSrc/com/intellij/internal/statistics/MetricEventUnitTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/internal/statistics/MetricEventUnitTest.kt @@ -5,12 +5,13 @@ package com.intellij.internal.statistics import com.intellij.internal.statistic.beans.* import com.intellij.internal.statistic.eventLog.FeatureUsageData +import com.intellij.testFramework.PlatformTestCase import com.intellij.util.containers.ContainerUtil.newArrayList import gnu.trove.THashSet import org.junit.Assert import org.junit.Test -class MetricEventUnitTest { +class MetricEventUnitTest : PlatformTestCase() { @Test fun `test compare metric events`() { diff --git a/platform/platform-tests/testSrc/com/intellij/internal/statistics/SystemRuntimeCollectorTest.kt b/platform/platform-tests/testSrc/com/intellij/internal/statistics/SystemRuntimeCollectorTest.kt index a2e685073cfa..500c2aaea707 100644 --- a/platform/platform-tests/testSrc/com/intellij/internal/statistics/SystemRuntimeCollectorTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/internal/statistics/SystemRuntimeCollectorTest.kt @@ -3,10 +3,11 @@ package com.intellij.internal.statistics import com.intellij.internal.statistic.collectors.fus.os.SystemRuntimeCollector import com.intellij.internal.statistic.eventLog.FeatureUsageData +import com.intellij.testFramework.PlatformTestCase import org.junit.Assert import org.junit.Test -class SystemRuntimeCollectorTest { +class SystemRuntimeCollectorTest : PlatformTestCase() { @Test diff --git a/platform/platform-tests/testSrc/com/intellij/internal/statistics/UsageDescriptorUnitTest.kt b/platform/platform-tests/testSrc/com/intellij/internal/statistics/UsageDescriptorUnitTest.kt index 65394b5ad845..2c68619d7091 100644 --- a/platform/platform-tests/testSrc/com/intellij/internal/statistics/UsageDescriptorUnitTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/internal/statistics/UsageDescriptorUnitTest.kt @@ -3,12 +3,13 @@ package com.intellij.internal.statistics import com.intellij.internal.statistic.beans.UsageDescriptor import com.intellij.internal.statistic.eventLog.FeatureUsageData +import com.intellij.testFramework.PlatformTestCase import com.intellij.util.containers.ContainerUtil.newArrayList import gnu.trove.THashSet import org.junit.Assert import org.junit.Test -class UsageDescriptorUnitTest { +class UsageDescriptorUnitTest : PlatformTestCase() { @Test fun `test compare usage descriptor`() { diff --git a/platform/platform-tests/testSrc/com/intellij/internal/statistics/whitelist/FeatureEventLogWhitelistFilterTest.kt b/platform/platform-tests/testSrc/com/intellij/internal/statistics/whitelist/FeatureEventLogWhitelistFilterTest.kt new file mode 100644 index 000000000000..2159401a3636 --- /dev/null +++ b/platform/platform-tests/testSrc/com/intellij/internal/statistics/whitelist/FeatureEventLogWhitelistFilterTest.kt @@ -0,0 +1,859 @@ +// 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. +package com.intellij.internal.statistics.whitelist + +import com.intellij.internal.statistic.eventLog.* +import com.intellij.internal.statistic.service.fus.FUSWhitelist +import com.intellij.internal.statistics.WhitelistBuilder +import com.intellij.internal.statistics.newEvent +import com.intellij.openapi.util.io.FileUtil +import org.junit.Test +import kotlin.test.assertEquals + +class FeatureEventLogWhitelistFilterTest { + + @Test + fun `test empty whitelist`() { + val all = ArrayList() + all.add(newEvent("recorder-id", "first")) + all.add(newEvent("recorder-id-1", "second")) + all.add(newEvent("recorder-id-2", "third")) + + testWhitelistFilter(FUSWhitelist.empty(), all, ArrayList()) + } + + @Test + fun `test whitelist without versions`() { + val first = newEvent("recorder-id", "first", build = "173.23") + val second = newEvent("recorder-id-1", "second", build = "173.23") + val third = newEvent("recorder-id", "third", build = "173.23") + + val all = ArrayList() + all.add(first) + all.add(second) + all.add(third) + val filtered = ArrayList() + filtered.add(first) + filtered.add(third) + + val whitelist = WhitelistBuilder() + whitelist.addBuild("recorder-id", "173.20.132", null) + testWhitelistFilter(whitelist.build(), all, filtered) + } + + @Test + fun `test whitelist with multi groups`() { + val first = newEvent("recorder-id", "first", build = "173.23") + val second = newEvent("recorder-id-1", "second", build = "173.23") + val third = newEvent("recorder-id-2", "third", build = "173.23") + + val all = ArrayList() + all.add(first) + all.add(second) + all.add(third) + val filtered = ArrayList() + filtered.add(first) + filtered.add(third) + + val whitelist = WhitelistBuilder() + whitelist.addBuild("recorder-id", "173.20.132", null) + whitelist.addBuild("recorder-id-2", "173.20.132", "173.24.132") + testWhitelistFilter(whitelist.build(), all, filtered) + } + + @Test + fun `test whitelist all groups`() { + val first = newEvent("recorder-id", "first", build = "173.23") + val second = newEvent("recorder-id-1", "second", build = "173.23") + val third = newEvent("recorder-id-2", "third", build = "173.23") + + val all = ArrayList() + all.add(first) + all.add(second) + all.add(third) + val filtered = ArrayList() + filtered.add(first) + filtered.add(second) + filtered.add(third) + + val whitelist = WhitelistBuilder() + whitelist.addBuild("recorder-id", null, "182.0") + whitelist.addBuild("recorder-id-1", null, "182.0") + whitelist.addBuild("recorder-id-2", null, "182.0") + testWhitelistFilter(whitelist.build(), all, filtered) + } + + @Test + fun `test whitelist with versions from`() { + val first = newEvent("recorder-id", "first", groupVersion = "1") + val second = newEvent("recorder-id", "third", groupVersion = "3") + + val all = ArrayList() + all.add(first) + all.add(second) + val filtered = ArrayList() + filtered.add(second) + + val whitelist = WhitelistBuilder() + whitelist.addVersion("recorder-id", "2", null) + testWhitelistFilter(whitelist.build(), all, filtered) + } + + @Test + fun `test whitelist with versions exact from`() { + val first = newEvent("recorder-id", "first", groupVersion = "1") + val second = newEvent("recorder-id", "third", groupVersion = "2") + + val all = ArrayList() + all.add(first) + all.add(second) + val filtered = ArrayList() + filtered.add(second) + + val whitelist = WhitelistBuilder() + whitelist.addVersion("recorder-id", "2", null) + testWhitelistFilter(whitelist.build(), all, filtered) + } + + @Test + fun `test whitelist with versions to`() { + val first = newEvent("recorder-id", "first", groupVersion = "1") + val second = newEvent("recorder-id", "third", groupVersion = "4") + + val all = ArrayList() + all.add(first) + all.add(second) + val filtered = ArrayList() + filtered.add(first) + + val whitelist = WhitelistBuilder() + whitelist.addVersion("recorder-id", null, "3") + testWhitelistFilter(whitelist.build(), all, filtered) + } + + @Test + fun `test whitelist with versions exact to`() { + val first = newEvent("recorder-id", "first", groupVersion = "1") + val second = newEvent("recorder-id", "third", groupVersion = "4") + + val all = ArrayList() + all.add(first) + all.add(second) + val filtered = ArrayList() + filtered.add(first) + + val whitelist = WhitelistBuilder() + whitelist.addVersion("recorder-id", null, "4") + testWhitelistFilter(whitelist.build(), all, filtered) + } + + @Test + fun `test whitelist with accept all versions`() { + val first = newEvent("recorder-id", "first", groupVersion = "1") + val second = newEvent("recorder-id", "third", groupVersion = "4") + + val all = ArrayList() + all.add(first) + all.add(second) + val filtered = ArrayList() + filtered.add(first) + filtered.add(second) + + val whitelist = WhitelistBuilder() + whitelist.addVersion("recorder-id", null, null) + testWhitelistFilter(whitelist.build(), all, filtered) + } + + @Test + fun `test whitelist with empty versions list`() { + val first = newEvent("recorder-id", "first", groupVersion = "1", build = "181.0") + val second = newEvent("recorder-id", "third", groupVersion = "4", build = "181.0") + + val all = ArrayList() + all.add(first) + all.add(second) + val filtered = ArrayList() + filtered.add(first) + filtered.add(second) + + val whitelist = WhitelistBuilder() + whitelist.addBuild("recorder-id", null, "182.0") + testWhitelistFilter(whitelist.build(), all, filtered) + } + + @Test + fun `test whitelist with versions from and to`() { + val first = newEvent("recorder-id", "first", groupVersion = "2") + val second = newEvent("recorder-id", "third", groupVersion = "4") + + val all = ArrayList() + all.add(first) + all.add(second) + val filtered = ArrayList() + filtered.add(first) + filtered.add(second) + + val whitelist = WhitelistBuilder() + whitelist.addVersion("recorder-id", "1", "5") + testWhitelistFilter(whitelist.build(), all, filtered) + } + + @Test + fun `test whitelist with versions exact from and to`() { + val first = newEvent("recorder-id", "first", groupVersion = "1") + val second = newEvent("recorder-id", "third", groupVersion = "4") + + val all = ArrayList() + all.add(first) + all.add(second) + val filtered = ArrayList() + filtered.add(first) + filtered.add(second) + + val whitelist = WhitelistBuilder() + whitelist.addVersion("recorder-id", "1", "5") + testWhitelistFilter(whitelist.build(), all, filtered) + } + + @Test + fun `test whitelist with versions from and exact to`() { + val first = newEvent("recorder-id", "first", groupVersion = "2") + val second = newEvent("recorder-id", "third", groupVersion = "4") + + val all = ArrayList() + all.add(first) + all.add(second) + val filtered = ArrayList() + filtered.add(first) + + val whitelist = WhitelistBuilder() + whitelist.addVersion("recorder-id", "1", "4") + testWhitelistFilter(whitelist.build(), all, filtered) + } + + @Test + fun `test whitelist with complimentary multi versions`() { + val first = newEvent("recorder-id", "first", groupVersion = "2") + val second = newEvent("recorder-id", "third", groupVersion = "4") + + val all = ArrayList() + all.add(first) + all.add(second) + val filtered = ArrayList() + filtered.add(first) + filtered.add(second) + + val whitelist = WhitelistBuilder() + whitelist.addVersion("recorder-id", "1", "4").addVersion("recorder-id", "4", "5") + testWhitelistFilter(whitelist.build(), all, filtered) + } + + @Test + fun `test whitelist with intersected multi versions`() { + val first = newEvent("recorder-id", "first", groupVersion = "2") + val second = newEvent("recorder-id", "third", groupVersion = "4") + + val all = ArrayList() + all.add(first) + all.add(second) + val filtered = ArrayList() + filtered.add(first) + filtered.add(second) + + val whitelist = WhitelistBuilder() + whitelist.addVersion("recorder-id", "1", "4").addVersion("recorder-id", "3", "5") + testWhitelistFilter(whitelist.build(), all, filtered) + } + + @Test + fun `test whitelist with incomplete multi versions`() { + val first = newEvent("recorder-id", "first", groupVersion = "2") + val second = newEvent("recorder-id", "third", groupVersion = "4") + + val all = ArrayList() + all.add(first) + all.add(second) + val filtered = ArrayList() + filtered.add(first) + + val whitelist = WhitelistBuilder() + whitelist.addVersion("recorder-id", null, "3").addVersion("recorder-id", "5", null) + testWhitelistFilter(whitelist.build(), all, filtered) + } + + @Test + fun `test whitelist with range and all range version`() { + val first = newEvent("recorder-id", "first", groupVersion = "2") + val second = newEvent("recorder-id", "third", groupVersion = "4") + + val all = ArrayList() + all.add(first) + all.add(second) + val filtered = ArrayList() + filtered.add(first) + filtered.add(second) + + val whitelist = WhitelistBuilder() + whitelist.addVersion("recorder-id", null, "2").addVersion("recorder-id", null, null) + testWhitelistFilter(whitelist.build(), all, filtered) + } + + @Test + fun `test filter snapshot builds`() { + val first = newEvent("recorder-id", "first", build = "999.9999") + val second = newEvent("recorder-id-1", "second", build = "999.0") + val third = newEvent("recorder-id", "third", build = "999.9999") + + val all = ArrayList() + all.add(first) + all.add(second) + all.add(third) + val filtered = ArrayList() + filtered.add(first) + filtered.add(third) + + testSnapshotBuilderFilter(all, filtered) + } + + @Test + fun `test filter none snapshot builds`() { + val first = newEvent("recorder-id", "first", build = "999.9999") + val second = newEvent("recorder-id-1", "second", build = "999.01") + val third = newEvent("recorder-id", "third", build = "999.9999") + + val all = ArrayList() + all.add(first) + all.add(second) + all.add(third) + testSnapshotBuilderFilter(all, all) + } + + @Test + fun `test filter all snapshot builds`() { + val first = newEvent("recorder-id", "first", build = "999.00") + val second = newEvent("recorder-id-1", "second", build = "999.0") + val third = newEvent("recorder-id", "third", build = "999.0") + + val all = ArrayList() + all.add(first) + all.add(second) + all.add(third) + testSnapshotBuilderFilter(all, ArrayList()) + } + + @Test + fun `test filter group id and snapshot builds`() { + val first = newEvent("recorder-id", "first", build = "999.9999") + val second = newEvent("recorder-id-1", "second", build = "999.9999") + val third = newEvent("recorder-id", "third", build = "999.0") + + val all = ArrayList() + all.add(first) + all.add(second) + all.add(third) + val filtered = ArrayList() + filtered.add(first) + + val whitelist = WhitelistBuilder() + whitelist.addBuild("recorder-id", "999.99", null) + testWhitelistAndSnapshotBuildFilter(whitelist.build(), all, filtered) + } + + @Test + fun `test filter group id, version from and snapshot builds`() { + val first = newEvent("recorder-id", "first", build = "999.9999", groupVersion = "3") + val second = newEvent("recorder-id", "second", build = "999.9999", groupVersion = "2") + val third = newEvent("recorder-id", "third", build = "999.0", groupVersion = "4") + val forth = newEvent("recorder-id-1", "forth", build = "999.9999", groupVersion = "5") + val fifth = newEvent("recorder-id-2", "fifth", build = "999.9999", groupVersion = "1") + + val all = ArrayList() + all.add(first) + all.add(second) + all.add(third) + all.add(forth) + all.add(fifth) + val filtered = ArrayList() + filtered.add(first) + + val whitelist = WhitelistBuilder() + whitelist.addVersion("recorder-id", "3", null) + testWhitelistAndSnapshotBuildFilter(whitelist.build(), all, filtered) + } + + @Test + fun `test filter group id, version to and snapshot builds`() { + val first = newEvent("recorder-id", "first", build = "999.9999", groupVersion = "3") + val second = newEvent("recorder-id", "second", build = "999.9999", groupVersion = "2") + val third = newEvent("recorder-id", "third", build = "999.0", groupVersion = "4") + val forth = newEvent("recorder-id-1", "forth", build = "999.9999", groupVersion = "5") + val fifth = newEvent("recorder-id-2", "fifth", build = "999.9999", groupVersion = "1") + + val all = ArrayList() + all.add(first) + all.add(second) + all.add(third) + all.add(forth) + all.add(fifth) + val filtered = ArrayList() + filtered.add(second) + + val whitelist = WhitelistBuilder() + whitelist.addVersion("recorder-id", null, "3") + testWhitelistAndSnapshotBuildFilter(whitelist.build(), all, filtered) + } + + @Test + fun `test filter group id, version from and to and snapshot builds`() { + val first = newEvent("recorder-id", "first", build = "999.9999", groupVersion = "3") + val second = newEvent("recorder-id", "second", build = "999.9999", groupVersion = "2") + val third = newEvent("recorder-id", "third", build = "999.0", groupVersion = "4") + val forth = newEvent("recorder-id-1", "forth", build = "999.9999", groupVersion = "5") + val fifth = newEvent("recorder-id-2", "fifth", build = "999.9999", groupVersion = "1") + + val all = ArrayList() + all.add(first) + all.add(second) + all.add(third) + all.add(forth) + all.add(fifth) + val filtered = ArrayList() + filtered.add(second) + + val whitelist = WhitelistBuilder() + whitelist.addVersion("recorder-id", "1", "3") + testWhitelistAndSnapshotBuildFilter(whitelist.build(), all, filtered) + } + + // test build ranges + @Test + fun `test filter build from with build short the same`() { + val first = newEvent("recorder-id", "first", build = "173.23") + + val all = ArrayList() + all.add(first) + val filtered = ArrayList() + filtered.add(first) + + val whitelist = WhitelistBuilder() + whitelist.addBuild("recorder-id", "173.23", null) + testWhitelistFilter(whitelist.build(), all, filtered) + } + + @Test + fun `test filter build from with build long the same`() { + val first = newEvent("recorder-id", "first", build = "173.23.435") + + val all = ArrayList() + all.add(first) + val filtered = ArrayList() + filtered.add(first) + + val whitelist = WhitelistBuilder() + whitelist.addBuild("recorder-id", "173.23.435", null) + testWhitelistFilter(whitelist.build(), all, filtered) + } + + @Test + fun `test filter build from with short build and bugfix build after`() { + val first = newEvent("recorder-id", "first", build = "173.232.1") + + val all = ArrayList() + all.add(first) + val filtered = ArrayList() + filtered.add(first) + + val whitelist = WhitelistBuilder() + whitelist.addBuild("recorder-id", "173.232", null) + testWhitelistFilter(whitelist.build(), all, filtered) + } + + @Test + fun `test filter build from with no bugfix build`() { + val first = newEvent("recorder-id", "first", build = "173.232") + + val all = ArrayList() + all.add(first) + + val whitelist = WhitelistBuilder() + whitelist.addBuild("recorder-id", "173.232.1", null) + testWhitelistFilter(whitelist.build(), all, ArrayList()) + } + + @Test + fun `test filter build from with major build after`() { + val first = newEvent("recorder-id", "first", build = "173.23") + + val all = ArrayList() + all.add(first) + val filtered = ArrayList() + filtered.add(first) + + val whitelist = WhitelistBuilder() + whitelist.addBuild("recorder-id", "172.20.132", null) + testWhitelistFilter(whitelist.build(), all, filtered) + } + + @Test + fun `test filter build from with minor build after`() { + val first = newEvent("recorder-id", "first", build = "173.23") + + val all = ArrayList() + all.add(first) + val filtered = ArrayList() + filtered.add(first) + + val whitelist = WhitelistBuilder() + whitelist.addBuild("recorder-id", "173.20.132", null) + testWhitelistFilter(whitelist.build(), all, filtered) + } + + @Test + fun `test filter build from with bugfix build after`() { + val first = newEvent("recorder-id", "first", build = "173.23.15") + + val all = ArrayList() + all.add(first) + val filtered = ArrayList() + filtered.add(first) + + val whitelist = WhitelistBuilder() + whitelist.addBuild("recorder-id", "173.23.13", null) + testWhitelistFilter(whitelist.build(), all, filtered) + } + + @Test + fun `test filter build from with major build before`() { + val first = newEvent("recorder-id", "first", build = "173.23") + + val all = ArrayList() + all.add(first) + + val whitelist = WhitelistBuilder() + whitelist.addBuild("recorder-id", "181.20.132", null) + testWhitelistFilter(whitelist.build(), all, ArrayList()) + } + + @Test + fun `test filter build from with minor build before`() { + val first = newEvent("recorder-id", "first", build = "173.23") + + val all = ArrayList() + all.add(first) + + val whitelist = WhitelistBuilder() + whitelist.addBuild("recorder-id", "173.203.132", null) + testWhitelistFilter(whitelist.build(), all, ArrayList()) + } + + @Test + fun `test filter build from with bugfix build before`() { + val first = newEvent("recorder-id", "first", build = "173.23.15") + + val all = ArrayList() + all.add(first) + + val whitelist = WhitelistBuilder() + whitelist.addBuild("recorder-id", "173.23.35", null) + testWhitelistFilter(whitelist.build(), all, ArrayList()) + } + + @Test + fun `test filter build from with whitelisted snapshot build after`() { + val first = newEvent("recorder-id", "first", build = "172.340") + + val all = ArrayList() + all.add(first) + + val whitelist = WhitelistBuilder() + whitelist.addBuild("recorder-id", "181.0", null) + testWhitelistFilter(whitelist.build(), all, ArrayList()) + } + + @Test + fun `test filter build from with whitelisted snapshot build before`() { + val first = newEvent("recorder-id", "first", build = "181.34") + + val all = ArrayList() + all.add(first) + val filtered = ArrayList() + filtered.add(first) + + val whitelist = WhitelistBuilder() + whitelist.addBuild("recorder-id", "181.0", null) + testWhitelistFilter(whitelist.build(), all, filtered) + } + + @Test + fun `test filter build to with build short the same`() { + val first = newEvent("recorder-id", "first", build = "173.23") + + val all = ArrayList() + all.add(first) + + val whitelist = WhitelistBuilder() + whitelist.addBuild("recorder-id", null, "173.23") + testWhitelistFilter(whitelist.build(), all, ArrayList()) + } + + @Test + fun `test filter build to with build long the same`() { + val first = newEvent("recorder-id", "first", build = "173.23.234") + + val all = ArrayList() + all.add(first) + + val whitelist = WhitelistBuilder() + whitelist.addBuild("recorder-id", null, "173.23.234") + testWhitelistFilter(whitelist.build(), all, ArrayList()) + } + + @Test + fun `test filter build to with major build before`() { + val first = newEvent("recorder-id", "first", build = "172.22") + + val all = ArrayList() + all.add(first) + val filtered = ArrayList() + filtered.add(first) + + val whitelist = WhitelistBuilder() + whitelist.addBuild("recorder-id", null, "173.23.234") + testWhitelistFilter(whitelist.build(), all, filtered) + } + + @Test + fun `test filter build to with minor build before`() { + val first = newEvent("recorder-id", "first", build = "173.22") + + val all = ArrayList() + all.add(first) + val filtered = ArrayList() + filtered.add(first) + + val whitelist = WhitelistBuilder() + whitelist.addBuild("recorder-id", null, "173.23.234") + testWhitelistFilter(whitelist.build(), all, filtered) + } + + @Test + fun `test filter build to with bugfix build before`() { + val first = newEvent("recorder-id", "first", build = "173.23.201") + + val all = ArrayList() + all.add(first) + val filtered = ArrayList() + filtered.add(first) + + val whitelist = WhitelistBuilder() + whitelist.addBuild("recorder-id", null, "173.23.234") + testWhitelistFilter(whitelist.build(), all, filtered) + } + + @Test + fun `test filter build to with major build after`() { + val first = newEvent("recorder-id", "first", build = "183.23.201") + + val all = ArrayList() + all.add(first) + + val whitelist = WhitelistBuilder() + whitelist.addBuild("recorder-id", null, "173.23.234") + testWhitelistFilter(whitelist.build(), all, ArrayList()) + } + + @Test + fun `test filter build to with minor build after`() { + val first = newEvent("recorder-id", "first", build = "183.345.201") + + val all = ArrayList() + all.add(first) + + val whitelist = WhitelistBuilder() + whitelist.addBuild("recorder-id", null, "183.23.234") + testWhitelistFilter(whitelist.build(), all, ArrayList()) + } + + @Test + fun `test filter build to with bugfix build after`() { + val first = newEvent("recorder-id", "first", build = "183.345.201") + + val all = ArrayList() + all.add(first) + + val whitelist = WhitelistBuilder() + whitelist.addBuild("recorder-id", null, "183.345.12") + testWhitelistFilter(whitelist.build(), all, ArrayList()) + } + + @Test + fun `test filter build from and to with major build between`() { + val first = newEvent("recorder-id", "first", build = "182.345.201") + + val all = ArrayList() + all.add(first) + val filtered = ArrayList() + filtered.add(first) + + val whitelist = WhitelistBuilder() + whitelist.addBuild("recorder-id", "181.345.12", "183.345.12") + testWhitelistFilter(whitelist.build(), all, filtered) + } + + @Test + fun `test filter build from and to with minor build between`() { + val first = newEvent("recorder-id", "first", build = "183.45.201") + + val all = ArrayList() + all.add(first) + val filtered = ArrayList() + filtered.add(first) + + val whitelist = WhitelistBuilder() + whitelist.addBuild("recorder-id", "183.35.12", "183.345.12") + testWhitelistFilter(whitelist.build(), all, filtered) + } + + @Test + fun `test filter build from and to with bugfix build between`() { + val first = newEvent("recorder-id", "first", build = "183.35.21") + + val all = ArrayList() + all.add(first) + val filtered = ArrayList() + filtered.add(first) + + val whitelist = WhitelistBuilder() + whitelist.addBuild("recorder-id", "183.35.12", "183.35.120") + testWhitelistFilter(whitelist.build(), all, filtered) + } + + @Test + fun `test filter group and build from with build after`() { + val first = newEvent("recorder-id", "first", build = "183.35.21") + val second = newEvent("recorder-id-1", "first", build = "183.35.21") + + val all = ArrayList() + all.add(first) + all.add(second) + val filtered = ArrayList() + filtered.add(first) + + val whitelist = WhitelistBuilder() + whitelist.addBuild("recorder-id", "183.35.12", null) + testWhitelistFilter(whitelist.build(), all, filtered) + } + + @Test + fun `test filter group and build from with build before and after`() { + val first = newEvent("recorder-id", "first", build = "183.35.21") + val second = newEvent("recorder-id-1", "first", build = "183.35.21") + + val all = ArrayList() + all.add(first) + all.add(second) + val filtered = ArrayList() + filtered.add(first) + + val whitelist = WhitelistBuilder() + whitelist.addBuild("recorder-id", "183.35.12", null) + whitelist.addBuild("recorder-id-1", "183.35.32", null) + testWhitelistFilter(whitelist.build(), all, filtered) + } + + @Test + fun `test filter group and build from with both build before`() { + val first = newEvent("recorder-id", "first", build = "183.35.21") + val second = newEvent("recorder-id-1", "first", build = "183.35.21") + + val all = ArrayList() + all.add(first) + all.add(second) + val filtered = ArrayList() + filtered.add(first) + filtered.add(second) + + val whitelist = WhitelistBuilder() + whitelist.addBuild("recorder-id", "183.35.12", null) + whitelist.addBuild("recorder-id-1", "181.35.32", null) + testWhitelistFilter(whitelist.build(), all, filtered) + } + + @Test + fun `test filter group and build from with both build after`() { + val first = newEvent("recorder-id", "first", build = "181.35.21") + val second = newEvent("recorder-id-1", "first", build = "181.13") + + val all = ArrayList() + all.add(first) + all.add(second) + + val whitelist = WhitelistBuilder() + whitelist.addBuild("recorder-id", "183.35.12", null) + whitelist.addBuild("recorder-id-1", "181.35.32", null) + testWhitelistFilter(whitelist.build(), all, ArrayList()) + } + + @Test + fun `test filter version and build`() { + val first = newEvent("recorder-id", "first", groupVersion = "3", build = "182.312") + val second = newEvent("recorder-id", "first", groupVersion = "11", build = "181.3") + val third = newEvent("recorder-id", "first", groupVersion = "5", build = "181.123") + val forth = newEvent("recorder-id", "first", groupVersion = "4", build = "181.32") + val fifth = newEvent("recorder-id", "first", groupVersion = "3", build = "183.113.341") + val sixth = newEvent("recorder-id", "first", groupVersion = "3", build = "191.13.341") + + val all = ArrayList() + all.add(first) + all.add(second) + all.add(third) + all.add(forth) + all.add(fifth) + all.add(sixth) + + val filtered = ArrayList() + filtered.add(forth) + filtered.add(fifth) + filtered.add(sixth) + + val whitelist = WhitelistBuilder() + whitelist.addBuild("recorder-id", "181.12", "182.312") + whitelist.addBuild("recorder-id", "183.35.12", null) + whitelist.addVersion("recorder-id", 3, 5) + whitelist.addVersion("recorder-id", 11, Int.MAX_VALUE) + testWhitelistFilter(whitelist.build(), all, filtered) + } + + private fun testWhitelistFilter(whitelist: FUSWhitelist, all: List, filtered: List) { + testEventLogFilter(all, filtered, LogEventWhitelistFilter(whitelist)) + } + + private fun testWhitelistAndSnapshotBuildFilter(whitelist: FUSWhitelist, all: List, filtered: List) { + testEventLogFilter(all, filtered, LogEventCompositeFilter(LogEventWhitelistFilter(whitelist), LogEventSnapshotBuildFilter)) + } + + private fun testSnapshotBuilderFilter(all: List, filtered: List) { + testEventLogFilter(all, filtered, LogEventSnapshotBuildFilter) + } + + private fun testEventLogFilter(all: List, filtered: List, filter: LogEventFilter) { + val records = ArrayList() + if (filtered.isNotEmpty()) { + records.add(LogEventRecord(filtered)) + } + val expected = LogEventRecordRequest("recorder-id", "IU", "user-id", records, false) + + val log = FileUtil.createTempFile("feature-event-log", ".log") + try { + val out = StringBuilder() + for (event in all) { + out.append(LogEventSerializer.toString(event)).append("\n") + } + FileUtil.writeToFile(log, out.toString()) + val actual = LogEventRecordRequest.create(log, "recorder-id", "IU", "user-id", 600, filter, false) + assertEquals(expected, actual) + } + finally { + FileUtil.delete(log) + } + } +} \ No newline at end of file diff --git a/platform/platform-tests/testSrc/com/intellij/internal/statistics/FeatureStatisticsWhitelistTest.kt b/platform/platform-tests/testSrc/com/intellij/internal/statistics/whitelist/StatisticsFilterGroupByBuildTest.kt similarity index 84% rename from platform/platform-tests/testSrc/com/intellij/internal/statistics/FeatureStatisticsWhitelistTest.kt rename to platform/platform-tests/testSrc/com/intellij/internal/statistics/whitelist/StatisticsFilterGroupByBuildTest.kt index 6d2ed84fe8bf..af6e2ee2a011 100644 --- a/platform/platform-tests/testSrc/com/intellij/internal/statistics/FeatureStatisticsWhitelistTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/internal/statistics/whitelist/StatisticsFilterGroupByBuildTest.kt @@ -1,19 +1,25 @@ -// 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. -package com.intellij.internal.statistics +// 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. +package com.intellij.internal.statistics.whitelist import com.intellij.internal.statistic.service.fus.FUStatisticsWhiteListGroupsService -import com.intellij.openapi.util.BuildNumber +import org.junit.Assert import org.junit.Test -import kotlin.test.assertEquals +import kotlin.test.assertFalse import kotlin.test.assertTrue -class FeatureStatisticsWhitelistTest { +class StatisticsFilterGroupByBuildTest { - private fun doTest(content: String, build: String, vararg expected: String) { - val actual = FUStatisticsWhiteListGroupsService.parseApprovedGroups(content, BuildNumber.fromString(build)) - assertEquals(expected.size, actual.size) + private fun doTestAccepted(content: String, build: String, vararg expected: String) { + val actual = FUStatisticsWhiteListGroupsService.parseApprovedGroups(content) for (e in expected) { - assertTrue(actual.accepts(e, 4)) + Assert.assertTrue(actual.accepts(e, "4", build)) + } + } + + private fun doTestRejected(content: String, build: String, vararg expected: String) { + val actual = FUStatisticsWhiteListGroupsService.parseApprovedGroups(content) + for (e in expected) { + Assert.assertFalse(actual.accepts(e, "4", build)) } } @@ -38,7 +44,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "IU-173.4284.118", "test.group.id") + doTestAccepted(content, "IU-173.4284.118", "test.group.id") } @Test @@ -61,7 +67,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "IU-173.4284.118", "test.group.id") + doTestAccepted(content, "IU-173.4284.118", "test.group.id") } @Test @@ -81,7 +87,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "IU-173.4284.118", "test.group.id") + doTestAccepted(content, "IU-173.4284.118", "test.group.id") } @Test @@ -101,7 +107,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "IU-173.4284.128", "test.group.id") + doTestAccepted(content, "IU-173.4284.128", "test.group.id") } @Test @@ -121,7 +127,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "IU-173.4285.118", "test.group.id") + doTestAccepted(content, "IU-173.4285.118", "test.group.id") } @Test @@ -141,7 +147,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "IU-173.4285", "test.group.id") + doTestAccepted(content, "IU-173.4285", "test.group.id") } @Test @@ -161,7 +167,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "IU-182.4280.118", "test.group.id") + doTestAccepted(content, "IU-182.4280.118", "test.group.id") } @Test @@ -181,7 +187,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "IU-182.4280", "test.group.id") + doTestAccepted(content, "IU-182.4280", "test.group.id") } @Test @@ -201,7 +207,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "IU-182", "test.group.id") + doTestAccepted(content, "IU-182", "test.group.id") } @Test @@ -221,7 +227,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "IU-173.4284.10") + doTestRejected(content, "IU-173.4284.10", "test.group.id") } @Test @@ -241,7 +247,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "IU-173.428.118") + doTestRejected(content, "IU-173.428.118", "test.group.id") } @Test @@ -261,7 +267,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "IU-173.428") + doTestRejected(content, "IU-173.428", "test.group.id") } @Test @@ -281,7 +287,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "IU-172.4284.118") + doTestRejected(content, "IU-172.4284.118", "test.group.id") } @Test @@ -301,7 +307,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "IU-172.4284") + doTestRejected(content, "IU-172.4284", "test.group.id") } @Test @@ -321,7 +327,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "IU-172") + doTestRejected(content, "IU-172", "test.group.id") } @Test @@ -342,7 +348,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "IU-173.4495.123") + doTestRejected(content, "IU-173.4495.123", "test.group.id") } @Test @@ -363,7 +369,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "IU-173.4495.128") + doTestRejected(content, "IU-173.4495.128", "test.group.id") } @Test @@ -384,7 +390,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "IU-173.4595.123") + doTestRejected(content, "IU-173.4595.123", "test.group.id") } @Test @@ -405,7 +411,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "IU-173.4595") + doTestRejected(content, "IU-173.4595", "test.group.id") } @Test @@ -426,7 +432,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "IU-182.4495.123") + doTestRejected(content, "IU-182.4495.123", "test.group.id") } @Test @@ -447,7 +453,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "IU-182.4495") + doTestRejected(content, "IU-182.4495", "test.group.id") } @Test @@ -468,7 +474,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "IU-182") + doTestRejected(content, "IU-182", "test.group.id") } @Test @@ -489,7 +495,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "IU-173.4495.11", "test.group.id") + doTestAccepted(content, "IU-173.4495.11", "test.group.id") } @Test @@ -510,7 +516,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "IU-173.4384.123", "test.group.id") + doTestAccepted(content, "IU-173.4384.123", "test.group.id") } @Test @@ -531,7 +537,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "IU-173.4384", "test.group.id") + doTestAccepted(content, "IU-173.4384", "test.group.id") } @Test @@ -552,7 +558,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "IU-181.4284.118", "test.group.id") + doTestAccepted(content, "IU-181.4284.118", "test.group.id") } @Test @@ -573,7 +579,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "IU-181.4284", "test.group.id") + doTestAccepted(content, "IU-181.4284", "test.group.id") } @Test @@ -594,7 +600,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "IU-181", "test.group.id") + doTestAccepted(content, "IU-181", "test.group.id") } @Test @@ -615,7 +621,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "IU-173.1428.118", "test.group.id") + doTestAccepted(content, "IU-173.1428.118", "test.group.id") } @Test @@ -635,7 +641,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "IU-173.1428.118", "test.group.id") + doTestAccepted(content, "IU-173.1428.118", "test.group.id") } @Test @@ -656,7 +662,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "IU-183.495.123", "test.group.id") + doTestAccepted(content, "IU-183.495.123", "test.group.id") } @Test @@ -676,7 +682,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "IU-183.495.123", "test.group.id") + doTestAccepted(content, "IU-183.495.123", "test.group.id") } @Test @@ -696,7 +702,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "PS-183.2495", "test.group.id") + doTestAccepted(content, "PS-183.2495", "test.group.id") } @Test @@ -716,7 +722,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "183.2495", "test.group.id") + doTestAccepted(content, "183.2495", "test.group.id") } @Test @@ -736,7 +742,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "183.1495", "test.group.id") + doTestAccepted(content, "183.1495", "test.group.id") } @Test @@ -756,7 +762,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "183.1495.245", "test.group.id") + doTestAccepted(content, "183.1495.245", "test.group.id") } @Test @@ -776,7 +782,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "183.1495") + doTestRejected(content, "183.1495", "test.group.id") } @Test @@ -796,7 +802,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "183.1495", "test.group.id") + doTestAccepted(content, "183.1495", "test.group.id") } @Test @@ -816,7 +822,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "183.1495.0", "test.group.id") + doTestAccepted(content, "183.1495.0", "test.group.id") } @Test @@ -836,7 +842,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "183.1495.12", "test.group.id") + doTestAccepted(content, "183.1495.12", "test.group.id") } @Test @@ -856,7 +862,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "183.1495") + doTestRejected(content, "183.1495", "test.group.id") } @Test @@ -877,7 +883,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "183.4885") + doTestRejected(content, "183.4885", "test.group.id") } @Test @@ -898,7 +904,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "183.4885.35") + doTestRejected(content, "183.4885.35", "test.group.id") } @Test @@ -919,7 +925,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "183.4884.35", "test.group.id") + doTestAccepted(content, "183.4884.35", "test.group.id") } @Test @@ -940,7 +946,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "183.4884", "test.group.id") + doTestAccepted(content, "183.4884", "test.group.id") } @Test @@ -960,7 +966,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "IC-191.SNAPSHOT", "test.group.id") + doTestAccepted(content, "IC-191.SNAPSHOT", "test.group.id") } @Test @@ -980,7 +986,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "IC-183.0") + doTestRejected(content, "IC-183.0", "test.group.id") } @Test @@ -1000,7 +1006,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "IC-181.0") + doTestRejected(content, "IC-181.0", "test.group.id") } @Test @@ -1020,7 +1026,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "PY-191.0", "test.group.id") + doTestAccepted(content, "PY-191.0", "test.group.id") } @Test @@ -1041,7 +1047,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "PY-191.0") + doTestRejected(content, "PY-191.0", "test.group.id") } @Test @@ -1062,7 +1068,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "PY-191.0", "test.group.id") + doTestAccepted(content, "PY-191.0", "test.group.id") } @Test @@ -1083,7 +1089,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "PY-191.0") + doTestRejected(content, "PY-191.0", "test.group.id") } @Test @@ -1104,7 +1110,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "PY-183.0") + doTestRejected(content, "PY-183.0", "test.group.id") } @Test @@ -1128,7 +1134,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "PY-182.2435", "test.group.id") + doTestAccepted(content, "PY-182.2435", "test.group.id") } @Test @@ -1155,7 +1161,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "183.200", "test.group.id") + doTestAccepted(content, "183.200", "test.group.id") } @Test @@ -1182,7 +1188,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "183.2200", "test.group.id") + doTestAccepted(content, "183.2200", "test.group.id") } @Test @@ -1209,7 +1215,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "183.3421") + doTestRejected(content, "183.3421", "test.group.id") } @Test @@ -1236,7 +1242,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "182.421.123") + doTestRejected(content, "182.421.123", "test.group.id") } @Test @@ -1263,7 +1269,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "183.45.12") + doTestRejected(content, "183.45.12", "test.group.id") } @Test @@ -1289,7 +1295,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "183.2145", "test.group.id") + doTestAccepted(content, "183.2145", "test.group.id") } @Test @@ -1314,7 +1320,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "182.2145", "test.group.id") + doTestAccepted(content, "182.2145", "test.group.id") } @Test @@ -1335,7 +1341,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "183.1145") + doTestRejected(content, "183.1145", "test.group.id") } @Test @@ -1367,7 +1373,8 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "183.1456.23", "test.group.id") + doTestAccepted(content, "183.1456.23", "test.group.id") + doTestRejected(content, "183.1456.23", "second.test.group.id") } @Test @@ -1399,7 +1406,8 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "182.1056", "second.test.group.id") + doTestAccepted(content, "182.1056", "second.test.group.id") + doTestRejected(content, "182.1056", "test.group.id") } @Test @@ -1431,7 +1439,7 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "183.1056", "test.group.id", "second.test.group.id") + doTestAccepted(content, "183.1056", "test.group.id", "second.test.group.id") } @Test @@ -1463,6 +1471,6 @@ class FeatureStatisticsWhitelistTest { }] } """ - doTest(content, "182.56") + doTestRejected(content, "182.56", "test.group.id", "second.test.group.id") } } \ No newline at end of file diff --git a/platform/platform-tests/testSrc/com/intellij/internal/statistics/whitelist/StatisticsParseWhitelistWithBuildTest.kt b/platform/platform-tests/testSrc/com/intellij/internal/statistics/whitelist/StatisticsParseWhitelistWithBuildTest.kt new file mode 100644 index 000000000000..12090ea20acb --- /dev/null +++ b/platform/platform-tests/testSrc/com/intellij/internal/statistics/whitelist/StatisticsParseWhitelistWithBuildTest.kt @@ -0,0 +1,616 @@ +// 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. +package com.intellij.internal.statistics.whitelist + +import com.intellij.internal.statistic.service.fus.FUSWhitelist +import com.intellij.internal.statistic.service.fus.FUStatisticsWhiteListGroupsService +import com.intellij.internal.statistics.WhitelistBuilder +import com.intellij.openapi.util.BuildNumber +import org.junit.Test +import kotlin.test.assertEquals + +class StatisticsParseWhitelistWithBuildTest { + + private fun doTest(content: String, expected: FUSWhitelist) { + val actual = FUStatisticsWhiteListGroupsService.parseApprovedGroups(content) + assertEquals(expected.size, actual.size) + assertEquals(expected, actual) + } + + private fun newBuild(vararg args: Int): BuildNumber { + return BuildNumber("", *args) + } + + @Test + fun `with one build with from`() { + val content = """ +{ + "groups" : [{ + "id" : "test.group.id", + "title" : "Test Group", + "description" : "Test group description", + "type" : "counter", + "builds" : [ { + "from" : "173.4284.118" + }], + "context" : { + } + }] +} + """ + val whitelist = WhitelistBuilder(). + addBuild("test.group.id", newBuild(173, 4284, 118), null) + doTest(content, whitelist.build()) + } + + @Test + fun `with one build with to`() { + val content = """ +{ + "groups" : [{ + "id" : "test.group.id", + "title" : "Test Group", + "description" : "Test group description", + "type" : "counter", + "builds" : [ { + "to" : "173.4284.118" + }], + "context" : { + } + }] +} + """ + val whitelist = WhitelistBuilder(). + addBuild("test.group.id", null, newBuild(173, 4284, 118)) + doTest(content, whitelist.build()) + } + + @Test + fun `with one build from and to`() { + val content = """ +{ + "groups" : [{ + "id" : "test.group.id", + "title" : "Test Group", + "description" : "Test group description", + "type" : "counter", + "builds" : [ { + "from" : "173.4284.118", + "to" : "181.231" + }], + "context" : { + } + }] +} + """ + val whitelist = WhitelistBuilder(). + addBuild("test.group.id", newBuild(173, 4284, 118), newBuild(181, 231)) + doTest(content, whitelist.build()) + } + + @Test + fun `with one build from snapshot and to`() { + val content = """ +{ + "groups" : [{ + "id" : "test.group.id", + "title" : "Test Group", + "description" : "Test group description", + "type" : "counter", + "builds" : [ { + "from" : "173.0", + "to" : "181.231" + }], + "context" : { + } + }] +} + """ + val whitelist = WhitelistBuilder(). + addBuild("test.group.id", newBuild(173, 0), newBuild(181, 231)) + doTest(content, whitelist.build()) + } + + @Test + fun `with one build from and to snapshot`() { + val content = """ +{ + "groups" : [{ + "id" : "test.group.id", + "title" : "Test Group", + "description" : "Test group description", + "type" : "counter", + "builds" : [ { + "from" : "173.4284.118", + "to" : "181.0" + }], + "context" : { + } + }] +} + """ + val whitelist = WhitelistBuilder(). + addBuild("test.group.id", newBuild(173, 4284, 118), newBuild(181, 0)) + doTest(content, whitelist.build()) + } + + @Test + fun `with one build from snapshot and to snapshot`() { + val content = """ +{ + "groups" : [{ + "id" : "test.group.id", + "title" : "Test Group", + "description" : "Test group description", + "type" : "counter", + "builds" : [ { + "from" : "173.0", + "to" : "181.0" + }], + "context" : { + } + }] +} + """ + val whitelist = WhitelistBuilder(). + addBuild("test.group.id", newBuild(173, 0), newBuild(181, 0)) + doTest(content, whitelist.build()) + } + + @Test + fun `with one number in from`() { + val content = """ +{ + "groups" : [{ + "id" : "test.group.id", + "title" : "Test Group", + "description" : "Test group description", + "type" : "counter", + "builds" : [ { + "from" : "173" + }], + "context" : { + } + }] +} + """ + val whitelist = WhitelistBuilder(). + addBuild("test.group.id", newBuild(173, 0), null) + doTest(content, whitelist.build()) + } + + @Test + fun `with both from and to and one number in from`() { + val content = """ +{ + "groups" : [{ + "id" : "test.group.id", + "title" : "Test Group", + "description" : "Test group description", + "type" : "counter", + "builds" : [ { + "from" : "173", + "to" : "182.31.3" + }], + "context" : { + } + }] +} + """ + val whitelist = WhitelistBuilder(). + addBuild("test.group.id", newBuild(173, 0), newBuild(182, 31, 3)) + doTest(content, whitelist.build()) + } + + @Test + fun `with one number in to`() { + val content = """ +{ + "groups" : [{ + "id" : "test.group.id", + "title" : "Test Group", + "description" : "Test group description", + "type" : "counter", + "builds" : [ { + "to" : "183" + }], + "context" : { + } + }] +} + """ + val whitelist = WhitelistBuilder(). + addBuild("test.group.id", null, newBuild(183, 0)) + doTest(content, whitelist.build()) + } + + @Test + fun `with both from and to and one number in to`() { + val content = """ +{ + "groups" : [{ + "id" : "test.group.id", + "title" : "Test Group", + "description" : "Test group description", + "type" : "counter", + "builds" : [ { + "from" : "173.332", + "to" : "182.0" + }], + "context" : { + } + }] +} + """ + val whitelist = WhitelistBuilder(). + addBuild("test.group.id", newBuild(173, 332), newBuild(182, 0)) + doTest(content, whitelist.build()) + } + + @Test + fun `with one number in from and to`() { + val content = """ +{ + "groups" : [{ + "id" : "test.group.id", + "title" : "Test Group", + "description" : "Test group description", + "type" : "counter", + "builds" : [ { + "from" : "12", + "to" : "183" + }], + "context" : { + } + }] +} + """ + val whitelist = WhitelistBuilder(). + addBuild("test.group.id", newBuild(12, 0), newBuild(183, 0)) + doTest(content, whitelist.build()) + } + + @Test + fun `with both from and to and negative from`() { + val content = """ +{ + "groups" : [{ + "id" : "test.group.id", + "title" : "Test Group", + "description" : "Test group description", + "type" : "counter", + "builds" : [ { + "from" : "-12", + "to" : "183.23" + }], + "context" : { + } + }] +} + """ + val whitelist = WhitelistBuilder(). + addBuild("test.group.id", newBuild(-12, 0), newBuild(183, 23)) + doTest(content, whitelist.build()) + } + + @Test + fun `with both from and to and negative to`() { + val content = """ +{ + "groups" : [{ + "id" : "test.group.id", + "title" : "Test Group", + "description" : "Test group description", + "type" : "counter", + "builds" : [ { + "from" : "12.2351.123", + "to" : "-183.23" + }], + "context" : { + } + }] +} + """ + val whitelist = WhitelistBuilder(). + addBuild("test.group.id", newBuild(12, 2351, 123), newBuild(-183, 23)) + doTest(content, whitelist.build()) + } + + @Test + fun `with two build ranges with first from`() { + val content = """ +{ + "groups" : [{ + "id" : "test.group.id", + "title" : "Test Group", + "description" : "Test group description", + "type" : "counter", + "builds" : [ { + "from" : "173.4284.118" + },{ + "from" : "182.421", + "to" : "183.5.1" + }], + "context" : { + } + }] +} + """ + val whitelist = WhitelistBuilder(). + addBuild("test.group.id", newBuild(173, 4284, 118), null). + addBuild("test.group.id", newBuild(182, 421), newBuild(183, 5, 1)) + doTest(content, whitelist.build()) + } + + @Test + fun `with two build ranges with second from`() { + val content = """ +{ + "groups" : [{ + "id" : "test.group.id", + "title" : "Test Group", + "description" : "Test group description", + "type" : "counter", + "builds" : [ { + "from" : "173.4284.118", + "to" : "181.231" + },{ + "from" : "182.421" + }], + "context" : { + } + }] +} + """ + val whitelist = WhitelistBuilder(). + addBuild("test.group.id", newBuild(173, 4284, 118), newBuild(181, 231)). + addBuild("test.group.id", newBuild(182, 421), null) + doTest(content, whitelist.build()) + } + + @Test + fun `with two build ranges with first to`() { + val content = """ +{ + "groups" : [{ + "id" : "test.group.id", + "title" : "Test Group", + "description" : "Test group description", + "type" : "counter", + "builds" : [ { + "to" : "181.231" + },{ + "from" : "182.421", + "to" : "183.5.1" + }], + "context" : { + } + }] +} + """ + val whitelist = WhitelistBuilder(). + addBuild("test.group.id", null, newBuild(181, 231)). + addBuild("test.group.id", newBuild(182, 421), newBuild(183, 5, 1)) + doTest(content, whitelist.build()) + } + + @Test + fun `with two build ranges with second to`() { + val content = """ +{ + "groups" : [{ + "id" : "test.group.id", + "title" : "Test Group", + "description" : "Test group description", + "type" : "counter", + "builds" : [ { + "from" : "173.4284.118", + "to" : "181.231" + },{ + "to" : "183.5.1" + }], + "context" : { + } + }] +} + """ + val whitelist = WhitelistBuilder(). + addBuild("test.group.id", newBuild(173, 4284, 118), newBuild(181, 231)). + addBuild("test.group.id", null, newBuild(183, 5, 1)) + doTest(content, whitelist.build()) + } + + @Test + fun `with two build ranges with from and to`() { + val content = """ +{ + "groups" : [{ + "id" : "test.group.id", + "title" : "Test Group", + "description" : "Test group description", + "type" : "counter", + "builds" : [ { + "from" : "173.4284.118", + "to" : "181.231" + },{ + "from" : "182.421", + "to" : "183.5.1" + }], + "context" : { + } + }] +} + """ + val whitelist = WhitelistBuilder(). + addBuild("test.group.id", newBuild(173, 4284, 118), newBuild(181, 231)). + addBuild("test.group.id", newBuild(182, 421), newBuild(183, 5, 1)) + doTest(content, whitelist.build()) + } + + @Test + fun `with build and version ranges`() { + val content = """ +{ + "groups" : [{ + "id" : "test.group.id", + "title" : "Test Group", + "description" : "Test group description", + "type" : "counter", + "builds" : [ { + "from" : "173.4284.118", + "to" : "181.231" + }], + "versions" : [{ + "from" : "10", + "to" : "15" + }], + "context" : { + } + }] +} + """ + val whitelist = WhitelistBuilder(). + addVersion("test.group.id", 10, 15). + addBuild("test.group.id", newBuild(173, 4284, 118), newBuild(181, 231)) + doTest(content, whitelist.build()) + } + + @Test + fun `with multiple build and version ranges`() { + val content = """ +{ + "groups" : [{ + "id" : "test.group.id", + "title" : "Test Group", + "description" : "Test group description", + "type" : "counter", + "builds" : [ { + "from" : "173.4284.118", + "to" : "181.231" + },{ + "from" : "182.421", + "to" : "183.5.1" + }], + "versions" : [ { + "from" : "2", + "to" : "5" + },{ + "from" : "10", + "to" : "15" + }], + "context" : { + } + }] +} + """ + val whitelist = WhitelistBuilder(). + addVersion("test.group.id", 2, 5). + addVersion("test.group.id", 10, 15). + addBuild("test.group.id", newBuild(173, 4284, 118), newBuild(181, 231)). + addBuild("test.group.id", newBuild(182, 421), newBuild(183, 5, 1)) + doTest(content, whitelist.build()) + } + + @Test + fun `with build and empty version ranges`() { + val content = """ +{ + "groups" : [{ + "id" : "test.group.id", + "title" : "Test Group", + "description" : "Test group description", + "type" : "counter", + "builds" : [ { + "from" : "173.4284.118", + "to" : "181.231" + }], + "versions" : [], + "context" : { + } + }] +} + """ + val whitelist = WhitelistBuilder(). + addBuild("test.group.id", newBuild(173, 4284, 118), newBuild(181, 231)) + doTest(content, whitelist.build()) + } + + @Test + fun `with multiple build and empty version ranges`() { + val content = """ +{ + "groups" : [{ + "id" : "test.group.id", + "title" : "Test Group", + "description" : "Test group description", + "type" : "counter", + "builds" : [ { + "from" : "173.4284.118", + "to" : "181.231" + },{ + "from" : "182.421", + "to" : "183.5.1" + }], + "versions" : [], + "context" : { + } + }] +} + """ + val whitelist = WhitelistBuilder(). + addBuild("test.group.id", newBuild(173, 4284, 118), newBuild(181, 231)). + addBuild("test.group.id", newBuild(182, 421), newBuild(183, 5, 1)) + doTest(content, whitelist.build()) + } + + @Test + fun `with multiple version and empty build ranges`() { + val content = """ +{ + "groups" : [{ + "id" : "test.group.id", + "title" : "Test Group", + "description" : "Test group description", + "type" : "counter", + "builds" : [], + "versions" : [ { + "from" : "2", + "to" : "5" + },{ + "from" : "10", + "to" : "15" + }], + "context" : { + } + }] +} + """ + val whitelist = WhitelistBuilder(). + addVersion("test.group.id", 2, 5). + addVersion("test.group.id", 10, 15) + doTest(content, whitelist.build()) + } + + @Test + fun `with version and empty build ranges`() { + val content = """ +{ + "groups" : [{ + "id" : "test.group.id", + "title" : "Test Group", + "description" : "Test group description", + "type" : "counter", + "builds" : [], + "versions" : [ { + "from" : "2", + "to" : "5" + }], + "context" : { + } + }] +} + """ + val whitelist = WhitelistBuilder(). + addVersion("test.group.id", 2, 5) + doTest(content, whitelist.build()) + } +} \ No newline at end of file diff --git a/platform/platform-tests/testSrc/com/intellij/internal/statistics/FeatureStatisticsWhitelistWithVersionTest.kt b/platform/platform-tests/testSrc/com/intellij/internal/statistics/whitelist/StatisticsParseWhitelistWithVersionTest.kt similarity index 69% rename from platform/platform-tests/testSrc/com/intellij/internal/statistics/FeatureStatisticsWhitelistWithVersionTest.kt rename to platform/platform-tests/testSrc/com/intellij/internal/statistics/whitelist/StatisticsParseWhitelistWithVersionTest.kt index 795dd50723f8..c0a19718498e 100644 --- a/platform/platform-tests/testSrc/com/intellij/internal/statistics/FeatureStatisticsWhitelistWithVersionTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/internal/statistics/whitelist/StatisticsParseWhitelistWithVersionTest.kt @@ -1,25 +1,23 @@ // 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. -package com.intellij.internal.statistics +package com.intellij.internal.statistics.whitelist import com.intellij.internal.statistic.service.fus.FUSWhitelist import com.intellij.internal.statistic.service.fus.FUStatisticsWhiteListGroupsService +import com.intellij.internal.statistics.WhitelistBuilder import com.intellij.openapi.util.BuildNumber import org.junit.Test import kotlin.test.assertEquals -class FeatureStatisticsWhitelistWithVersionTest { +class StatisticsParseWhitelistWithVersionTest { private fun doTest(content: String, expected: FUSWhitelist) { - val actual = FUStatisticsWhiteListGroupsService.parseApprovedGroups(content, BuildNumber.fromString("191.0")) + val actual = FUStatisticsWhiteListGroupsService.parseApprovedGroups(content) assertEquals(expected.size, actual.size) assertEquals(expected, actual) } - private fun newVersion(from: Int, to: Int): FUSWhitelist.VersionRange { - val range = FUSWhitelist.VersionRange() - range.from = from - range.to = to - return range + private fun newBuild(vararg args: Int): BuildNumber { + return BuildNumber("", *args) } @Test @@ -43,7 +41,10 @@ class FeatureStatisticsWhitelistWithVersionTest { }] } """ - doTest(content, WhitelistBuilder().add("test.group.id", newVersion(3, 5)).build()) + val whitelist = WhitelistBuilder(). + addVersion("test.group.id", 3, 5). + addBuild("test.group.id", newBuild(173, 4284, 118), null) + doTest(content, whitelist.build()) } @Test @@ -66,7 +67,10 @@ class FeatureStatisticsWhitelistWithVersionTest { }] } """ - doTest(content, WhitelistBuilder().add("test.group.id", newVersion(3, Int.MAX_VALUE)).build()) + val whitelist = WhitelistBuilder(). + addVersion("test.group.id", 3, Int.MAX_VALUE). + addBuild("test.group.id", newBuild(173, 4284, 118), null) + doTest(content, whitelist.build()) } @Test @@ -89,7 +93,10 @@ class FeatureStatisticsWhitelistWithVersionTest { }] } """ - doTest(content, WhitelistBuilder().add("test.group.id", newVersion(0, 13)).build()) + val whitelist = WhitelistBuilder(). + addVersion("test.group.id", 0, 13). + addBuild("test.group.id", newBuild(173, 4284, 118), null) + doTest(content, whitelist.build()) } @Test @@ -111,7 +118,10 @@ class FeatureStatisticsWhitelistWithVersionTest { }] } """ - doTest(content, WhitelistBuilder().add("test.group.id", newVersion(0, Int.MAX_VALUE)).build()) + val whitelist = WhitelistBuilder(). + addVersion("test.group.id", 0, Int.MAX_VALUE). + addBuild("test.group.id", newBuild(173, 4284, 118), null) + doTest(content, whitelist.build()) } @Test @@ -132,7 +142,9 @@ class FeatureStatisticsWhitelistWithVersionTest { }] } """ - doTest(content, WhitelistBuilder().add("test.group.id").build()) + val whitelist = WhitelistBuilder(). + addBuild("test.group.id", newBuild(173, 4284, 118), null) + doTest(content, whitelist.build()) } @Test @@ -159,7 +171,11 @@ class FeatureStatisticsWhitelistWithVersionTest { }] } """ - doTest(content, WhitelistBuilder().add("test.group.id", newVersion(1, 5), newVersion(6, 7)).build()) + val whitelist = WhitelistBuilder(). + addVersion("test.group.id", 1, 5). + addVersion("test.group.id", 6, 7). + addBuild("test.group.id", newBuild(173, 4284, 118), null) + doTest(content, whitelist.build()) } @Test @@ -186,7 +202,11 @@ class FeatureStatisticsWhitelistWithVersionTest { }] } """ - doTest(content, WhitelistBuilder().add("test.group.id", newVersion(1, 8), newVersion(6, 7)).build()) + val whitelist = WhitelistBuilder(). + addVersion("test.group.id", 1, 8). + addVersion("test.group.id", 6, 7). + addBuild("test.group.id", newBuild(173, 4284, 118), null) + doTest(content, whitelist.build()) } @Test @@ -211,7 +231,11 @@ class FeatureStatisticsWhitelistWithVersionTest { }] } """ - doTest(content, WhitelistBuilder().add("test.group.id", newVersion(0, 8), newVersion(8, Int.MAX_VALUE)).build()) + val whitelist = WhitelistBuilder(). + addVersion("test.group.id", 0, 8). + addVersion("test.group.id", 8, Int.MAX_VALUE). + addBuild("test.group.id", newBuild(173, 4284, 118), null) + doTest(content, whitelist.build()) } @Test @@ -235,7 +259,10 @@ class FeatureStatisticsWhitelistWithVersionTest { }] } """ - doTest(content, WhitelistBuilder().add("test.group.id", newVersion(13, 8)).build()) + val whitelist = WhitelistBuilder(). + addVersion("test.group.id", 13, 8). + addBuild("test.group.id", newBuild(173, 4284, 118), null) + doTest(content, whitelist.build()) } @Test @@ -257,7 +284,9 @@ class FeatureStatisticsWhitelistWithVersionTest { }] } """ - doTest(content, WhitelistBuilder().add("test.group.id", newVersion(3, 8)).build()) + val whitelist = WhitelistBuilder(). + addVersion("test.group.id", 3, 8) + doTest(content, whitelist.build()) } @Test @@ -297,7 +326,9 @@ class FeatureStatisticsWhitelistWithVersionTest { }] } """ - doTest(content, WhitelistBuilder().add("test.group.id", newVersion(3, 8)).build()) + val whitelist = WhitelistBuilder(). + addVersion("test.group.id", 3, 8) + doTest(content, whitelist.build()) } @Test @@ -338,7 +369,10 @@ class FeatureStatisticsWhitelistWithVersionTest { }] } """ - doTest(content, WhitelistBuilder().add("test.group.id", newVersion(Int.MAX_VALUE, 5)).build()) + val whitelist = WhitelistBuilder(). + addVersion("test.group.id", Int.MAX_VALUE, 5). + addBuild("test.group.id", newBuild(173, 4284, 118), null) + doTest(content, whitelist.build()) } @Test @@ -362,7 +396,10 @@ class FeatureStatisticsWhitelistWithVersionTest { }] } """ - doTest(content, WhitelistBuilder().add("test.group.id", newVersion(3, 0)).build()) + val whitelist = WhitelistBuilder(). + addVersion("test.group.id", 3, 0). + addBuild("test.group.id", newBuild(173, 4284, 118), null) + doTest(content, whitelist.build()) } @Test @@ -386,7 +423,10 @@ class FeatureStatisticsWhitelistWithVersionTest { }] } """ - doTest(content, WhitelistBuilder().add("test.group.id", newVersion(Int.MAX_VALUE, 5)).build()) + val whitelist = WhitelistBuilder(). + addVersion("test.group.id", Int.MAX_VALUE, 5). + addBuild("test.group.id", newBuild(173, 4284, 118), null) + doTest(content, whitelist.build()) } @Test @@ -410,6 +450,9 @@ class FeatureStatisticsWhitelistWithVersionTest { }] } """ - doTest(content, WhitelistBuilder().add("test.group.id", newVersion(3, 0)).build()) + val whitelist = WhitelistBuilder(). + addVersion("test.group.id", 3, 0). + addBuild("test.group.id", newBuild(173, 4284, 118), null) + doTest(content, whitelist.build()) } } \ No newline at end of file diff --git a/platform/projectModel-api/src/com/intellij/openapi/components/ReportValue.java b/platform/projectModel-api/src/com/intellij/openapi/components/ReportValue.java new file mode 100644 index 000000000000..97fddcfc40aa --- /dev/null +++ b/platform/projectModel-api/src/com/intellij/openapi/components/ReportValue.java @@ -0,0 +1,19 @@ +// 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. +package com.intellij.openapi.components; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Indicates that an absolute value of the numerical field should be reported in statistics. + * Won't work on objects, string or enum fields. + * + * Can be used within persistent components if reportStatistics flag is enabled. + * @see State#reportStatistic() + */ +@Target({ElementType.FIELD, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ReportValue { +} diff --git a/platform/projectModel-api/src/com/intellij/openapi/components/StoragePathMacros.java b/platform/projectModel-api/src/com/intellij/openapi/components/StoragePathMacros.java index 2a10ed7c4f42..05f74fe73354 100644 --- a/platform/projectModel-api/src/com/intellij/openapi/components/StoragePathMacros.java +++ b/platform/projectModel-api/src/com/intellij/openapi/components/StoragePathMacros.java @@ -3,6 +3,7 @@ package com.intellij.openapi.components; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; /** @@ -28,6 +29,10 @@ public final class StoragePathMacros { */ public static final String CACHE_FILE = "$CACHE_FILE$"; + @ApiStatus.Experimental + @NotNull + public static final String PRODUCT_WORKSPACE_FILE = "$PRODUCT_WORKSPACE_FILE$"; + @NotNull public static final String MODULE_FILE = "$MODULE_FILE$"; diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/MatchOptions.java b/platform/structuralsearch/source/com/intellij/structuralsearch/MatchOptions.java index bbdd8a08a087..597b5de07157 100644 --- a/platform/structuralsearch/source/com/intellij/structuralsearch/MatchOptions.java +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/MatchOptions.java @@ -31,7 +31,7 @@ public class MatchOptions implements JDOMExternalizable { @NotNull private String pattern; - private String myPatternContext; + private String myPatternContextId; @NonNls private static final String TEXT_ATTRIBUTE_NAME = "text"; @NonNls private static final String LOOSE_MATCHING_ATTRIBUTE_NAME = "loose"; @@ -65,7 +65,7 @@ public class MatchOptions implements JDOMExternalizable { scopeType = options.scopeType; scopeDescriptor = options.scopeDescriptor; pattern = options.pattern; - myPatternContext = options.myPatternContext; + myPatternContextId = options.myPatternContextId; } public MatchOptions copy() { @@ -169,11 +169,11 @@ public class MatchOptions implements JDOMExternalizable { if (myFileType != null) { element.setAttribute(FILE_TYPE_ATTR_NAME, myFileType.getName()); } - if (myDialect != null) { + if (myDialect != null && (myFileType == null || myFileType.getLanguage() != myDialect)) { element.setAttribute(DIALECT_ATTR_NAME, myDialect.getID()); } - if (myPatternContext != null) { - element.setAttribute(PATTERN_CONTEXT_ATTR_NAME, myPatternContext); + if (myPatternContextId != null) { + element.setAttribute(PATTERN_CONTEXT_ATTR_NAME, myPatternContextId); } if (scope != null) { @@ -202,7 +202,7 @@ public class MatchOptions implements JDOMExternalizable { myFileType = getFileTypeByName(element.getAttributeValue(FILE_TYPE_ATTR_NAME)); myDialect = Language.findLanguageByID(element.getAttributeValue(DIALECT_ATTR_NAME)); - myPatternContext = element.getAttributeValue(PATTERN_CONTEXT_ATTR_NAME); + myPatternContextId = element.getAttributeValue(PATTERN_CONTEXT_ATTR_NAME); final String value = element.getAttributeValue(SCOPE_TYPE); scopeType = (value == null) ? null : Scopes.Type.valueOf(value); @@ -241,12 +241,12 @@ public class MatchOptions implements JDOMExternalizable { if (!variableConstraints.equals(matchOptions.variableConstraints)) return false; if (myFileType != matchOptions.myFileType) return false; if (!Objects.equals(myDialect, matchOptions.myDialect)) return false; - if (!Objects.equals(myPatternContext, matchOptions.myPatternContext)) return false; + if (!Objects.equals(myPatternContextId, matchOptions.myPatternContextId)) return false; return true; } -public int hashCode() { + public int hashCode() { int result = (looseMatching ? 1 : 0); result = 29 * result + (recursiveSearch ? 1 : 0); result = 29 * result + (caseSensitiveMatch ? 1 : 0); @@ -256,7 +256,7 @@ public int hashCode() { result = 29 * result + scope.hashCode(); if (myFileType != null) result = 29 * result + myFileType.hashCode(); if (myDialect != null) result = 29 * result + myDialect.hashCode(); - if (myPatternContext != null) result = 29 * result + myPatternContext.hashCode(); + if (myPatternContextId != null) result = 29 * result + myPatternContextId.hashCode(); return result; } @@ -284,11 +284,12 @@ public int hashCode() { myDialect = dialect; } - public String getPatternContext() { - return myPatternContext; + public PatternContext getPatternContext() { + if (myPatternContextId == null) return null; + return StructuralSearchUtil.findPatternContextByID(myPatternContextId, getDialect()); } - public void setPatternContext(String patternContext) { - myPatternContext = patternContext; + public void setPatternContext(PatternContext patternContext) { + myPatternContextId = (patternContext == null) ? null : patternContext.getId(); } } diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/PatternContext.java b/platform/structuralsearch/source/com/intellij/structuralsearch/PatternContext.java new file mode 100644 index 000000000000..0f8eafe0c1ef --- /dev/null +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/PatternContext.java @@ -0,0 +1,52 @@ +// 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. +package com.intellij.structuralsearch; + +import org.jetbrains.annotations.NotNull; + +/** + * @author Bas Leijdekkers + */ +public final class PatternContext implements Comparable { + + public final String myID; + private final String myDisplayName; + + public PatternContext(@NotNull String ID, String displayName) { + myID = ID; + myDisplayName = displayName; + + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + final PatternContext other = (PatternContext)o; + return myID.equals(other.myID) && myDisplayName.equals(other.myDisplayName); + } + + @Override + public int hashCode() { + return 31 * myID.hashCode() + myDisplayName.hashCode(); + } + + @Override + public int compareTo(@NotNull PatternContext o) { + return myDisplayName.compareTo(o.myDisplayName); + } + + @NotNull + public String getId() { + return myID; + } + + public String getDisplayName() { + return myDisplayName; + } + + @Override + public String toString() { + return myDisplayName + " (" + myID + ')'; + } +} diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/StructuralSearchProfile.java b/platform/structuralsearch/source/com/intellij/structuralsearch/StructuralSearchProfile.java index 50ddff33fc96..6d4a10208068 100644 --- a/platform/structuralsearch/source/com/intellij/structuralsearch/StructuralSearchProfile.java +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/StructuralSearchProfile.java @@ -22,6 +22,7 @@ import com.intellij.structuralsearch.plugin.replace.impl.ReplacementBuilder; import com.intellij.structuralsearch.plugin.replace.impl.Replacer; import com.intellij.structuralsearch.plugin.ui.Configuration; import com.intellij.structuralsearch.plugin.ui.UIUtil; +import com.intellij.util.SmartList; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -36,6 +37,7 @@ import java.util.List; public abstract class StructuralSearchProfile { public static final ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.structuralsearch.profile"); + protected static final String PATTERN_PLACEHOLDER = "$$PATTERN_PLACEHOLDER$$"; public abstract void compile(PsiElement[] elements, @NotNull GlobalCompilingVisitor globalVisitor); @@ -59,13 +61,52 @@ public abstract class StructuralSearchProfile { @NotNull PatternTreeContext context, @NotNull LanguageFileType fileType, @NotNull Language language, - @Nullable String contextName, + @Nullable String contextId, @NotNull Project project, boolean physical) { - final String name = "__dummy." + fileType.getDefaultExtension(); - final PsiFile file = PsiFileFactory.getInstance(project).createFileFromText(name, language, text, physical, true); + final String strContext = getContext(text, language, contextId); + final int offset = strContext.indexOf(PATTERN_PLACEHOLDER); - return file != null ? file.getChildren() : PsiElement.EMPTY_ARRAY; + final int patternLength = text.length(); + final String patternInContext = strContext.replace(PATTERN_PLACEHOLDER, text); + + final String name = "__dummy." + fileType.getDefaultExtension(); + final PsiFile file = PsiFileFactory.getInstance(project).createFileFromText(name, language, patternInContext, physical, true); + if (file == null) { + return PsiElement.EMPTY_ARRAY; + } + + final List result = new SmartList<>(); + + PsiElement element = file.findElementAt(offset); + if (element == null) { + return PsiElement.EMPTY_ARRAY; + } + + PsiElement topElement = element; + element = element.getParent(); + + while (element != null) { + if (element.getTextRange().getStartOffset() == offset && element.getTextLength() <= patternLength) { + topElement = element; + } + element = element.getParent(); + } + + if (topElement instanceof PsiFile) { + return topElement.getChildren(); + } + + final int endOffset = offset + patternLength; + result.add(topElement); + topElement = topElement.getNextSibling(); + + while (topElement != null && topElement.getTextRange().getEndOffset() <= endOffset) { + result.add(topElement); + topElement = topElement.getNextSibling(); + } + + return result.toArray(PsiElement.EMPTY_ARRAY); } /** @@ -89,8 +130,18 @@ public abstract class StructuralSearchProfile { return createPatternTree(text, context, (LanguageFileType)fileType, language, contextName, project, physical); } + @NotNull + public List getPatternContexts() { + return Collections.emptyList(); + } + + @NotNull + protected String getContext(@NotNull String pattern, @Nullable Language language, @Nullable String contextId) { + return PATTERN_PLACEHOLDER; + } + @Nullable - public PsiCodeFragment createCodeFragment(Project project, String text) { + public PsiCodeFragment createCodeFragment(Project project, String text, String contextId) { return null; } @@ -129,7 +180,7 @@ public abstract class StructuralSearchProfile { public String getText(PsiElement match, int start, int end) { final String matchText = match.getText(); - if (start==0 && end==-1) return matchText; + if (start == 0 && end == -1) return matchText; return matchText.substring(start, end == -1 ? matchText.length() : end); } @@ -143,7 +194,7 @@ public abstract class StructuralSearchProfile { } return element.getText(); } - + public String getMeaningfulText(PsiElement element) { return getTypedVarString(element); } @@ -192,7 +243,8 @@ public abstract class StructuralSearchProfile { if (buf.length() > 0) { if (info.isArgumentContext()) { buf.append(','); - } else { + } + else { final PsiElement sibling = currentElement.getPrevSibling(); buf.append(sibling instanceof PsiWhiteSpace ? sibling.getText() : " "); } @@ -202,7 +254,8 @@ public abstract class StructuralSearchProfile { removeSemicolon = currentElement instanceof PsiComment; } replacementString = buf.toString(); - } else { + } + else { if (info.isStatementContext()) { removeSemicolon = match.getMatch() instanceof PsiComment; } @@ -211,7 +264,7 @@ public abstract class StructuralSearchProfile { offset = Replacer.insertSubstitution(result, offset, info, replacementString); if (info.isStatementContext() && - (removeSemicolon || StringUtil.endsWithChar(replacementString, ';') || StringUtil.endsWithChar(replacementString, '}'))) { + (removeSemicolon || StringUtil.endsWithChar(replacementString, ';') || StringUtil.endsWithChar(replacementString, '}'))) { final int start = info.getStartIndex() + offset; result.delete(start, start + 1); offset--; @@ -255,10 +308,10 @@ public abstract class StructuralSearchProfile { * Override this method to influence which UI controls are shown when editing the constraints of the specified variable. * * @param constraintName the name of the constraint controls for which applicability is considered. - * See {@link UIUtil} for predefined constraint names - * @param variableNode the psi element corresponding to the current variable - * @param completePattern true, if the current variableNode encompasses the complete pattern. The variableNode can also be null in this case. - * @param target true, if the current variableNode is the target of the search + * See {@link UIUtil} for predefined constraint names + * @param variableNode the psi element corresponding to the current variable + * @param completePattern true, if the current variableNode encompasses the complete pattern. The variableNode can also be null in this case. + * @param target true, if the current variableNode is the target of the search * @return true, if the requested constraint is applicable and the corresponding UI should be shown when editing the variable; false otherwise */ public boolean isApplicableConstraint(String constraintName, @Nullable PsiElement variableNode, boolean completePattern, boolean target) { @@ -267,12 +320,16 @@ public abstract class StructuralSearchProfile { if (target) return false; case UIUtil.MAXIMUM_UNLIMITED: case UIUtil.TEXT: - case UIUtil.REFERENCE: return !completePattern; + case UIUtil.REFERENCE: + return !completePattern; } return false; } - public final boolean isApplicableConstraint(String constraintName, List nodes, boolean completePattern, boolean target) { + public final boolean isApplicableConstraint(String constraintName, + List nodes, + boolean completePattern, + boolean target) { if (nodes.isEmpty()) { return isApplicableConstraint(constraintName, (PsiElement)null, completePattern, target); } diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/StructuralSearchProfileBase.java b/platform/structuralsearch/source/com/intellij/structuralsearch/StructuralSearchProfileBase.java index 734578292a83..10a1705fb2f3 100644 --- a/platform/structuralsearch/source/com/intellij/structuralsearch/StructuralSearchProfileBase.java +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/StructuralSearchProfileBase.java @@ -21,17 +21,17 @@ import com.intellij.psi.tree.TokenSet; import com.intellij.structuralsearch.impl.matcher.CompiledPattern; import com.intellij.structuralsearch.impl.matcher.GlobalMatchingVisitor; import com.intellij.structuralsearch.impl.matcher.MatchContext; -import com.intellij.structuralsearch.impl.matcher.PatternTreeContext; import com.intellij.structuralsearch.impl.matcher.compiler.GlobalCompilingVisitor; import com.intellij.structuralsearch.impl.matcher.handlers.*; import com.intellij.structuralsearch.impl.matcher.iterators.SsrFilteringNodeIterator; import com.intellij.structuralsearch.impl.matcher.strategies.MatchingStrategy; import com.intellij.structuralsearch.plugin.replace.ReplaceOptions; -import com.intellij.util.ArrayUtilRt; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.*; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; import java.util.regex.Pattern; /** @@ -39,7 +39,6 @@ import java.util.regex.Pattern; */ public abstract class StructuralSearchProfileBase extends StructuralSearchProfile { private static final String DELIMITER_CHARS = ",;.[]{}():"; - protected static final String PATTERN_PLACEHOLDER = "$$PATTERN_PLACEHOLDER$$"; @Override public void compile(PsiElement[] elements, @NotNull final GlobalCompilingVisitor globalVisitor) { @@ -179,66 +178,6 @@ public abstract class StructuralSearchProfileBase extends StructuralSearchProfil @NotNull protected abstract LanguageFileType getFileType(); - @NotNull - @Override - public PsiElement[] createPatternTree(@NotNull String text, - @NotNull PatternTreeContext context, - @NotNull LanguageFileType fileType, - @NotNull Language language, - @Nullable String contextName, - @NotNull Project project, - boolean physical) { - if (context == PatternTreeContext.Block) { - final String strContext = getContext(text, language, contextName); - if (strContext == null) { - return PsiElement.EMPTY_ARRAY; - } - final int offset = strContext.indexOf(PATTERN_PLACEHOLDER); - - final int patternLength = text.length(); - final String patternInContext = strContext.replace(PATTERN_PLACEHOLDER, text); - - final String name = "__dummy." + fileType.getDefaultExtension(); - final PsiFile file = PsiFileFactory.getInstance(project).createFileFromText(name, language, patternInContext, physical, true); - if (file == null) { - return PsiElement.EMPTY_ARRAY; - } - - final List result = new ArrayList<>(); - - PsiElement element = file.findElementAt(offset); - if (element == null) { - return PsiElement.EMPTY_ARRAY; - } - - PsiElement topElement = element; - element = element.getParent(); - - while (element != null) { - if (element.getTextRange().getStartOffset() == offset && element.getTextLength() <= patternLength) { - topElement = element; - } - element = element.getParent(); - } - - if (topElement instanceof PsiFile) { - return topElement.getChildren(); - } - - final int endOffset = offset + patternLength; - result.add(topElement); - topElement = topElement.getNextSibling(); - - while (topElement != null && topElement.getTextRange().getEndOffset() <= endOffset) { - result.add(topElement); - topElement = topElement.getNextSibling(); - } - - return result.toArray(PsiElement.EMPTY_ARRAY); - } - return super.createPatternTree(text, context, fileType, language, contextName, project, physical); - } - @Override public void checkReplacementPattern(Project project, ReplaceOptions options) {} @@ -247,16 +186,6 @@ public abstract class StructuralSearchProfileBase extends StructuralSearchProfil return new DocumentBasedReplaceHandler(project); } - @NotNull - public String[] getContextNames() { - return ArrayUtilRt.EMPTY_STRING_ARRAY; - } - - @Nullable - protected String getContext(@NotNull String pattern, @Nullable Language language, @Nullable String contextName) { - return PATTERN_PLACEHOLDER; - } - static boolean canBePatternVariable(PsiElement element) { // can be leaf element! (ex. var a = 1 <-> var $a$ = 1) if (element instanceof LeafElement) { diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/StructuralSearchUtil.java b/platform/structuralsearch/source/com/intellij/structuralsearch/StructuralSearchUtil.java index 231698d6d490..5d8f46949313 100644 --- a/platform/structuralsearch/source/com/intellij/structuralsearch/StructuralSearchUtil.java +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/StructuralSearchUtil.java @@ -225,4 +225,22 @@ public class StructuralSearchUtil { public static String normalize(@NotNull String text) { return stripAccents(normalizeWhiteSpace(text)); } + + public static PatternContext findPatternContextByID(String id, Language language) { + return findPatternContextByID(id, getProfileByLanguage(language)); + } + + public static PatternContext findPatternContextByID(String id, StructuralSearchProfile profile) { + if (profile == null) { + return null; + } + final List patternContexts = profile.getPatternContexts(); + if (patternContexts.isEmpty()) { + return null; + } + if (id == null) { + return patternContexts.get(0); + } + return patternContexts.stream().filter(context -> context.getId().equals(id)).findFirst().orElse(patternContexts.get(0)); + } } diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/XmlStructuralSearchProfile.java b/platform/structuralsearch/source/com/intellij/structuralsearch/XmlStructuralSearchProfile.java index f985de88b032..96f89001213d 100644 --- a/platform/structuralsearch/source/com/intellij/structuralsearch/XmlStructuralSearchProfile.java +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/XmlStructuralSearchProfile.java @@ -85,7 +85,7 @@ public class XmlStructuralSearchProfile extends StructuralSearchProfile { @NotNull PatternTreeContext context, @NotNull LanguageFileType fileType, @NotNull Language language, - String contextName, + String contextId, @NotNull Project project, boolean physical) { text = context == PatternTreeContext.File ? text : "" + text + ""; diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/CompiledPattern.java b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/CompiledPattern.java index 273c77ff6e31..8ab159d4d12a 100644 --- a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/CompiledPattern.java +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/CompiledPattern.java @@ -86,7 +86,7 @@ public abstract class CompiledPattern { @NotNull public String getTypedVarString(PsiElement element) { final StructuralSearchProfile profile = StructuralSearchUtil.getProfileByPsiElement(element); - String typedVarString = (profile == null) ? element.getText() : profile.getTypedVarString(element); + final String typedVarString = (profile == null) ? element.getText() : profile.getTypedVarString(element); return typedVarString.trim(); } diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/MatcherImplUtil.java b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/MatcherImplUtil.java index e6472e96027e..2408c6beb04a 100644 --- a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/MatcherImplUtil.java +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/MatcherImplUtil.java @@ -5,6 +5,7 @@ import com.intellij.lang.Language; import com.intellij.openapi.fileTypes.LanguageFileType; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElement; +import com.intellij.structuralsearch.PatternContext; import com.intellij.structuralsearch.StructuralSearchProfile; import com.intellij.structuralsearch.StructuralSearchUtil; @@ -33,7 +34,7 @@ public class MatcherImplUtil { PatternTreeContext context, LanguageFileType fileType, Language language, - String contextName, + PatternContext patternContext, Project project, boolean physical) { if (language == null) { @@ -41,7 +42,8 @@ public class MatcherImplUtil { } final StructuralSearchProfile profile = StructuralSearchUtil.getProfileByLanguage(language); if (profile != null) { - return profile.createPatternTree(text, context, fileType, language, contextName, project, physical); + final String contextId = (patternContext == null) ? null : patternContext.getId(); + return profile.createPatternTree(text, context, fileType, language, contextId, project, physical); } return PsiElement.EMPTY_ARRAY; } diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/compiler/PatternCompiler.java b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/compiler/PatternCompiler.java index 77291925ec74..94e94428c5c0 100644 --- a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/compiler/PatternCompiler.java +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/compiler/PatternCompiler.java @@ -571,7 +571,12 @@ public class PatternCompiler { } final GlobalCompilingVisitor compilingVisitor = new GlobalCompilingVisitor(); - compilingVisitor.compile(elements.toArray(PsiElement.EMPTY_ARRAY), context); + try { + compilingVisitor.compile(elements.toArray(PsiElement.EMPTY_ARRAY), context); + } + catch (MalformedPatternException e) { + if (checkForErrors) throw e; + } new DeleteNodesAction(compilingVisitor.getLexicalNodes()).run(); return elements; } diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/predicates/ScriptSupport.java b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/predicates/ScriptSupport.java index 0b67d8ff13db..e5c7bef6a9ba 100644 --- a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/predicates/ScriptSupport.java +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/predicates/ScriptSupport.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-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. package com.intellij.structuralsearch.impl.matcher.predicates; import com.intellij.openapi.diagnostic.Logger; @@ -108,7 +108,7 @@ public class ScriptSupport { throw t; } catch (Throwable t) { - Logger.getInstance(ScriptSupport.class).warn("Exception thrown by Structural Search Groovy Script", t); + Logger.getInstance(ScriptSupport.class).info("Exception thrown by Structural Search Groovy Script", t); throw new StructuralSearchScriptException(t); } finally { diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/replace/ui/ReplacementPreviewDialog.java b/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/replace/ui/ReplacementPreviewDialog.java index 9f154686ee3d..1c5b52169341 100644 --- a/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/replace/ui/ReplacementPreviewDialog.java +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/replace/ui/ReplacementPreviewDialog.java @@ -99,7 +99,7 @@ public final class ReplacementPreviewDialog extends DialogWrapper { PsiFile file = null; final StructuralSearchProfile profile = StructuralSearchUtil.getProfileByFileType(myFileType); if (profile != null) { - file = profile.createCodeFragment(project, ""); + file = profile.createCodeFragment(project, "", null); } if (file != null) { diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/EditVarConstraintsDialog.java b/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/EditVarConstraintsDialog.java index 5361763c527f..a92ace211043 100644 --- a/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/EditVarConstraintsDialog.java +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/EditVarConstraintsDialog.java @@ -574,7 +574,7 @@ class EditVarConstraintsDialog extends DialogWrapper { // there is no right way to create a code fragment for generic language, so we use this hole since we need extend resolve scope for (StructuralSearchProfile profile : StructuralSearchProfile.EP_NAME.getExtensions()) { if (profile.isMyLanguage(groovy)) { - final PsiCodeFragment fragment = Objects.requireNonNull(profile.createCodeFragment(project, text)); + final PsiCodeFragment fragment = Objects.requireNonNull(profile.createCodeFragment(project, text, null)); fragment.forceResolveScope(new StructuralSearchScriptScope(myProject)); doc = PsiDocumentManager.getInstance(project).getDocument(fragment); break; diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/FileTypeInfo.java b/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/FileTypeInfo.java index aa2d815f25f0..e5a2d758c883 100644 --- a/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/FileTypeInfo.java +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/FileTypeInfo.java @@ -4,6 +4,7 @@ package com.intellij.structuralsearch.plugin.ui; import com.intellij.lang.Language; import com.intellij.openapi.fileTypes.LanguageFileType; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.structuralsearch.PatternContext; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -21,20 +22,16 @@ public class FileTypeInfo { private final LanguageFileType myFileType; private final Language myDialect; - private final String myContext; - private final boolean myEnabled; + private final PatternContext myContext; + private final boolean myNested; private final String myDescription; - public FileTypeInfo(@NotNull LanguageFileType fileType, - @Nullable Language dialect, - @Nullable String context, - boolean enabled, - boolean duplicated) { + public FileTypeInfo(@NotNull LanguageFileType fileType, @NotNull Language dialect, @Nullable PatternContext context, boolean nested) { myFileType = fileType; myDialect = dialect; myContext = context; - myEnabled = enabled; - myDescription = getDescription(fileType, duplicated); + myNested = nested; + myDescription = getDescription(fileType); } @NotNull @@ -48,62 +45,42 @@ public class FileTypeInfo { } @Nullable - public String getContext() { + public PatternContext getContext() { return myContext; } @NotNull public String getText() { - if (myDialect != null) { - return myDialect.getDisplayName(); - } - if (myContext != null) { - return myContext + " Context"; - } - return myFileType.getName(); - } - - @NotNull - public String getSearchText() { - if (myDialect != null) { - return myDialect.getDisplayName(); - } - return myFileType.getName(); - } - - @NotNull - public String getFullText() { - if (myDialect != null) { - return myDescription + " - " + myDialect.getDisplayName(); - } - if (myContext != null) { - return myDescription + " - " + myContext + " Context"; + if (myNested) { + if (myDialect != null && myDialect != myFileType.getLanguage()) { + return myDialect.getDisplayName(); + } + if (myContext != null) { + return myDescription + " - " + myContext.getDisplayName(); + } } return myDescription; } + @NotNull + public String getSearchText() { + return (myDialect != null) ? myDialect.getDisplayName() : myFileType.getName(); + } + public boolean isNested() { - return myDialect != null || myContext != null; + return myNested; } - public boolean isEnabled() { - return myEnabled; - } - - public boolean isEqualTo(@NotNull LanguageFileType fileType, @Nullable Language dialect, @Nullable String context) { - return Objects.equals(myFileType, fileType) && - Objects.equals(myDialect, dialect) && - Objects.equals(myContext, context); + public boolean isEqualTo(@NotNull LanguageFileType fileType, @Nullable Language dialect, @Nullable PatternContext context) { + return (myFileType == fileType) + && (dialect == null || myDialect == dialect) + && (context == null || myContext == context); } @NotNull - private static String getDescription(@NotNull LanguageFileType fileType, boolean duplicated) { + private static String getDescription(@NotNull LanguageFileType fileType) { final String description = fileType.getDescription(); - final String trimmedDescription = StringUtil.capitalizeWords(CLEANUP.matcher(description).replaceAll(""), true); - if (!duplicated) { - return trimmedDescription; - } - return trimmedDescription + " (" + fileType.getName() + ")"; + return StringUtil.capitalizeWords(CLEANUP.matcher(description).replaceAll(""), true); } @Override @@ -111,9 +88,9 @@ public class FileTypeInfo { if (this == o) return true; if (!(o instanceof FileTypeInfo)) return false; final FileTypeInfo info = (FileTypeInfo)o; - return Objects.equals(myFileType, info.myFileType) && - Objects.equals(myDialect, info.myDialect) && - Objects.equals(myContext, info.myContext); + return myFileType == info.myFileType + && myDialect == info.myDialect + && myContext == info.myContext; } @Override @@ -123,6 +100,6 @@ public class FileTypeInfo { @Override public String toString() { - return getFullText(); + return getText(); } } diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/FileTypeSelector.java b/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/FileTypeSelector.java index 9be36429da62..11f6d44ae76d 100644 --- a/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/FileTypeSelector.java +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/FileTypeSelector.java @@ -3,11 +3,10 @@ package com.intellij.structuralsearch.plugin.ui; import com.intellij.lang.Language; import com.intellij.lang.LanguageUtil; -import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.fileTypes.LanguageFileType; import com.intellij.openapi.ui.ComboBox; +import com.intellij.structuralsearch.PatternContext; import com.intellij.structuralsearch.StructuralSearchProfile; -import com.intellij.structuralsearch.StructuralSearchProfileBase; import com.intellij.structuralsearch.StructuralSearchUtil; import com.intellij.ui.LayeredIcon; import com.intellij.ui.SimpleListCellRenderer; @@ -42,7 +41,9 @@ public class FileTypeSelector extends ComboBox { return info != null ? info.getFileType() : null; } - public void setSelectedItem(@NotNull LanguageFileType type, @Nullable Language dialect, @Nullable String context) { + public void setSelectedItem(@NotNull LanguageFileType type, + @Nullable Language dialect, + @Nullable PatternContext context) { final DefaultComboBoxModel model = (DefaultComboBoxModel)getModel(); for (int i = 0; i < model.getSize(); i++) { final FileTypeInfo info = model.getElementAt(i); @@ -53,23 +54,6 @@ public class FileTypeSelector extends ComboBox { } } - @Override - public void setSelectedItem(Object anObject) { - if (anObject instanceof FileTypeInfo) { - final FileTypeInfo selectedInfo = (FileTypeInfo)anObject; - if (!selectedInfo.isEnabled()) { - final MyComboBoxModel model = (MyComboBoxModel)getModel(); - final int index = model.getIndexOf(selectedInfo); - if (index >= 0 && index + 1 < model.getSize()) { - final FileTypeInfo nextInfo = model.getElementAt(index + 1); - super.setSelectedItem(nextInfo); - return; - } - } - } - super.setSelectedItem(anObject); - } - @NotNull private static DefaultComboBoxModel createModel() { final List types = new ArrayList<>(); @@ -81,30 +65,25 @@ public class FileTypeSelector extends ComboBox { Collections.sort(types, (o1, o2) -> o1.getDescription().compareToIgnoreCase(o2.getDescription())); final List infos = new ArrayList<>(); for (LanguageFileType fileType : types) { - final boolean duplicated = isDuplicated(fileType, types); - final StructuralSearchProfile profile = StructuralSearchUtil.getProfileByFileType(fileType); assert profile != null; - if (profile instanceof StructuralSearchProfileBase) { - final String[] contextNames = ((StructuralSearchProfileBase)profile).getContextNames(); - if (contextNames.length != 0) { - Arrays.sort(contextNames); - infos.add(new FileTypeInfo(fileType, null, null, false, duplicated)); - for (String contextName: contextNames) { - infos.add(new FileTypeInfo(fileType, null, contextName, true, duplicated)); - } - continue; // proceed with the next file type + final Language language = fileType.getLanguage(); + final List patternContexts = new ArrayList<>(profile.getPatternContexts()); + if (!patternContexts.isEmpty()) { + infos.add(new FileTypeInfo(fileType, language, patternContexts.get(0), false)); + for (int i = 1; i < patternContexts.size(); i++) { + infos.add(new FileTypeInfo(fileType, language, patternContexts.get(i), true)); } + continue; // proceed with the next file type } - infos.add(new FileTypeInfo(fileType, null, null, true, duplicated)); + infos.add(new FileTypeInfo(fileType, language, null, false)); - final Language language = fileType.getLanguage(); final Language[] languageDialects = LanguageUtil.getLanguageDialects(language); Arrays.sort(languageDialects, Comparator.comparing(Language::getDisplayName)); for (Language dialect : languageDialects) { if (profile.isMyLanguage(dialect)) { - infos.add(new FileTypeInfo(fileType, dialect, null, true, duplicated)); + infos.add(new FileTypeInfo(fileType, dialect, null, true)); } } } @@ -112,11 +91,6 @@ public class FileTypeSelector extends ComboBox { return new MyComboBoxModel(infos); } - private static boolean isDuplicated(@NotNull LanguageFileType fileType, @NotNull List types) { - final String description = fileType.getDescription(); - return types.stream().anyMatch(type -> type != fileType && description.equals(type.getDescription())); - } - private static class MyComboBoxModel extends DefaultComboBoxModel { MyComboBoxModel(List infos) { super(infos.toArray(FileTypeInfo.EMPTY_ARRAY)); @@ -134,14 +108,8 @@ public class FileTypeSelector extends ComboBox { if (value == null) { return; } - if (value.isNested() && index >= 0) { - setIcon(WIDE_EMPTY_ICON); - setText(value.getText()); - } - else { - setIcon(getFileTypeIcon(value)); - setText(value.getFullText()); - } + setIcon(value.isNested() && index >= 0 ? WIDE_EMPTY_ICON : getFileTypeIcon(value)); + setText(value.getText()); } @NotNull diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/SearchDialog.java b/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/SearchDialog.java index ee3da78c5dac..3c48b27f2857 100644 --- a/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/SearchDialog.java +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/SearchDialog.java @@ -79,7 +79,7 @@ public class SearchDialog extends DialogWrapper { @NonNls private LanguageFileType ourFtSearchVariant = StructuralSearchUtil.getDefaultFileType(); private static Language ourDialect = null; - private static String ourContext = null; + private static PatternContext ourContext = null; private final boolean myShowScopePanel; private final boolean myRunFindActionOnClose; diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/StructuralSearchDialog.java b/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/StructuralSearchDialog.java index 44db8ad30cf6..263250f17d9c 100644 --- a/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/StructuralSearchDialog.java +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/StructuralSearchDialog.java @@ -119,7 +119,7 @@ public class StructuralSearchDialog extends DialogWrapper { Configuration myConfiguration; @NonNls LanguageFileType myFileType = StructuralSearchUtil.getDefaultFileType(); Language myDialect = null; - String myContext = null; + PatternContext myPatternContext = null; private final List myRangeHighlighters = new SmartList<>(); // ui management @@ -193,7 +193,7 @@ public class StructuralSearchDialog extends DialogWrapper { private EditorTextField createEditor() { final StructuralSearchProfile profile = StructuralSearchUtil.getProfileByFileType(myFileType); assert profile != null; - final Document document = UIUtil.createDocument(getProject(), myFileType, myDialect, "", profile); + final Document document = UIUtil.createDocument(getProject(), myFileType, myDialect, myPatternContext, "", profile); final EditorTextField textField = new EditorTextField(document, getProject(), myFileType, false, false) { @Override @@ -273,13 +273,19 @@ public class StructuralSearchDialog extends DialogWrapper { private void initializeFilterPanel() { final MatchOptions matchOptions = getConfiguration().getMatchOptions(); final CompiledPattern compiledPattern = PatternCompiler.compilePattern(getProject(), matchOptions, false, false); - if (compiledPattern != null) { - myFilterPanel.setCompiledPattern(compiledPattern); - } - if (!myFilterPanel.isInitialized()) { - myFilterPanel.initFilters(UIUtil.getOrAddVariableConstraint(Configuration.CONTEXT_VAR_NAME, myConfiguration)); - } - myFilterPanel.setValid(compiledPattern != null); + ApplicationManager.getApplication().invokeLater(() -> { + if (compiledPattern != null) { + final SubstitutionShortInfoHandler handler = SubstitutionShortInfoHandler.retrieve(mySearchCriteriaEdit.getEditor()); + if (handler != null) { + handler.updateEditorInlays(); + } + myFilterPanel.setCompiledPattern(compiledPattern); + } + if (!myFilterPanel.isInitialized()) { + myFilterPanel.initFilters(UIUtil.getOrAddVariableConstraint(Configuration.CONTEXT_VAR_NAME, myConfiguration)); + } + myFilterPanel.setValid(compiledPattern != null); + }); } private Configuration createConfiguration(Configuration template) { @@ -494,9 +500,17 @@ public class StructuralSearchDialog extends DialogWrapper { myRecursive.setVisible(!myReplace); myMatchCase = new JCheckBox(FindBundle.message("find.popup.case.sensitive"), true); myFileType = UIUtil.detectFileType(mySearchContext); + myDialect = myFileType.getLanguage(); + final StructuralSearchProfile profile = StructuralSearchUtil.getProfileByFileType(myFileType); + if (profile != null) { + final List contexts = profile.getPatternContexts(); + if (!contexts.isEmpty()) { + myPatternContext = contexts.get(0); + } + } myFileTypesComboBox = new FileTypeSelector(); myFileTypesComboBox.setMinimumAndPreferredWidth(200); - myFileTypesComboBox.setSelectedItem(myFileType, myDialect, myContext); + myFileTypesComboBox.setSelectedItem(myFileType, myDialect, myPatternContext); myFileTypesComboBox.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { @@ -505,14 +519,14 @@ public class StructuralSearchDialog extends DialogWrapper { if (item == null) return; myFileType = item.getFileType(); myDialect = item.getDialect(); - myContext = item.getContext(); + myPatternContext = item.getContext(); final StructuralSearchProfile profile = StructuralSearchUtil.getProfileByFileType(myFileType); assert profile != null; final Document searchDocument = - UIUtil.createDocument(getProject(), myFileType, myDialect, mySearchCriteriaEdit.getText(), profile); + UIUtil.createDocument(getProject(), myFileType, myDialect, myPatternContext, mySearchCriteriaEdit.getText(), profile); mySearchCriteriaEdit.setNewDocumentAndFileType(myFileType, searchDocument); final Document replaceDocument = - UIUtil.createDocument(getProject(), myFileType, myDialect, myReplaceCriteriaEdit.getText(), profile); + UIUtil.createDocument(getProject(), myFileType, myDialect, myPatternContext, myReplaceCriteriaEdit.getText(), profile); myReplaceCriteriaEdit.setNewDocumentAndFileType(myFileType, replaceDocument); myFilterPanel.setProfile(profile); initiateValidation(); @@ -843,24 +857,26 @@ public class StructuralSearchDialog extends DialogWrapper { }); } + Balloon myBalloon = null; void reportMessage(String message, boolean error, JComponent component) { com.intellij.util.ui.UIUtil.invokeLaterIfNeeded(() -> { + if (myBalloon != null) myBalloon.hide(); component.putClientProperty("JComponent.outline", (!error || message == null) ? null : "error"); component.repaint(); if (message == null) return; - final Balloon balloon = JBPopupFactory.getInstance() + myBalloon = JBPopupFactory.getInstance() .createHtmlTextBalloonBuilder(message, error ? MessageType.ERROR : MessageType.WARNING, null) .setHideOnFrameResize(false) .createBalloon(); if (component != myScopePanel) { - balloon.show(new RelativePoint(component, new Point(component.getWidth() / 2, component.getHeight())), Balloon.Position.below); + myBalloon.show(new RelativePoint(component, new Point(component.getWidth() / 2, component.getHeight())), Balloon.Position.below); } else { - balloon.show(new RelativePoint(component, new Point(component.getWidth() / 2, 0)), Balloon.Position.above); + myBalloon.show(new RelativePoint(component, new Point(component.getWidth() / 2, 0)), Balloon.Position.above); } - balloon.showInCenterOf(component); - Disposer.register(myDisposable, balloon); + myBalloon.showInCenterOf(component); + Disposer.register(myDisposable, myBalloon); }); } @@ -1020,7 +1036,7 @@ public class StructuralSearchDialog extends DialogWrapper { } matchOptions.setFileType(myFileType); matchOptions.setDialect(myDialect); - matchOptions.setPatternContext(myContext); + matchOptions.setPatternContext(myPatternContext); matchOptions.setSearchPattern(getPattern(mySearchCriteriaEdit)); matchOptions.setCaseSensitiveMatch(myMatchCase.isSelected()); diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/SubstitutionShortInfoHandler.java b/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/SubstitutionShortInfoHandler.java index efcb91d8acdf..a8bfcaa104ff 100644 --- a/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/SubstitutionShortInfoHandler.java +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/SubstitutionShortInfoHandler.java @@ -1,13 +1,16 @@ -// 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-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. package com.intellij.structuralsearch.plugin.ui; import com.intellij.codeInsight.hint.TooltipController; import com.intellij.codeInsight.hint.TooltipGroup; +import com.intellij.codeInsight.template.Template; +import com.intellij.codeInsight.template.TemplateManager; import com.intellij.codeInsight.template.impl.TemplateImplUtil; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.LogicalPosition; +import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.event.*; +import com.intellij.openapi.editor.markup.TextAttributes; +import com.intellij.openapi.ui.GraphicsConfig; +import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; @@ -19,6 +22,7 @@ import com.intellij.structuralsearch.plugin.replace.ui.ReplaceConfiguration; import com.intellij.ui.ColorUtil; import com.intellij.ui.HintHint; import com.intellij.util.SmartList; +import com.intellij.util.ui.GraphicsUtil; import com.intellij.util.ui.JBUI; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; @@ -26,7 +30,10 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.function.Consumer; public class SubstitutionShortInfoHandler implements DocumentListener, EditorMouseMotionListener, CaretListener { @@ -37,8 +44,9 @@ public class SubstitutionShortInfoHandler implements DocumentListener, EditorMou private final Editor editor; @Nullable private final Consumer myCurrentVariableCallback; public static final Key CURRENT_CONFIGURATION_KEY = Key.create("SS.CurrentConfiguration"); + private final Map> inlays = new HashMap<>(); - SubstitutionShortInfoHandler(@NotNull Editor _editor, @Nullable Consumer currentVariableCallback) { + private SubstitutionShortInfoHandler(@NotNull Editor _editor, @Nullable Consumer currentVariableCallback) { editor = _editor; myCurrentVariableCallback = currentVariableCallback; } @@ -98,7 +106,7 @@ public class SubstitutionShortInfoHandler implements DocumentListener, EditorMou myCurrentVariableCallback.accept(Configuration.CONTEXT_VAR_NAME); } - if (variableName != null) { + if (variableName != null && !text.isEmpty()) { showTooltip(editor, start, end + 1, text); } } @@ -109,6 +117,7 @@ public class SubstitutionShortInfoHandler implements DocumentListener, EditorMou variables.clear(); variables.addAll(TemplateImplUtil.parseVariables(document.getCharsSequence()).keySet()); modificationTimeStamp = document.getModificationStamp(); + updateEditorInlays(); } } @@ -122,6 +131,7 @@ public class SubstitutionShortInfoHandler implements DocumentListener, EditorMou if (event.getOldLength() == event.getNewLength()) return; // to handle backspace & delete (backspace strangely is not reported to the caret listener) handleInputFocusMovement(editor.getCaretModel().getLogicalPosition(), true); + updateEditorInlays(); } public List getVariables() { @@ -131,9 +141,9 @@ public class SubstitutionShortInfoHandler implements DocumentListener, EditorMou @NotNull static String getShortParamString(NamedScriptableDefinition namedScriptableDefinition, boolean editLink) { - final boolean newDialog = Registry.is("ssr.use.new.search.dialog"); + final boolean verbose = !Registry.is("ssr.use.new.search.dialog"); if (namedScriptableDefinition == null) { - return SSRBundle.message(newDialog ? "no.filters.tooltip.message" : "no.constraints.specified.tooltip.message"); + return verbose ? SSRBundle.message("no.constraints.specified.tooltip.message") : ""; } final StringBuilder buf = new StringBuilder(); @@ -142,10 +152,18 @@ public class SubstitutionShortInfoHandler implements DocumentListener, EditorMou final String linkColor = ColorUtil.toHtmlColor(JBUI.CurrentTheme.Link.linkColor()); if (namedScriptableDefinition instanceof MatchVariableConstraint) { final MatchVariableConstraint constraint = (MatchVariableConstraint)namedScriptableDefinition; - if (constraint.isPartOfSearchResults() && !newDialog) { + final String name = constraint.getName(); + if (!Configuration.CONTEXT_VAR_NAME.equals(name)) { + final int maxCount = constraint.getMaxCount(); + final int minCount = constraint.getMinCount(); + if (verbose || minCount != 1 || maxCount != 1) { + append(buf, SSRBundle.message("min.occurs.tooltip.message", minCount, (maxCount == Integer.MAX_VALUE) ? "∞" : maxCount)); + } + } + if (constraint.isPartOfSearchResults() && verbose) { append(buf, SSRBundle.message("target.tooltip.message")); } - if (constraint.getRegExp() != null && !constraint.getRegExp().isEmpty()) { + if (!constraint.getRegExp().isEmpty()) { append(buf, SSRBundle.message("text.tooltip.message", constraint.isInvertRegExp() ? 1 : 0, StringUtil.escapeXmlEntities(constraint.getRegExp()), @@ -161,7 +179,6 @@ public class SubstitutionShortInfoHandler implements DocumentListener, EditorMou append(buf, SSRBundle.message("reference.target.tooltip.message", constraint.isInvertReference() ? 1 : 0, text)); } - constraint.getNameOfExprType(); if (!constraint.getNameOfExprType().isEmpty()) { append(buf, SSRBundle.message("exprtype.tooltip.message", constraint.isInvertExprType() ? 1 : 0, @@ -183,27 +200,17 @@ public class SubstitutionShortInfoHandler implements DocumentListener, EditorMou final String text = StringUtil.escapeXmlEntities(StringUtil.unquoteString(constraint.getWithinConstraint())); append(buf, SSRBundle.message("within.constraints.tooltip.message", constraint.isInvertWithinConstraint() ? 1 : 0, text)); } - - final String name = constraint.getName(); - if (!Configuration.CONTEXT_VAR_NAME.equals(name)) { - final int maxCount = constraint.getMaxCount(); - final int minCount = constraint.getMinCount(); - if (!newDialog || minCount != 1 || maxCount != 1) { - append(buf, SSRBundle.message("min.occurs.tooltip.message", minCount, (maxCount == Integer.MAX_VALUE) ? "∞" : maxCount)); - } - } } final String script = namedScriptableDefinition.getScriptCodeConstraint(); if (script != null && script.length() > 2) { - final String text = "
    " + StringUtil.escapeXmlEntities(StringUtil.unquoteString(script)) + "
    "; - append(buf, SSRBundle.message("script.tooltip.message", text)); + append(buf, SSRBundle.message("script.tooltip.message")); } - if (buf.length() == 0 && !editLink) { - buf.append(SSRBundle.message(!newDialog ? "no.constraints.specified.tooltip.message" : "no.filters.tooltip.message")); + if (buf.length() == 0 && !editLink && verbose) { + buf.append(SSRBundle.message("no.constraints.specified.tooltip.message")); } - if (editLink && newDialog) { + if (editLink && !verbose && !Registry.is("ssr.use.editor.inlays.instead.of.tool.tips")) { if (buf.length() > 0) buf.append("
    "); buf.append(" currentVariableCallback) { @@ -254,4 +264,101 @@ public class SubstitutionShortInfoHandler implements DocumentListener, EditorMou editor.getCaretModel().addCaretListener(handler); editor.putUserData(LISTENER_KEY, handler); } + + void updateEditorInlays() { + if (!Registry.is("ssr.use.editor.inlays.instead.of.tool.tips") || !Registry.is("ssr.use.new.search.dialog")) { + return; + } + final String text = editor.getDocument().getText(); + final Template template = TemplateManager.getInstance(editor.getProject()).createTemplate("", "", text); + final int segmentsCount = template.getSegmentsCount(); + final InlayModel inlayModel = editor.getInlayModel(); + final HashSet variables = new HashSet<>(inlays.keySet()); + final Configuration configuration = editor.getUserData(CURRENT_CONFIGURATION_KEY); + if (configuration == null) return; + int variableNameLength = 0; + for (int i = 0; i < segmentsCount; i++) { + final int offset = template.getSegmentOffset(i); + final String name = template.getSegmentName(i); + variableNameLength += name.length() + 2; + final NamedScriptableDefinition variable = configuration.findVariable(name); + final String labelText = getShortParamString(variable, false); + if (labelText.isEmpty()) { + continue; + } + final Inlay inlay = inlays.get(name); + if (inlay == null) { + inlays.put(name, inlayModel.addInlineElement(offset + variableNameLength, new FilterRenderer(labelText))); + } + else { + final FilterRenderer renderer = inlay.getRenderer(); + renderer.setText(labelText); + inlay.updateSize(); + variables.remove(name); + } + } + final Inlay inlay = inlays.get(Configuration.CONTEXT_VAR_NAME); + if (inlay == null) { + final NamedScriptableDefinition variable = configuration.findVariable(Configuration.CONTEXT_VAR_NAME); + final String labelText = getShortParamString(variable, false); + if (!labelText.isEmpty()) { + inlays.put(Configuration.CONTEXT_VAR_NAME, + inlayModel.addBlockElement(text.length() + variableNameLength, true, false, 0, + new FilterRenderer("complete pattern: " + labelText))); + } + } + for (String variable : variables) { + Disposer.dispose(inlays.remove(variable)); + } + } + + private static class FilterRenderer implements EditorCustomElementRenderer { + + private String myText; + + FilterRenderer(String text) { + myText = text; + } + + public void setText(String text) { + myText = text; + } + + @Override + public int calcWidthInPixels(@NotNull Editor editor) { + return getFontMetrics(editor).stringWidth(myText) + 12; + } + + private static Font getFont() { + return UIManager.getFont("Label.font"); + } + + private static FontMetrics getFontMetrics(Editor editor) { + return editor.getContentComponent().getFontMetrics(getFont()) ; + } + + @Override + public void paint(@NotNull Inlay inlay, @NotNull Graphics g, @NotNull Rectangle r, @NotNull TextAttributes textAttributes) { + final Editor editor = inlay.getEditor(); + final TextAttributes attributes = editor.getColorsScheme().getAttributes(DefaultLanguageHighlighterColors.INLINE_PARAMETER_HINT); + if (attributes == null) { + return; + } + final FontMetrics metrics = getFontMetrics(editor); + final Color backgroundColor = attributes.getBackgroundColor(); + if (backgroundColor != null) { + final GraphicsConfig config = GraphicsUtil.setupAAPainting(g); + GraphicsUtil.paintWithAlpha(g, 0.55f); + g.setColor(backgroundColor); + g.fillRoundRect(r.x + 2, r.y, r.width - 4, r.height, 8, 8); + config.restore(); + } + final Color foregroundColor = attributes.getForegroundColor(); + if (foregroundColor != null) { + g.setColor(foregroundColor); + g.setFont(getFont()); + g.drawString(myText, r.x + 6, r.y + r.height - metrics.getDescent()); + } + } + } } diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/UIUtil.java b/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/UIUtil.java index 6e283aa26d7d..8127ff1dc230 100644 --- a/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/UIUtil.java +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/UIUtil.java @@ -25,6 +25,7 @@ import com.intellij.openapi.fileTypes.FileTypes; import com.intellij.openapi.fileTypes.LanguageFileType; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.Balloon; +import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.wm.ToolWindowId; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; @@ -197,6 +198,9 @@ public class UIUtil { completeMatchInfo.addMouseListener(new MouseAdapter() { @Override public void mouseEntered(MouseEvent ignore) { + if (Registry.is("ssr.use.editor.inlays.instead.of.tool.tips") && Registry.is("ssr.use.new.search.dialog")) { + return; + } final Configuration configuration = configurationProducer.get(); if (configuration == null) { return; @@ -258,10 +262,10 @@ public class UIUtil { public static LanguageFileType detectFileType(@NotNull SearchContext searchContext) { final PsiFile file = searchContext.getFile(); - PsiElement context = file; + PsiElement context = null; final Editor editor = searchContext.getEditor(); - if (editor != null && context != null) { + if (editor != null && file != null) { final int offset = editor.getCaretModel().getOffset(); context = InjectedLanguageManager.getInstance(searchContext.getProject()).findInjectedElementAt(file, offset); if (context == null) { @@ -270,6 +274,9 @@ public class UIUtil { if (context != null) { context = context.getParent(); } + if (context == null) { + context = file; + } } if (context != null) { final Language language = context.getLanguage(); @@ -283,9 +290,10 @@ public class UIUtil { } @NotNull - public static Document createDocument(@NotNull Project project, @NotNull LanguageFileType fileType, Language dialect, @NotNull String text, - @NotNull StructuralSearchProfile profile) { - PsiFile codeFragment = profile.createCodeFragment(project, text); + public static Document createDocument(@NotNull Project project, @NotNull LanguageFileType fileType, Language dialect, + PatternContext patternContext, @NotNull String text, @NotNull StructuralSearchProfile profile) { + final String contextId = (patternContext == null) ? null : patternContext.getId(); + PsiFile codeFragment = profile.createCodeFragment(project, text, contextId); if (codeFragment == null) { codeFragment = createFileFragment(project, fileType, dialect, text); } @@ -302,7 +310,7 @@ public class UIUtil { @NotNull public static Editor createEditor(@NotNull Project project, @NotNull LanguageFileType fileType, Language dialect, @NotNull String text, @NotNull StructuralSearchProfile profile) { - PsiFile codeFragment = profile.createCodeFragment(project, text); + PsiFile codeFragment = profile.createCodeFragment(project, text, null); if (codeFragment == null) { codeFragment = createFileFragment(project, fileType, dialect, text); } diff --git a/platform/structuralsearch/source/messages/SSRBundle.properties b/platform/structuralsearch/source/messages/SSRBundle.properties index 3d50a35ae383..aae113888fdd 100644 --- a/platform/structuralsearch/source/messages/SSRBundle.properties +++ b/platform/structuralsearch/source/messages/SSRBundle.properties @@ -104,7 +104,7 @@ predefined.configuration.]junit.test.cases=junit test cases predefined.configuration.ifs=if's predefined.configuration.anonymous.classes=anonymous classes predefined.configuration.javadoc.tags=javadoc tags -predefined.configuration.all.methods.of.the.class.within.hierarchy=all methods of the class (within hierarchy) +predefined.configuration.all.methods.of.the.class.within.hierarchy=all methods of a class (within hierarchy) predefined.configuration.similar.methods.structure=similar methods structure predefined.configuration.class.implements.two.interfaces=class implementing two interfaces predefined.configuration.bean.info.classes=Bean info classes @@ -120,13 +120,13 @@ predefined.configuration.try.without.resources=try statements without resources predefined.configuration.switch.with.branches=switch statements with few branches predefined.configuration.labeled.break=labeled break statements predefined.configuration.block.dcls=block dcls -predefined.configuration.methods.of.the.class=methods of the class +predefined.configuration.methods.of.the.class=constructors & methods predefined.configuration.deprecated.methods=deprecated methods predefined.configuration.instanceof=instanceof predefined.configuration.implementors.of.interface.within.hierarchy=implementors of interface (within hierarchy) predefined.configuration.generic.casts=generic casts predefined.configuration.field.selections=field selections -predefined.configuration.fields.of.the.class=fields of the class +predefined.configuration.fields.of.the.class=fields of a class predefined.configuration.array.access=array access predefined.configuration.usage.of.derived.type.in.cast=usage of derived type in cast predefined.configuration.annotated.methods=annotated methods @@ -154,11 +154,11 @@ predefined.configuration.serializable.classes.and.their.serialization.implementa predefined.configuration.annotated.fields=annotated fields predefined.configuration.generic.classes=generic classes predefined.configuration.javadoc.annotated.class=javadoc annotated class -predefined.configuration.constructors.of.the.class=constructors of the class +predefined.configuration.constructors.of.the.class=class constructors predefined.configuration.typed.symbol=typed symbol -predefined.configuration.all.fields.of.the.class=all fields of the class -predefined.configuration.instance.fields.of.the.class=instance fields of the class -predefined.configuration.packagelocal.fields.of.the.class=package-private fields of the class +predefined.configuration.all.fields.of.the.class=all fields of a class +predefined.configuration.instance.fields.of.the.class=instance fields of a class +predefined.configuration.packagelocal.fields.of.the.class=package-private fields of a class predefined.configuration.classes=classes predefined.configuration.classes.interfaces.enums=classes, interfaces \\& enums predefined.configuration.new.expressions=new expressions @@ -187,14 +187,13 @@ editvarcontraints.edit.variables=Edit Variables no.constraints.specified.tooltip.message=no constraints specified no.filters.tooltip.message=no filters script.option.text=Script text\: -occurs.tooltip.message=occurs: {0} -min.occurs.tooltip.message=count=[{0},{1}] +min.occurs.tooltip.message=[{0},{1}] target.tooltip.message=target text.tooltip.message=text{0,choice,0#=|1#\u2260}{1}{2,choice,0#|1#', whole words'}{3,choice,0#|1#', within hierarchy'} -hierarchy.tooltip.message=search within hierarchy +hierarchy.tooltip.message=within hierarchy exprtype.tooltip.message=type{0,choice,0#=|1#\u2260}{1}{2,choice,0#|1#', within hierarchy'} expected.type.tooltip.message=expected type{0,choice,0#=|1#\u2260}{1}{2,choice,0#|1#', within hierarchy'} -script.tooltip.message=script=
    {0} +script.tooltip.message=script within.constraints.tooltip.message=within{0,choice,0#=|1#\u2260}{1} reference.target.tooltip.message=reference{0,choice,0#=|1#\u2260}{1} complete.match.variable.tooltip.message=Complete Match: {0} diff --git a/platform/structuralsearch/testSource/com/intellij/structuralsearch/JavaPredefinedConfigurationsTest.java b/platform/structuralsearch/testSource/com/intellij/structuralsearch/JavaPredefinedConfigurationsTest.java index 75910b956f6b..4ac30303531e 100644 --- a/platform/structuralsearch/testSource/com/intellij/structuralsearch/JavaPredefinedConfigurationsTest.java +++ b/platform/structuralsearch/testSource/com/intellij/structuralsearch/JavaPredefinedConfigurationsTest.java @@ -156,7 +156,17 @@ public class JavaPredefinedConfigurationsTest extends StructuralSearchTestCase { " void o(String s) {}" + "}", "public void m(final int i, int j, int k) { System.out.println(i); }"); - //assertTrue("untested configurations: " + configurationMap.keySet(), configurationMap.isEmpty()); + doTest(configurationMap.remove(SSRBundle.message("predefined.configuration.methods.of.the.class")), + "abstract class X {" + + " X() {}" + + " X(String s) {}" + + " abstract void x();" + + " int x(int i) {}" + + " boolean x(double d, Object o) {}" + + "}", + "X() {}", "X(String s) {}", "abstract void x();", "int x(int i) {}", "boolean x(double d, Object o) {}"); + //assertTrue((templates.length - configurationMap.size()) + " of " + templates.length + + // " existing templates tested. Untested templates: " + configurationMap.keySet(), configurationMap.isEmpty()); } private void doTest(Configuration template, String source, String... results) { diff --git a/platform/tasks-platform-impl/src/com/intellij/tasks/impl/TaskManagerImpl.java b/platform/tasks-platform-impl/src/com/intellij/tasks/impl/TaskManagerImpl.java index 3b968aa0429e..afb8d2617d99 100644 --- a/platform/tasks-platform-impl/src/com/intellij/tasks/impl/TaskManagerImpl.java +++ b/platform/tasks-platform-impl/src/com/intellij/tasks/impl/TaskManagerImpl.java @@ -408,7 +408,7 @@ public final class TaskManagerImpl extends TaskManager implements PersistentStat ChangeListManager changeListManager = ChangeListManager.getInstance(myProject); for (ShelvedChangeList list : manager.getShelvedChangeLists()) { if (name.equals(list.DESCRIPTION)) { - manager.unshelveChangeList(list, list.getChanges(myProject), list.getBinaryFiles(), changeListManager.getDefaultChangeList(), true); + manager.unshelveChangeList(list, null, list.getBinaryFiles(), changeListManager.getDefaultChangeList(), true); return; } } diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index 74467756f8fe..061cad2b562d 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -515,7 +515,6 @@ vcs.skip.single.default.changelist.description=Don't show changelist node in Loc vcs.non.modal.commit=false vcs.non.modal.commit.description=Allow to commit directly from Local Changes -vcs.non.modal.commit.restartRequired=true vcs.unversioned.files.max.intree=1000 vcs.unversioned.files.max.intree.description=Maximum number of unversioned and ignored files displayed in the Local Changes file tree. \ @@ -1544,6 +1543,8 @@ ssr.template.from.selection.builder=false ssr.template.from.selection.builder.description=Allows to build template with live-template-like placeholders when invoking SSR from editor with selection ssr.save.templates.to.ide.instead.of.project.workspace=true ssr.save.templates.to.ide.instead.of.project.workspace.description=Makes saved Structural Search templates globally available for all projects +ssr.use.editor.inlays.instead.of.tool.tips=true +ssr.use.editor.inlays.instead.of.tool.tips.description=Replaces filter tool tips with editor inlays in the new Structural Search dialog jdk.regex.soe.workaround=true jdk.regex.soe.workaround.description=In regular expression pattern replace choice \\n|. with . (and DOT_ALL option) to prevent stack overflow during matching diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/VcsApplicationSettings.java b/platform/vcs-api/src/com/intellij/openapi/vcs/VcsApplicationSettings.java index 42a8f12fbf80..3099d8656ab0 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/VcsApplicationSettings.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/VcsApplicationSettings.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-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. package com.intellij.openapi.vcs; import com.intellij.openapi.components.PersistentStateComponent; @@ -25,6 +25,7 @@ public class VcsApplicationSettings implements PersistentStateComponent() @@ -97,9 +96,9 @@ abstract class AbstractCommonCheckinAction : AbstractVcsAction(), UpdateInBackgr } val executor = getExecutor(project) - if (executor == null && isNonModalCommit()) { - val workflowHandler = (ChangesViewManager.getInstance(project) as? ChangesViewManager)?.commitWorkflowHandler - workflowHandler?.run { + val workflowHandler = (ChangesViewManager.getInstance(project) as? ChangesViewManager)?.commitWorkflowHandler + if (executor == null && workflowHandler != null) { + workflowHandler.run { setCommitState(included, isForceUpdateNotEmptyCommitState()) activate() } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesViewCommitPanelSplitter.kt b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesViewCommitPanelSplitter.kt new file mode 100644 index 000000000000..512b8a48b349 --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesViewCommitPanelSplitter.kt @@ -0,0 +1,34 @@ +// 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. +package com.intellij.openapi.vcs.changes + +import com.intellij.ide.util.PropertiesComponent +import com.intellij.ui.OnePixelSplitter +import com.intellij.util.ui.update.UiNotifyConnector +import javax.swing.JComponent + +private const val CHANGES_VIEW_COMMIT_SPLITTER_PROPORTION = "ChangesViewManager.COMMIT_SPLITTER_PROPORTION" + +private class ChangesViewCommitPanelSplitter : OnePixelSplitter(true, CHANGES_VIEW_COMMIT_SPLITTER_PROPORTION, 1.0f) { + private var isDefaultProportionSet = false + + override fun setSecondComponent(component: JComponent?) { + if (component != null && !isDefaultProportionSet) { + UiNotifyConnector.doWhenFirstShown(this) { + isDefaultProportionSet = true + proportion = 1.0f - (component.preferredSize.getHeight().toFloat() / height).coerceIn(0.05f, 0.95f) + } + } + super.setSecondComponent(component) + } + + override fun loadProportion() { + val key = splitterProportionKey + isDefaultProportionSet = key != null && PropertiesComponent.getInstance().isValueSet(key) + + if (isDefaultProportionSet) super.loadProportion() + } + + override fun saveProportion() { + if (isDefaultProportionSet) super.saveProportion() + } +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesViewManager.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesViewManager.java index 9c49ff1d8734..90ee3846133d 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesViewManager.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesViewManager.java @@ -49,6 +49,7 @@ import com.intellij.util.xmlb.annotations.XCollection; import com.intellij.vcs.commit.ChangesViewCommitPanel; import com.intellij.vcs.commit.ChangesViewCommitWorkflow; import com.intellij.vcs.commit.ChangesViewCommitWorkflowHandler; +import com.intellij.vcs.commit.CommitWorkflowManager; import gnu.trove.THashSet; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -77,7 +78,6 @@ import static com.intellij.ui.ScrollPaneFactory.createScrollPane; import static com.intellij.util.containers.ContainerUtil.set; import static com.intellij.util.ui.JBUI.Panels.simplePanel; import static com.intellij.util.ui.UIUtil.addBorder; -import static com.intellij.vcsUtil.VcsImplUtil.isNonModalCommit; import static java.util.stream.Collectors.toList; @State( @@ -91,6 +91,9 @@ public class ChangesViewManager implements ChangesViewI, ProjectComponent, Persi @NotNull private final ChangesListView myView; private ChangesViewCommitPanel myCommitPanel; + private BorderLayoutPanel myContentPanel; + private ChangesViewCommitPanelSplitter myCommitPanelSplitter; + private SimpleToolWindowPanel myToolWindowPanel; private ChangesViewCommitWorkflowHandler myCommitWorkflowHandler; private final VcsConfiguration myVcsConfiguration; private JPanel myProgressLabel; @@ -123,7 +126,7 @@ public class ChangesViewManager implements ChangesViewI, ProjectComponent, Persi myProject = project; myContentManager = contentManager; myVcsConfiguration = VcsConfiguration.getInstance(myProject); - myView = new ChangesListView(project, isNonModalCommit()); + myView = new ChangesListView(project, false); myTreeExpander = new MyTreeExpander(); myView.setTreeExpander(myTreeExpander); myTreeUpdateAlarm = new Alarm(Alarm.ThreadToUse.POOLED_THREAD, project); @@ -157,12 +160,13 @@ public class ChangesViewManager implements ChangesViewI, ProjectComponent, Persi ChangeListManager.getInstance(myProject).addChangeListListener(new MyChangeListListener(), myProject); if (ApplicationManager.getApplication().isHeadlessEnvironment()) return; - JComponent panel = createChangeViewComponent(); - myContent = new MyChangeViewContent(panel, ChangesViewContentManager.LOCAL_CHANGES, false); + myToolWindowPanel = createChangeViewComponent(); + myContent = new MyChangeViewContent(myToolWindowPanel, ChangesViewContentManager.LOCAL_CHANGES, false); myContent.setHelpId(ChangesListView.HELP_ID); myContent.setCloseable(false); myContentManager.addContent(myContent); - if (myCommitPanel != null) Disposer.register(myContent, myCommitPanel); + + CommitWorkflowManager.install(myProject); scheduleRefresh(); myProject.getMessageBus().connect().subscribe(RemoteRevisionsCache.REMOTE_VERSION_CHANGED, () -> scheduleRefresh()); @@ -192,27 +196,43 @@ public class ChangesViewManager implements ChangesViewI, ProjectComponent, Persi return myCommitWorkflowHandler; } - private JComponent createChangeViewComponent() { + public void updateCommitWorkflow(boolean isNonModal) { + if (isNonModal) { + if (myCommitPanel == null) { + myCommitPanel = new ChangesViewCommitPanel(myView, myToolWindowPanel); + myCommitWorkflowHandler = new ChangesViewCommitWorkflowHandler(new ChangesViewCommitWorkflow(myProject), myCommitPanel); + Disposer.register(myContent, myCommitPanel); + + myCommitPanelSplitter.setSecondComponent(myCommitPanel); + } + } + else if (myCommitPanel != null) { + myCommitPanelSplitter.setSecondComponent(null); + Disposer.dispose(myCommitPanel); + + myCommitPanel = null; + myCommitWorkflowHandler = null; + } + } + + @NotNull + private SimpleToolWindowPanel createChangeViewComponent() { ActionToolbar changesToolbar = createChangesToolbar(); addBorder(changesToolbar.getComponent(), createBorder(JBColor.border(), SideBorder.RIGHT)); BorderLayoutPanel changesPanel = simplePanel(createScrollPane(myView)).addToLeft(changesToolbar.getComponent()); - BorderLayoutPanel contentPanel = new BorderLayoutPanel() { + myCommitPanelSplitter = new ChangesViewCommitPanelSplitter(); + myCommitPanelSplitter.setFirstComponent(changesPanel); + myContentPanel = new BorderLayoutPanel() { @Override public Dimension getMinimumSize() { return isMinimumSizeSet() ? super.getMinimumSize() : changesToolbar.getComponent().getPreferredSize(); } }; - contentPanel.addToCenter(changesPanel); - if (isNonModalCommit()) { - myCommitPanel = new ChangesViewCommitPanel(myView); - contentPanel.addToBottom(myCommitPanel); - - myCommitWorkflowHandler = new ChangesViewCommitWorkflowHandler(new ChangesViewCommitWorkflow(myProject), myCommitPanel); - } + myContentPanel.addToCenter(myCommitPanelSplitter); MyChangeProcessor changeProcessor = new MyChangeProcessor(myProject); - mySplitterComponent = new PreviewDiffSplitterComponent(contentPanel, changeProcessor, CHANGES_VIEW_PREVIEW_SPLITTER_PROPORTION, + mySplitterComponent = new PreviewDiffSplitterComponent(myContentPanel, changeProcessor, CHANGES_VIEW_PREVIEW_SPLITTER_PROPORTION, myVcsConfiguration.LOCAL_CHANGES_DETAILS_PREVIEW_SHOWN); myView.installPopupHandler((DefaultActionGroup)ActionManager.getInstance().getAction("ChangesViewPopupMenu")); @@ -243,13 +263,11 @@ public class ChangesViewManager implements ChangesViewI, ProjectComponent, Persi return panel; } - private void registerShortcuts(@NotNull JComponent component) { + private static void registerShortcuts(@NotNull JComponent component) { registerWithShortcutSet("ChangesView.Refresh", CommonShortcuts.getRerun(), component); registerWithShortcutSet("ChangesView.NewChangeList", CommonShortcuts.getNew(), component); registerWithShortcutSet("ChangesView.RemoveChangeList", CommonShortcuts.getDelete(), component); registerWithShortcutSet(IdeActions.MOVE_TO_ANOTHER_CHANGE_LIST, CommonShortcuts.getMove(), component); - - if (myCommitPanel != null) myCommitPanel.setupShortcuts(component); } @NotNull diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsShelveUtils.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsShelveUtils.java index dd7de13c2247..4ebc21b28992 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsShelveUtils.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsShelveUtils.java @@ -28,6 +28,7 @@ import java.io.IOException; import java.util.Collection; import java.util.HashSet; import java.util.List; +import java.util.Objects; public class VcsShelveUtils { private static final Logger LOG = Logger.getInstance(VcsShelveUtils.class.getName()); @@ -42,15 +43,18 @@ public class VcsShelveUtils { assert baseDir != null; final String projectPath = baseDir.getPath() + "/"; + shelvedChangeList.loadChangesIfNeeded(project); + final List changes = Objects.requireNonNull(shelvedChangeList.getChanges()); + List binaryFiles = shelvedChangeList.getBinaryFiles(); + LOG.info("refreshing files "); // The changes are temporary copied to the first local change list, the next operation will restore them back // Refresh files that might be affected by unshelve - refreshFilesBeforeUnshelve(project, shelvedChangeList, projectPath); + refreshFilesBeforeUnshelve(projectPath, changes, binaryFiles); LOG.info("Unshelving shelvedChangeList: " + shelvedChangeList); - final List changes = shelvedChangeList.getChanges(project); // we pass null as target change list for Patch Applier to do NOTHING with change lists - shelveManager.unshelveChangeList(shelvedChangeList, changes, shelvedChangeList.getBinaryFiles(), targetChangeList, false, true, + shelveManager.unshelveChangeList(shelvedChangeList, changes, binaryFiles, targetChangeList, false, true, true, leftConflictTitle, rightConflictTitle, true); ApplicationManager.getApplication().invokeAndWait(() -> markUnshelvedFilesNonUndoable(project, changes)); } @@ -72,24 +76,26 @@ public class VcsShelveUtils { } } - private static void refreshFilesBeforeUnshelve(final Project project, ShelvedChangeList shelvedChangeList, String projectPath) { + private static void refreshFilesBeforeUnshelve(String projectPath, + @NotNull List shelvedChanges, + @NotNull List binaryFiles) { HashSet filesToRefresh = new HashSet<>(); - for (ShelvedChange c : shelvedChangeList.getChanges(project)) { + shelvedChanges.forEach(c -> { if (c.getBeforePath() != null) { filesToRefresh.add(new File(projectPath + c.getBeforePath())); } if (c.getAfterPath() != null) { filesToRefresh.add(new File(projectPath + c.getAfterPath())); } - } - for (ShelvedBinaryFile f : shelvedChangeList.getBinaryFiles()) { + }); + binaryFiles.forEach(f -> { if (f.BEFORE_PATH != null) { filesToRefresh.add(new File(projectPath + f.BEFORE_PATH)); } if (f.AFTER_PATH != null) { filesToRefresh.add(new File(projectPath + f.AFTER_PATH)); } - } + }); LocalFileSystem.getInstance().refreshIoFiles(filesToRefresh); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesActionProvider.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesActionProvider.java index 58b3142dce45..1539859990d7 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesActionProvider.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesActionProvider.java @@ -99,7 +99,7 @@ public class DiffShelvedChangesActionProvider implements AnActionExtensionProvid List changeLists = ShelvedChangesViewManager.getShelvedLists(dc); ShelvedChangeList changeList = assertNotNull(ContainerUtil.getFirstItem(changeLists)); - final List textChanges = changeList.getChanges(project); + final List textChanges = Objects.requireNonNull(changeList.getChanges()); final List binaryChanges = changeList.getBinaryFiles(); final List diffRequestProducers = new ArrayList<>(); @@ -392,7 +392,7 @@ public class DiffShelvedChangesActionProvider implements AnActionExtensionProvid } } else { - return createDiffRequest(myProject, myChange.getChange(myProject), getName(), context, indicator); + return createDiffRequest(myProject, myChange.getChange(), getName(), context, indicator); } } } @@ -500,9 +500,9 @@ public class DiffShelvedChangesActionProvider implements AnActionExtensionProvid @NotNull TextFilePatch patch, @NotNull UserDataHolder context, @NotNull ProgressIndicator indicator) throws DiffRequestProducerException { - DiffRequest diffRequest = myChange.isConflictingChange(myProject) + DiffRequest diffRequest = myChange.isConflictingChange() ? createConflictDiffRequest(myProject, myFile, patch, SHELVED_VERSION, texts, getName()) - : createDiffRequest(myProject, myChange.getChange(myProject), getName(), context, indicator); + : createDiffRequest(myProject, myChange.getChange(), getName(), context, indicator); if (!myWithLocal) { DiffUtil.addNotification(createNotification(DIFF_WITH_BASE_ERROR + " Showing difference with local version"), diffRequest); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ImportIntoShelfAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ImportIntoShelfAction.java index 8566a2ba5427..2c92eed9dd40 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ImportIntoShelfAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ImportIntoShelfAction.java @@ -32,9 +32,6 @@ import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.List; -/** - * @author irengrig - */ public class ImportIntoShelfAction extends DumbAwareAction { public ImportIntoShelfAction() { super("Import Patches...", "Copies a patch file to the shelf", null); @@ -58,7 +55,7 @@ public class ImportIntoShelfAction extends DumbAwareAction { final List patchTypeFiles = new ArrayList<>(); final boolean filesFound = pm.runProcessWithProgressSynchronously( - (Runnable)() -> patchTypeFiles.addAll(shelveChangesManager.gatherPatchFiles(files)), "Looking for patch files...", true, project); + (Runnable)() -> patchTypeFiles.addAll(shelveChangesManager.gatherPatchFiles(files)), "Looking for Patch Files...", true, project); if (!filesFound || patchTypeFiles.isEmpty()) return; if (!patchTypeFiles.equals(files)) { final String message = "Found " + (patchTypeFiles.size() == 1 ? @@ -81,7 +78,7 @@ public class ImportIntoShelfAction extends DumbAwareAction { if (lists.isEmpty() && exceptions.isEmpty()) { VcsBalloonProblemNotifier.showOverChangesView(project, "No patches found", MessageType.WARNING); } - }, "Import patches into shelf", true, project); + }, "Import Patches into Shelf...", true, project); }); } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesManager.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesManager.java index a0858933e278..7df2c10b134d 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesManager.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesManager.java @@ -33,11 +33,9 @@ import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.project.ProjectKt; -import com.intellij.util.Consumer; -import com.intellij.util.ObjectUtils; -import com.intellij.util.PathUtil; -import com.intellij.util.SmartList; +import com.intellij.util.*; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.ContainerUtilRt; import com.intellij.util.messages.MessageBus; import com.intellij.util.messages.Topic; import com.intellij.util.text.CharArrayCharSequence; @@ -69,9 +67,11 @@ import static com.intellij.openapi.util.io.FileUtil.toSystemIndependentName; import static com.intellij.openapi.util.text.StringUtil.notNullize; import static com.intellij.openapi.vcs.changes.ChangeListUtil.getChangeListNameForUnshelve; import static com.intellij.openapi.vcs.changes.ChangeListUtil.getPredefinedChangeList; +import static com.intellij.openapi.vcs.changes.shelf.ShelvedChangeList.createShelvedChangesFromFilePatches; import static com.intellij.util.ObjectUtils.assertNotNull; import static com.intellij.util.ObjectUtils.chooseNotNull; import static com.intellij.util.containers.ContainerUtil.*; +import static java.util.Objects.requireNonNull; @State(name = "ShelveChangesManager", storages = {@Storage(StoragePathMacros.WORKSPACE_FILE)}) public class ShelveChangesManager implements PersistentStateComponent, ProjectComponent { @@ -240,18 +240,20 @@ public class ShelveChangesManager implements PersistentStateComponent, final SchemeManager newSchemeManager = createShelveSchemeManager(myProject, VcsUtil.getFilePath(toFile).getPath()); newSchemeManager.loadSchemes(); if (VcsConfiguration.getInstance(myProject).MOVE_SHELVES && fromFile.exists()) { - new Task.Modal(myProject, "Moving Shelves to the New Directory...", true) { + new Task.Modal(myProject, "Copying Shelves to the New Directory...", true) { @Override public void run(@NotNull ProgressIndicator indicator) { for (ShelvedChangeList list : mySchemeManager.getAllSchemes()) { if (!list.isValid()) continue; - ShelvedChangeList migratedList = ShelvedChangeList.copy(list); - //find new name; - File newTargetDirectory = suggestPatchName(myProject, migratedList.DESCRIPTION, toFile, ""); - migrateResourcesTo(migratedList, newTargetDirectory, false); - newSchemeManager.addScheme(migratedList, false); - // migrate resources and scheme path - indicator.checkCanceled(); + try { + File newTargetDirectory = suggestPatchName(myProject, list.DESCRIPTION, toFile, ""); + ShelvedChangeList migratedList = createChangelistCopyWithChanges(list, newTargetDirectory); + newSchemeManager.addScheme(migratedList, false); + indicator.checkCanceled(); + } + catch (IOException e) { + LOG.error("Can't copy patch file: " + list.PATH); + } } clearShelvedLists(mySchemeManager.getAllSchemes(), false); } @@ -331,45 +333,9 @@ public class ShelveChangesManager implements PersistentStateComponent, } } - /** - * Should be called only once: when Settings Repository plugin runs first time - * - * @return collection of non-migrated or not deleted files to show a error somewhere outside - */ @NotNull - public Collection checkAndMigrateOldPatchResourcesToNewSchemeStorage() { - Collection nonMigratedPaths = new ArrayList<>(); - for (ShelvedChangeList list : mySchemeManager.getAllSchemes()) { - File newPatchDir = new File(getShelfResourcesDirectory(), list.getName()); - // it should be enough for migration to check if resource directory exists. If any bugs appeared add isAncestor checks for each path - if (!newPatchDir.exists() && newPatchDir.mkdirs()) { - nonMigratedPaths.addAll(migrateResourcesTo(list, newPatchDir, true)); - } - } - return nonMigratedPaths; - } - - @NotNull - private static Collection migrateResourcesTo(@NotNull ShelvedChangeList list, - @NotNull File targetDirectory, - boolean deleteOld) { - Collection nonMigratedPaths = new ArrayList<>(); - //try to copy/move .patch file - File patchFile = new File(list.PATH); - if (patchFile.exists()) { - File newPatchFile = getPatchFileInConfigDir(targetDirectory); - try { - FileUtil.copy(patchFile, newPatchFile); - list.PATH = toSystemIndependentName(newPatchFile.getPath()); - if (deleteOld) { - FileUtil.delete(patchFile); - } - } - catch (IOException e) { - nonMigratedPaths.add(list.PATH); - } - } - + private static List copyBinaryFiles(@NotNull ShelvedChangeList list, @NotNull File targetDirectory) { + List copied = new ArrayList<>(); for (ShelvedBinaryFile file : list.getBinaryFiles()) { if (file.SHELVED_PATH != null) { File shelvedFile = new File(file.SHELVED_PATH); @@ -377,18 +343,15 @@ public class ShelveChangesManager implements PersistentStateComponent, File newShelvedFile = new File(targetDirectory, PathUtil.getFileName(file.AFTER_PATH)); try { FileUtil.copy(shelvedFile, newShelvedFile); - file.SHELVED_PATH = toSystemIndependentName(newShelvedFile.getPath()); - if (deleteOld) { - FileUtil.delete(shelvedFile); - } + copied.add(new ShelvedBinaryFile(file.BEFORE_PATH, file.AFTER_PATH, toSystemIndependentName(newShelvedFile.getPath()))); } catch (IOException e) { - nonMigratedPaths.add(shelvedFile.getPath()); + LOG.error("Can't copy binary file: " + list.PATH); } } } } - return nonMigratedPaths; + return copied; } @NotNull @@ -475,7 +438,9 @@ public class ShelveChangesManager implements PersistentStateComponent, baseRevisionsOfDvcsIntoContext(textChanges, commitContext); ShelfFileProcessorUtil.savePatchFile(myProject, patchFile, patches, null, commitContext); - final ShelvedChangeList changeList = new ShelvedChangeList(patchFile.toString(), commitMessage.replace('\n', ' '), binaryFiles); + final ShelvedChangeList changeList = new ShelvedChangeList(patchFile.toString(), commitMessage.replace('\n', ' '), binaryFiles, + createShelvedChangesFromFilePatches(myProject, patchFile.toString(), + patches)); changeList.markToDelete(markToBeDeleted); changeList.setName(schemePatchDir.getName()); ProgressManager.checkCanceled(); @@ -525,7 +490,9 @@ public class ShelveChangesManager implements PersistentStateComponent, File schemePatchDir = generateUniqueSchemePatchDir(fileName, true); File patchFile = getPatchFileInConfigDir(schemePatchDir); ShelfFileProcessorUtil.savePatchFile(myProject, patchFile, patches, patchTransitExtensions, new CommitContext()); - final ShelvedChangeList changeList = new ShelvedChangeList(patchFile.toString(), fileName.replace('\n', ' '), new SmartList<>()); + final ShelvedChangeList changeList = new ShelvedChangeList(patchFile.toString(), fileName.replace('\n', ' '), new SmartList<>(), + createShelvedChangesFromFilePatches(myProject, patchFile.getPath(), + patches)); changeList.setName(schemePatchDir.getName()); mySchemeManager.addScheme(changeList, false); return changeList; @@ -554,6 +521,7 @@ public class ShelveChangesManager implements PersistentStateComponent, return result; } + @CalledInBackground public List importChangeLists(final Collection files, final Consumer exceptionConsumer) { final List result = new ArrayList<>(files.size()); @@ -563,20 +531,22 @@ public class ShelveChangesManager implements PersistentStateComponent, filesProgress.updateIndicator(file); final String description = file.getNameWithoutExtension().replace('_', ' '); File schemeNameDir = generateUniqueSchemePatchDir(description, true); - final File patchPath = getPatchFileInConfigDir(schemeNameDir); - final ShelvedChangeList list = new ShelvedChangeList(patchPath.getPath(), description, new SmartList<>(), - file.getTimeStamp()); - list.setName(schemeNameDir.getName()); + final File patchFile = getPatchFileInConfigDir(schemeNameDir); + String patchPath = patchFile.getPath(); try { - final List patchesList = loadPatches(myProject, file.getPath(), new CommitContext()); - if (!patchesList.isEmpty()) { - FileUtil.copy(new File(file.getPath()), patchPath); - // add only if ok to read patch + List filePatches = loadPatchesWithoutContent(myProject, patchPath, new CommitContext()); + if (!filePatches.isEmpty()) { + FileUtil.copy(new File(file.getPath()), patchFile); + final ShelvedChangeList list = + new ShelvedChangeList(patchPath, description, new SmartList<>(), + createShelvedChangesFromFilePatches(myProject, patchPath, filePatches), + file.getTimeStamp()); + list.setName(schemeNameDir.getName()); mySchemeManager.addScheme(list, false); result.add(list); } } - catch (IOException | PatchSyntaxException e) { + catch (Exception e) { exceptionConsumer.consume(new VcsException(e)); } } @@ -970,7 +940,7 @@ public class ShelveChangesManager implements PersistentStateComponent, public void run(@NotNull ProgressIndicator indicator) { for (ShelvedChangeList changeList : selectedChangeLists) { List changesForChangelist = - new ArrayList<>(intersection(changeList.getChanges(myProject), selectedChanges)); + new ArrayList<>(intersection(requireNonNull(changeList.getChanges()), selectedChanges)); List binariesForChangelist = new ArrayList<>(intersection(changeList.getBinaryFiles(), selectedBinaryChanges)); boolean shouldUnshelveAllList = changesForChangelist.isEmpty() && binariesForChangelist.isEmpty(); @@ -1089,10 +1059,12 @@ public class ShelveChangesManager implements PersistentStateComponent, boolean delete) { try { - ShelvedChangeList listCopy = createChangelistCopy(changeList); + ShelvedChangeList listCopy = createChangelistCopyWithChanges(changeList, generateUniqueSchemePatchDir(changeList.DESCRIPTION, true)); + listCopy.updateDate(); + //changes should be loaded saveRemainingChangesInList(changeList, remainingPatches, remainingBinaries, commitContext); - removeFromList(listCopy, changeList.getChanges(myProject), changeList.getBinaryFiles()); + removeFromListWithChanges(listCopy, requireNonNull(changeList.getChanges()), changeList.getBinaryFiles()); if (delete) { markChangeListAsDeleted(listCopy); } @@ -1114,26 +1086,30 @@ public class ShelveChangesManager implements PersistentStateComponent, writePatchesToFile(myProject, changeList.PATH, remainingPatches, commitContext); changeList.getBinaryFiles().retainAll(remainingBinaries); - changeList.clearLoadedChanges(); + changeList.setChanges(createShelvedChangesFromFilePatches(myProject, changeList.PATH, remainingPatches)); } - private void saveListAsScheme(@NotNull ShelvedChangeList list) { - if (!list.getBinaryFiles().isEmpty() || - !list.getChanges(myProject).isEmpty()) { + void saveListAsScheme(@NotNull ShelvedChangeList list) { + if (!list.getBinaryFiles().isEmpty() || !isEmpty(list.getChanges())) { // all newly create ShelvedChangeList have to be added to SchemesManger as new scheme mySchemeManager.addScheme(list, false); } } @NotNull - private ShelvedChangeList createChangelistCopy(@NotNull ShelvedChangeList changeList) throws IOException { - final File newPatchDir = generateUniqueSchemePatchDir(changeList.DESCRIPTION, true); - final File newPath = getPatchFileInConfigDir(newPatchDir); + ShelvedChangeList createChangelistCopyWithChanges(@NotNull ShelvedChangeList changeList, @NotNull File targetDir) + throws IOException { + final File newPath = getPatchFileInConfigDir(targetDir); FileUtil.copy(new File(changeList.PATH), newPath); - final ShelvedChangeList listCopy = new ShelvedChangeList(newPath.getAbsolutePath(), changeList.DESCRIPTION, - new ArrayList<>(changeList.getBinaryFiles())); + changeList.loadChangesIfNeeded(myProject); + + final ShelvedChangeList listCopy = + new ShelvedChangeList(newPath.getAbsolutePath(), changeList.DESCRIPTION, copyBinaryFiles(changeList, targetDir), + ContainerUtilRt.newArrayList(requireNonNull(changeList.getChanges())), changeList.DATE.getTime()); listCopy.markToDelete(changeList.isMarkedToDelete()); - listCopy.setName(newPatchDir.getName()); + listCopy.setRecycled(changeList.isRecycled()); + listCopy.setDeleted(changeList.isDeleted()); + listCopy.setName(targetDir.getName()); return listCopy; } @@ -1158,7 +1134,7 @@ public class ShelveChangesManager implements PersistentStateComponent, clearShelvedLists(getRecycledShelvedChangeLists(), true); } - private void clearShelvedLists(@NotNull List shelvedLists, boolean updateView) { + void clearShelvedLists(@NotNull List shelvedLists, boolean updateView) { if (shelvedLists.isEmpty()) return; for (ShelvedChangeList list : shelvedLists) { deleteResources(list); @@ -1174,9 +1150,10 @@ public class ShelveChangesManager implements PersistentStateComponent, return new HashSet<>(ContainerUtil.notNullize(myShelvingFiles)); } - private void removeFromList(@NotNull final ShelvedChangeList listCopy, - @NotNull List shelvedChanges, - @NotNull List shelvedBinaryChanges) { + private void removeFromListWithChanges(@NotNull final ShelvedChangeList listCopy, + @NotNull List shelvedChanges, + @NotNull List shelvedBinaryChanges) { + //listCopy should contain loaded changes removeBinaries(listCopy, shelvedBinaryChanges); removeChanges(listCopy, shelvedChanges); @@ -1184,8 +1161,9 @@ public class ShelveChangesManager implements PersistentStateComponent, try { final CommitContext commitContext = new CommitContext(); final List patches = new ArrayList<>(); - for (ShelvedChange change : listCopy.getChanges(myProject)) { - patches.add(change.loadFilePatch(myProject, commitContext)); + List filePatches = loadPatches(myProject, listCopy.PATH, commitContext); + for (ShelvedChange change : requireNonNull(listCopy.getChanges())) { + patches.add(find(filePatches, patch -> change.getBeforePath().equals(patch.getBeforeName()))); } writePatchesToFile(myProject, listCopy.PATH, patches, commitContext); } @@ -1195,8 +1173,8 @@ public class ShelveChangesManager implements PersistentStateComponent, } } - private void removeChanges(@NotNull ShelvedChangeList list, @NotNull List shelvedChanges) { - for (Iterator iterator = list.getChanges(myProject).iterator(); iterator.hasNext(); ) { + private static void removeChanges(@NotNull ShelvedChangeList list, @NotNull List shelvedChanges) { + for (Iterator iterator = requireNonNull(list.getChanges()).iterator(); iterator.hasNext(); ) { final ShelvedChange change = iterator.next(); for (ShelvedChange newChange : shelvedChanges) { if (Comparing.equal(change.getBeforePath(), newChange.getBeforePath()) && @@ -1243,8 +1221,6 @@ public class ShelveChangesManager implements PersistentStateComponent, } private void deleteResources(@NotNull final ShelvedChangeList changeList) { - FileUtil.delete(new File(getShelfResourcesDirectory(), changeList.getName())); - //backward compatibility deletion: if we didn't preform resource migration FileUtil.delete(new File(changeList.PATH)); for (ShelvedBinaryFile binaryFile : changeList.getBinaryFiles()) { final String path = binaryFile.SHELVED_PATH; @@ -1252,10 +1228,16 @@ public class ShelveChangesManager implements PersistentStateComponent, FileUtil.delete(new File(path)); } } + //schema dir may be related to another list, so check that it's empty first + File schemaDir = new File(getShelfResourcesDirectory(), changeList.getName()); + if (schemaDir.exists() && ArrayUtil.isEmpty(schemaDir.list())) { + FileUtil.delete(schemaDir); + } } public void renameChangeList(final ShelvedChangeList changeList, final String newName) { changeList.DESCRIPTION = newName; + notifyStateChanged(); } @NotNull @@ -1289,7 +1271,6 @@ public class ShelveChangesManager implements PersistentStateComponent, public void setShowRecycled(final boolean showRecycled) { myState.myShowRecycled = showRecycled; - notifyStateChanged(); } @NotNull diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelvedChange.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelvedChange.java index d8707a3bf8f5..3c2e3a07c044 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelvedChange.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelvedChange.java @@ -33,6 +33,7 @@ import com.intellij.openapi.vcs.VcsBundle; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.changes.*; import com.intellij.openapi.vcs.history.VcsRevisionNumber; +import com.intellij.util.containers.ContainerUtil; import com.intellij.vcsUtil.VcsUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -49,18 +50,23 @@ public class ShelvedChange { private final String myBeforePath; private final String myAfterPath; private final FileStatus myFileStatus; - private Change myChange; + @NotNull private final Change myChange; - public ShelvedChange(final String patchPath, final String beforePath, final String afterPath, final FileStatus fileStatus) { + public ShelvedChange(@NotNull Project project, + final String patchPath, + final String beforePath, + final String afterPath, + final FileStatus fileStatus) { myPatchPath = patchPath; myBeforePath = beforePath; // optimisation: memory myAfterPath = Comparing.equal(beforePath, afterPath) ? beforePath : afterPath; myFileStatus = fileStatus; + myChange = createChange(project); } - public boolean isConflictingChange(final Project project) { - ContentRevision afterRevision = getChange(project).getAfterRevision(); + public boolean isConflictingChange() { + ContentRevision afterRevision = getChange().getAfterRevision(); if (afterRevision == null) return false; try { afterRevision.getContent(); @@ -86,33 +92,39 @@ public class ShelvedChange { } @NotNull - public Change getChange(@NotNull Project project) { - // todo unify with - if (myChange == null) { - File baseDir = new File(project.getBaseDir().getPath()); - - File file = getAbsolutePath(baseDir, myBeforePath); - FilePath beforePath = VcsUtil.getFilePath(file, false); - ContentRevision beforeRevision = null; - if (myFileStatus != FileStatus.ADDED) { - beforeRevision = new CurrentContentRevision(beforePath) { - @Override - @NotNull - public VcsRevisionNumber getRevisionNumber() { - return new TextRevisionNumber(VcsBundle.message("local.version.title")); - } - }; - } - ContentRevision afterRevision = null; - if (myFileStatus != FileStatus.DELETED) { - FilePath afterPath = VcsUtil.getFilePath(getAbsolutePath(baseDir, myAfterPath), false); - afterRevision = new PatchedContentRevision(project, beforePath, afterPath); - } - myChange = new Change(beforeRevision, afterRevision, myFileStatus); - } + public Change getChange() { return myChange; } + @NotNull + @Deprecated + public Change getChange(@NotNull Project project) { + return myChange; + } + + private Change createChange(@NotNull Project project) { + File baseDir = new File(Objects.requireNonNull(project.getBasePath())); + + File file = getAbsolutePath(baseDir, myBeforePath); + FilePath beforePath = VcsUtil.getFilePath(file, false); + ContentRevision beforeRevision = null; + if (myFileStatus != FileStatus.ADDED) { + beforeRevision = new CurrentContentRevision(beforePath) { + @Override + @NotNull + public VcsRevisionNumber getRevisionNumber() { + return new TextRevisionNumber(VcsBundle.message("local.version.title")); + } + }; + } + ContentRevision afterRevision = null; + if (myFileStatus != FileStatus.DELETED) { + FilePath afterPath = VcsUtil.getFilePath(getAbsolutePath(baseDir, myAfterPath), false); + afterRevision = new PatchedContentRevision(project, beforePath, afterPath); + } + return new Change(beforeRevision, afterRevision, myFileStatus); + } + private static File getAbsolutePath(final File baseDir, final String relativePath) { File file; try { @@ -128,12 +140,7 @@ public class ShelvedChange { @Nullable public TextFilePatch loadFilePatch(final Project project, CommitContext commitContext) throws IOException, PatchSyntaxException { List filePatches = ShelveChangesManager.loadPatches(project, myPatchPath, commitContext); - for(TextFilePatch patch: filePatches) { - if (myBeforePath.equals(patch.getBeforeName())) { - return patch; - } - } - return null; + return ContainerUtil.find(filePatches, patch -> myBeforePath.equals(patch.getBeforeName())); } @Override diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelvedChangeList.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelvedChangeList.java index 5a19173f1cc7..baba3bbc7b34 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelvedChangeList.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelvedChangeList.java @@ -29,9 +29,11 @@ import com.intellij.openapi.vcs.FileStatus; import org.jdom.Element; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.File; import java.util.ArrayList; +import java.util.Collection; import java.util.Date; import java.util.List; @@ -48,42 +50,34 @@ public class ShelvedChangeList implements JDOMExternalizable, ExternalizableSche public String PATH; public String DESCRIPTION; public Date DATE; - private List myChanges; + private volatile List myChanges; private List myBinaryFiles; private boolean myRecycled; private boolean myToDelete; private boolean myIsDeleted; private String mySchemeName; - public ShelvedChangeList() { + ShelvedChangeList() { } - public ShelvedChangeList(final String path, final String description, final List binaryFiles) { - this(path, description, binaryFiles, System.currentTimeMillis()); + public ShelvedChangeList(final String path, + final String description, + final List binaryFiles, + @NotNull List shelvedChanges) { + this(path, description, binaryFiles, shelvedChanges, System.currentTimeMillis()); } - public ShelvedChangeList(final String path, final String description, final List binaryFiles, final long time) { + ShelvedChangeList(final String path, + final String description, + final List binaryFiles, + @NotNull List shelvedChanges, + final long time) { PATH = FileUtil.toSystemIndependentName(path); DESCRIPTION = description; DATE = new Date(time); myBinaryFiles = binaryFiles; mySchemeName = DESCRIPTION; - } - - static ShelvedChangeList copy(@NotNull ShelvedChangeList list) { - ShelvedChangeList copied = new ShelvedChangeList(); - copied.PATH = list.PATH; - copied.DESCRIPTION = list.DESCRIPTION; - copied.DATE = list.DATE; - copied.myBinaryFiles = new ArrayList<>(); - for (ShelvedBinaryFile file : list.getBinaryFiles()) { - copied.myBinaryFiles.add(new ShelvedBinaryFile(file.BEFORE_PATH, file.AFTER_PATH, file.SHELVED_PATH)); - } - copied.mySchemeName = list.DESCRIPTION; - copied.myRecycled = list.isRecycled(); - copied.myToDelete = list.isMarkedToDelete(); - copied.myIsDeleted = list.isDeleted(); - return copied; + myChanges = shelvedChanges; } public boolean isRecycled() { @@ -140,34 +134,52 @@ public class ShelvedChangeList implements JDOMExternalizable, ExternalizableSche return DESCRIPTION; } - public List getChanges(Project project) { + public void loadChangesIfNeeded(@NotNull Project project) { if (myChanges == null) { try { - myChanges = new ArrayList<>(); final List list = ShelveChangesManager.loadPatchesWithoutContent(project, PATH, null); - for (FilePatch patch : list) { - FileStatus status; - if (patch.isNewFile()) { - status = FileStatus.ADDED; - } - else if (patch.isDeletedFile()) { - status = FileStatus.DELETED; - } - else { - status = FileStatus.MODIFIED; - } - myChanges.add(new ShelvedChange(PATH, patch.getBeforeName(), patch.getAfterName(), status)); - } + myChanges = createShelvedChangesFromFilePatches(project, PATH, list); } catch (Exception e) { LOG.error("Failed to parse the file patch: [" + PATH + "]", e); } } + } + + @Nullable + public List getChanges() { return myChanges; } - public void clearLoadedChanges() { - myChanges = null; + @Deprecated + public List getChanges(Project project) { + loadChangesIfNeeded(project); + return getChanges(); + } + + void setChanges(List shelvedChanges) { + myChanges = shelvedChanges; + } + + @NotNull + static List createShelvedChangesFromFilePatches(@NotNull Project project, + @NotNull String patchPath, + @NotNull Collection filePatches) { + List changes = new ArrayList<>(); + for (FilePatch patch : filePatches) { + FileStatus status; + if (patch.isNewFile()) { + status = FileStatus.ADDED; + } + else if (patch.isDeletedFile()) { + status = FileStatus.DELETED; + } + else { + status = FileStatus.MODIFIED; + } + changes.add(new ShelvedChange(project, patchPath, patch.getBeforeName(), patch.getAfterName(), status)); + } + return changes; } public List getBinaryFiles() { @@ -190,7 +202,7 @@ public class ShelvedChangeList implements JDOMExternalizable, ExternalizableSche } public void markToDelete(boolean toDeleted) { - myToDelete = toDeleted; + myToDelete = toDeleted; } public boolean isMarkedToDelete() { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelvedChangesViewManager.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelvedChangesViewManager.java index 5bdeb7d56a54..92957a0214f1 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelvedChangesViewManager.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelvedChangesViewManager.java @@ -25,6 +25,7 @@ import com.intellij.openapi.fileTypes.FileTypeManager; import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.util.BackgroundTaskUtil; import com.intellij.openapi.project.DumbAwareRunnable; import com.intellij.openapi.project.Project; import com.intellij.openapi.startup.StartupManager; @@ -84,6 +85,7 @@ import static com.intellij.util.ObjectUtils.assertNotNull; import static com.intellij.util.containers.ContainerUtil.*; import static com.intellij.util.containers.UtilKt.isEmpty; import static java.util.Comparator.comparing; +import static java.util.Objects.requireNonNull; public class ShelvedChangesViewManager implements Disposable { private static final Logger LOG = Logger.getInstance(ShelvedChangesViewManager.class); @@ -99,6 +101,8 @@ public class ShelvedChangesViewManager implements Disposable { final DeleteProvider myDeleteProvider = new MyShelveDeleteProvider(); private final MergingUpdateQueue myUpdateQueue; private final VcsConfiguration myVcsConfiguration; + private volatile List myLoadedLists = emptyList(); + private final List myPostUpdateEdtActivity = new ArrayList<>(); public static final DataKey> SHELVED_CHANGELIST_KEY = DataKey.create("ShelveChangesManager.ShelvedChangeListData"); @@ -152,7 +156,6 @@ public class ShelvedChangesViewManager implements Disposable { String editorValue = ((TreeCellEditor)e.getSource()).getCellEditorValue().toString(); ShelvedChangeList shelvedChangeList = ((ShelvedListNode)node).getList(); ShelveChangesManager.getInstance(project).renameChangeList(shelvedChangeList, editorValue); - myTree.getModel().valueForPathChanged(TreeUtil.getPathFromRoot(node), shelvedChangeList); } } @@ -180,7 +183,7 @@ public class ShelvedChangesViewManager implements Disposable { } @CalledInAwt - private void updateChangesContent() { + void updateViewContent() { if (myShelveChangesManager.getAllLists().isEmpty()) { if (myContent != null) { myContentManager.removeContent(myContent); @@ -251,7 +254,7 @@ public class ShelvedChangesViewManager implements Disposable { private void createShelvedListsWithChangesNode(@NotNull List shelvedLists, @NotNull MutableTreeNode parentNode) { shelvedLists.forEach(changeList -> { List shelvedChanges = new ArrayList<>(); - changeList.getChanges(myProject).stream().map(ShelvedWrapper::new).forEach(shelvedChanges::add); + requireNonNull(changeList.getChanges()).stream().map(ShelvedWrapper::new).forEach(shelvedChanges::add); changeList.getBinaryFiles().stream().map(ShelvedWrapper::new).forEach(shelvedChanges::add); shelvedChanges.sort(comparing(s -> s.getChange(myProject), CHANGE_COMPARATOR)); @@ -266,6 +269,22 @@ public class ShelvedChangesViewManager implements Disposable { } } + @CalledInAwt + private void updateTreeModel() { + myTree.setPaintBusy(true); + BackgroundTaskUtil.executeOnPooledThread(myProject, () -> { + List lists = myShelveChangesManager.getAllLists(); + lists.forEach(l -> l.loadChangesIfNeeded(myProject)); + myLoadedLists = sorted(lists, ChangelistComparator.getInstance()); + ApplicationManager.getApplication().invokeLater(() -> { + myTree.setPaintBusy(false); + updateViewContent(); + myPostUpdateEdtActivity.forEach(Runnable::run); + myPostUpdateEdtActivity.clear(); + }, ModalityState.NON_MODAL); + }); + } + @CalledInAwt public void startEditing(@NotNull ShelvedChangeList shelvedChangeList) { runAfterUpdate(() -> { @@ -305,8 +324,8 @@ public class ShelvedChangesViewManager implements Disposable { private void runAfterUpdate(@NotNull Runnable postUpdateRunnable) { GuiUtils.invokeLaterIfNeeded(() -> { myUpdateQueue.cancelAllUpdates(); - updateChangesContent(); - postUpdateRunnable.run(); + myPostUpdateEdtActivity.add(postUpdateRunnable); + updateTreeModel(); }, ModalityState.NON_MODAL); } @@ -371,21 +390,12 @@ public class ShelvedChangesViewManager implements Disposable { @Override public void rebuildTree() { - DefaultTreeModel newModel = buildTreeModel(); - updateTreeModel(newModel); - } - - private DefaultTreeModel buildTreeModel() { MyShelvedTreeModelBuilder modelBuilder = new MyShelvedTreeModelBuilder(); - final List changeLists = new ArrayList<>(myShelveChangesManager.getShelvedChangeLists()); - if (myShelveChangesManager.isShowRecycled()) { - changeLists.addAll(myShelveChangesManager.getRecycledShelvedChangeLists()); - } - changeLists.sort(ChangelistComparator.getInstance()); - - modelBuilder.setShelvedLists(changeLists); - modelBuilder.setDeletedShelvedLists(sorted(myShelveChangesManager.getDeletedLists(), ChangelistComparator.getInstance())); - return modelBuilder.build(); + List changeLists = new ArrayList<>(myLoadedLists); + modelBuilder + .setShelvedLists(filter(changeLists, l -> !l.isDeleted() && (myShelveChangesManager.isShowRecycled() || !l.isRecycled()))); + modelBuilder.setDeletedShelvedLists(filter(changeLists, ShelvedChangeList::isDeleted)); + updateTreeModel(modelBuilder.build()); } @Nullable @@ -537,9 +547,8 @@ public class ShelvedChangesViewManager implements Disposable { private List getChangesNotInLists(@NotNull List listsToDelete, @NotNull List shelvedChanges) { List result = new ArrayList<>(shelvedChanges); - for (ShelvedChangeList list : listsToDelete) { - result.removeAll(list.getChanges(myProject)); - } + // all changes should be loaded because action performed from loaded shelf tab + listsToDelete.stream().map(list -> requireNonNull(list.getChanges())).forEach(result::removeAll); return result; } @@ -807,7 +816,7 @@ public class ShelvedChangesViewManager implements Disposable { @Override public void run() { - updateChangesContent(); + updateTreeModel(); } @Override diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelvedWrapper.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelvedWrapper.java index d6661406be0a..bc507fc9b1d6 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelvedWrapper.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelvedWrapper.java @@ -70,13 +70,13 @@ class ShelvedWrapper { } Change getChange(@NotNull Project project) { - return myShelvedChange != null ? myShelvedChange.getChange(project) : assertNotNull(myBinaryFile).createChange(project); + return myShelvedChange != null ? myShelvedChange.getChange() : assertNotNull(myBinaryFile).createChange(project); } @Nullable public VirtualFile getBeforeVFUnderProject(@NotNull final Project project) { - if (getBeforePath() == null || project.getBaseDir() == null) return null; - final File baseDir = new File(project.getBaseDir().getPath()); + if (getBeforePath() == null || project.getBasePath() == null) return null; + final File baseDir = new File(project.getBasePath()); final File file = new File(baseDir, getBeforePath()); return LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShowHideRecycledAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShowHideRecycledAction.java index d0852749e0eb..6e8253d412e7 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShowHideRecycledAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShowHideRecycledAction.java @@ -50,6 +50,7 @@ public class ShowHideRecycledAction extends ToggleAction implements DumbAware { final Project project = getEventProject(e); if (project != null) { ShelveChangesManager.getInstance(project).setShowRecycled(state); + ShelvedChangesViewManager.getInstance(project).updateViewContent(); } } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/UnshelveWithDialogAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/UnshelveWithDialogAction.java index 0a242a305ebf..c5d0cf27b4f8 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/UnshelveWithDialogAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/UnshelveWithDialogAction.java @@ -44,6 +44,7 @@ import java.awt.event.KeyEvent; import java.io.File; import java.util.Collections; import java.util.List; +import java.util.Objects; import static com.intellij.openapi.vcs.changes.ChangeListUtil.getChangeListNameForUnshelve; import static com.intellij.openapi.vcs.changes.ChangeListUtil.getPredefinedChangeList; @@ -100,8 +101,8 @@ public class UnshelveWithDialogAction extends DumbAwareAction { chooser.getSelectedList()); } - private static boolean hasNotAllSelectedChanges(@NotNull Project project, @NotNull ShelvedChangeList list, @Nullable Change[] changes) { - return changes != null && (list.getChanges(project).size() + list.getBinaryFiles().size()) != changes.length; + private static boolean hasNotAllSelectedChanges(@NotNull ShelvedChangeList list, @Nullable Change[] changes) { + return changes != null && (Objects.requireNonNull(list.getChanges()).size() + list.getBinaryFiles().size()) != changes.length; } @Override @@ -118,7 +119,7 @@ public class UnshelveWithDialogAction extends DumbAwareAction { @Nullable Change[] preselectedChanges) { super(project, new UnshelvePatchDefaultExecutor(project, changeList), Collections.emptyList(), ApplyPatchMode.UNSHELVE, patchFile, null, getPredefinedChangeList(changeList, ChangeListManager.getInstance(project)), binaryShelvedPatches, - hasNotAllSelectedChanges(project, changeList, preselectedChanges) ? newArrayList(preselectedChanges) : null, + hasNotAllSelectedChanges(changeList, preselectedChanges) ? newArrayList(preselectedChanges) : null, getChangeListNameForUnshelve(changeList), true); setOKButtonText(VcsBundle.getString("unshelve.changes.action")); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesListView.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesListView.java index 32bec0fba89f..fec415c913f9 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesListView.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesListView.java @@ -55,9 +55,6 @@ public class ChangesListView extends ChangesTree implements DataProvider, DnDAwa super(project, showCheckboxes, true); setDragEnabled(true); - if (showCheckboxes) { - setInclusionHashingStrategy(ChangeListChange.HASHING_STRATEGY); - } } @Override diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesTree.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesTree.java index 6b74bd044734..974027afc806 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesTree.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesTree.java @@ -5,6 +5,7 @@ import com.intellij.ide.CommonActionsManager; import com.intellij.ide.CopyProvider; import com.intellij.ide.DefaultTreeExpander; import com.intellij.ide.TreeExpander; +import com.intellij.ide.dnd.DnDAware; import com.intellij.ide.projectView.impl.ProjectViewTree; import com.intellij.ide.util.PropertiesComponent; import com.intellij.ide.util.treeView.TreeState; @@ -23,6 +24,7 @@ import com.intellij.openapi.vcs.changes.ChangesUtil; import com.intellij.openapi.vcs.changes.issueLinks.TreeLinkMouseListener; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.newvfs.VfsPresentationUtil; +import com.intellij.openapi.wm.impl.IdeGlassPaneImpl; import com.intellij.ui.*; import com.intellij.ui.treeStructure.Tree; import com.intellij.util.ArrayUtilRt; @@ -61,7 +63,8 @@ import static com.intellij.util.ui.ThreeStateCheckBox.State; public abstract class ChangesTree extends Tree implements DataProvider { @NotNull protected final Project myProject; - private final boolean myShowCheckboxes; + private boolean myShowCheckboxes; + @Nullable private ClickListener myCheckBoxClickHandler; private final int myCheckboxWidth; @NotNull private final ChangesGroupingSupport myGroupingSupport; private boolean myIsModelFlat; @@ -102,10 +105,9 @@ public abstract class ChangesTree extends Tree implements DataProvider { final ChangesBrowserNodeRenderer nodeRenderer = new ChangesBrowserNodeRenderer(myProject, this::isShowFlatten, highlightProblems); setCellRenderer(new MyTreeCellRenderer(nodeRenderer)); - if (myShowCheckboxes) { - new MyToggleSelectionAction().registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0)), this); - installCheckBoxClickHandler(); - } + new MyToggleSelectionAction().registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0)), this); + showCheckboxesChanged(); + installEnterKeyHandler(); installDoubleClickHandler(); installTreeLinkHandler(nodeRenderer); @@ -119,14 +121,14 @@ public abstract class ChangesTree extends Tree implements DataProvider { } /** - * There is special logic for {@link com.intellij.ide.dnd.DnDAware} components in - * {@link com.intellij.openapi.wm.impl.IdeGlassPaneImpl#dispatch(AWTEvent)} that doesn't call + * There is special logic for {@link DnDAware} components in + * {@link IdeGlassPaneImpl#dispatch(AWTEvent)} that doesn't call * {@link Component#processMouseEvent(MouseEvent)} in case of mouse clicks over selection. * * So we add "checkbox mouse clicks" handling as a listener. */ - private void installCheckBoxClickHandler() { - new ClickListener() { + private ClickListener installCheckBoxClickHandler() { + ClickListener handler = new ClickListener() { @Override public boolean onClick(@NotNull MouseEvent event, int clickCount) { if (myShowCheckboxes && isEnabled()) { @@ -142,7 +144,10 @@ public abstract class ChangesTree extends Tree implements DataProvider { } return false; } - }.installOn(this); + }; + handler.installOn(this); + + return handler; } protected void installEnterKeyHandler() { @@ -304,6 +309,26 @@ public abstract class ChangesTree extends Tree implements DataProvider { return myShowCheckboxes; } + public void setShowCheckboxes(boolean value) { + boolean oldValue = myShowCheckboxes; + myShowCheckboxes = value; + + if (oldValue != value) { + showCheckboxesChanged(); + } + } + + private void showCheckboxesChanged() { + if (isShowCheckboxes()) { + myCheckBoxClickHandler = installCheckBoxClickHandler(); + } + else if (myCheckBoxClickHandler != null) { + myCheckBoxClickHandler.uninstall(this); + myCheckBoxClickHandler = null; + } + repaint(); + } + private void changeGrouping() { PropertiesComponent.getInstance(myProject).setValues(GROUPING_KEYS, ArrayUtilRt.toStringArray(getGroupingSupport().getGroupingKeys())); @@ -615,10 +640,7 @@ public abstract class ChangesTree extends Tree implements DataProvider { myCheckBox = new ThreeStateCheckBox(); myTextRenderer = textRenderer; - if (myShowCheckboxes) { - add(myCheckBox, BorderLayout.WEST); - } - + add(myCheckBox, BorderLayout.WEST); add(myTextRenderer, BorderLayout.CENTER); setOpaque(false); } @@ -633,25 +655,24 @@ public abstract class ChangesTree extends Tree implements DataProvider { boolean hasFocus) { setBackground(null); - myCheckBox.setBackground(null); - myCheckBox.setOpaque(false); myTextRenderer.setOpaque(false); myTextRenderer.setTransparentIconBackground(true); myTextRenderer.setToolTipText(null); myTextRenderer.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus); - if (myShowCheckboxes) { + + myCheckBox.setBackground(null); + myCheckBox.setOpaque(false); + myCheckBox.setVisible(myShowCheckboxes); + if (myCheckBox.isVisible()) { State state = getNodeStatus((ChangesBrowserNode)value); myCheckBox.setState(state); myCheckBox.setEnabled(tree.isEnabled() && isNodeEnabled((ChangesBrowserNode)value)); - revalidate(); + } + revalidate(); - return this; - } - else { - return myTextRenderer; - } + return this; } @Override @@ -710,6 +731,11 @@ public abstract class ChangesTree extends Tree implements DataProvider { } private class MyToggleSelectionAction extends AnAction implements DumbAware { + @Override + public void update(@NotNull AnActionEvent e) { + e.getPresentation().setEnabledAndVisible(isShowCheckboxes()); + } + @Override public void actionPerformed(@NotNull AnActionEvent e) { List changes = getSelectedUserObjects(); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CurrentBranchComponent.kt b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CurrentBranchComponent.kt index 15b728e5c57f..a76f0a0851da 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CurrentBranchComponent.kt +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CurrentBranchComponent.kt @@ -2,10 +2,11 @@ package com.intellij.openapi.vcs.changes.ui import com.intellij.icons.AllIcons +import com.intellij.openapi.Disposable import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Disposer import com.intellij.openapi.vcs.changes.Change import com.intellij.openapi.vcs.changes.ChangesUtil.getFilePath -import com.intellij.vcs.commit.CommitWorkflowUi import com.intellij.openapi.vcs.changes.ui.ChangesGroupingSupport.Companion.REPOSITORY_GROUPING import com.intellij.openapi.vfs.VirtualFile import com.intellij.ui.ColorUtil @@ -17,9 +18,11 @@ import com.intellij.util.ui.UIUtil.rightArrow import com.intellij.vcs.branch.BranchData import com.intellij.vcs.branch.BranchStateProvider import com.intellij.vcs.branch.LinkedBranchData +import com.intellij.vcs.commit.CommitWorkflowUi import com.intellij.vcsUtil.VcsUtil.getFilePath import java.awt.Color import java.awt.Dimension +import java.beans.PropertyChangeListener import javax.swing.JTree.TREE_MODEL_PROPERTY import javax.swing.UIManager @@ -38,14 +41,19 @@ class CurrentBranchComponent( } init { + isVisible = false icon = AllIcons.Vcs.Branch foreground = TEXT_COLOR - tree.addPropertyChangeListener { e -> + val treeChangeListener = PropertyChangeListener { e -> if (e.propertyName == TREE_MODEL_PROPERTY) { refresh() } } + tree.addPropertyChangeListener(treeChangeListener) + Disposer.register(commitWorkflowUi, Disposable { tree.removePropertyChangeListener(treeChangeListener) }) + + refresh() } override fun getPreferredSize(): Dimension? = if (isVisible) super.getPreferredSize() else emptySize() diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/CommitDialogSettingsPanel.form b/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/CommitDialogSettingsPanel.form index 410fbf5f9f64..b19d4202a649 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/CommitDialogSettingsPanel.form +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/CommitDialogSettingsPanel.form @@ -1,6 +1,6 @@
    - + @@ -10,7 +10,7 @@ - + @@ -19,7 +19,7 @@ - + @@ -37,7 +37,7 @@ - + @@ -45,7 +45,7 @@ - + @@ -53,7 +53,7 @@ - + @@ -61,7 +61,7 @@ - + @@ -80,15 +80,24 @@ - + - + + + + + + + + + + diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/CommitDialogSettingsPanel.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/CommitDialogSettingsPanel.java index 812d4a6bf93b..2fc5ed686f17 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/CommitDialogSettingsPanel.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/CommitDialogSettingsPanel.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-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. package com.intellij.openapi.vcs.configurable; import com.intellij.openapi.Disposable; @@ -7,18 +7,26 @@ import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.ComboBox; import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.vcs.VcsApplicationSettings; import com.intellij.openapi.vcs.VcsConfiguration; import com.intellij.openapi.vcs.VcsShowConfirmationOption; import com.intellij.ui.EnumComboBoxModel; import com.intellij.ui.SimpleListCellRenderer; import com.intellij.ui.components.JBCheckBox; +import com.intellij.util.ui.UI; +import com.intellij.vcs.commit.CommitWorkflowManager; import com.intellij.vcs.commit.message.CommitMessageInspectionsPanel; import org.jetbrains.annotations.NotNull; import javax.swing.*; +import static com.intellij.openapi.application.ApplicationManager.getApplication; + public class CommitDialogSettingsPanel implements ConfigurableUi, Disposable { @NotNull private final Project myProject; + + @SuppressWarnings("unused") private JPanel myCommitFromLocalChangesPanel; + private JBCheckBox myCommitFromLocalChanges; private JBCheckBox myShowUnversionedFiles; private JPanel myMainPanel; private CommitMessageInspectionsPanel myInspectionsPanel; @@ -37,8 +45,24 @@ public class CommitDialogSettingsPanel implements ConfigurableUi() @@ -89,13 +93,15 @@ class ChangesViewCommitPanel(private val changesView: ChangesListView) : BorderL override fun actionPerformed(e: ActionEvent) = fireDefaultExecutorCalled() } private val commitButton = object : JBOptionButton(defaultCommitAction, emptyArray()) { + private val focusManager = IdeFocusManager.getInstance(project) + init { background = BACKGROUND_COLOR optionTooltipText = getDefaultTooltip() isOkToProcessDefaultMnemonics = false } - override fun isDefaultButton() = true + override fun isDefaultButton(): Boolean = focusManager.getFocusedDescendantFor(rootComponent) != null } private val commitLegendCalculator = ChangeInfoCalculator() private val commitLegend = CommitLegendPanel(commitLegendCalculator) @@ -105,11 +111,17 @@ class ChangesViewCommitPanel(private val changesView: ChangesListView) : BorderL buildLayout() - changesView.setInclusionListener { inclusionEventDispatcher.multicaster.inclusionChanged() } + with(changesView) { + setInclusionHashingStrategy(ChangeListChange.HASHING_STRATEGY) + setInclusionListener { inclusionEventDispatcher.multicaster.inclusionChanged() } + isShowCheckboxes = true + } addInclusionListener(object : InclusionListener { override fun inclusionChanged() = this@ChangesViewCommitPanel.inclusionChanged() }, this) + + setupShortcuts(rootComponent) } private fun buildLayout() { @@ -120,7 +132,7 @@ class ChangesViewCommitPanel(private val changesView: ChangesListView) : BorderL }.withBackground(BACKGROUND_COLOR) val centerPanel = simplePanel(commitMessage).addToBottom(buttonPanel) - addToCenter(centerPanel).addToLeft(toolbar.component).withBorder(createBorder(JBColor.border(), SideBorder.TOP)) + addToCenter(centerPanel).addToLeft(toolbar.component) withPreferredHeight(85) } @@ -137,11 +149,11 @@ class ChangesViewCommitPanel(private val changesView: ChangesListView) : BorderL private fun fireDefaultExecutorCalled() = executorEventDispatcher.multicaster.executorCalled(null) - fun setupShortcuts(component: JComponent) { - DefaultCommitAction().registerCustomShortcutSet(DEFAULT_COMMIT_ACTION_SHORTCUT, component) + private fun setupShortcuts(component: JComponent) { + DefaultCommitAction().registerCustomShortcutSet(DEFAULT_COMMIT_ACTION_SHORTCUT, component, this) DumbAwareAction.create { if (commitButton.isEnabled) commitButton.showPopup() - }.registerCustomShortcutSet(getDefaultShowPopupShortcut(), component) + }.registerCustomShortcutSet(getDefaultShowPopupShortcut(), component, this) } override val commitMessageUi: CommitMessageUi get() = commitMessage @@ -229,7 +241,14 @@ class ChangesViewCommitPanel(private val changesView: ChangesListView) : BorderL override fun startBeforeCommitChecks() = Unit override fun endBeforeCommitChecks(result: CheckinHandler.ReturnResult) = Unit - override fun dispose() = Unit + override fun dispose() { + with(changesView) { + isShowCheckboxes = false + setInclusionListener(null) + clearInclusion() + setInclusionHashingStrategy(canonicalStrategy()) + } + } inner class DefaultCommitAction : DumbAwareAction() { override fun update(e: AnActionEvent) { diff --git a/platform/vcs-impl/src/com/intellij/vcs/commit/ChangesViewCommitWorkflow.kt b/platform/vcs-impl/src/com/intellij/vcs/commit/ChangesViewCommitWorkflow.kt index 348c7510c44c..578e0fd52b83 100644 --- a/platform/vcs-impl/src/com/intellij/vcs/commit/ChangesViewCommitWorkflow.kt +++ b/platform/vcs-impl/src/com/intellij/vcs/commit/ChangesViewCommitWorkflow.kt @@ -4,10 +4,7 @@ package com.intellij.vcs.commit import com.intellij.openapi.application.runInEdt import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.project.Project -import com.intellij.openapi.util.Disposer import com.intellij.openapi.vcs.ProjectLevelVcsManager -import com.intellij.openapi.vcs.ProjectLevelVcsManager.VCS_CONFIGURATION_CHANGED -import com.intellij.openapi.vcs.VcsListener import com.intellij.openapi.vcs.changes.* import com.intellij.openapi.vcs.checkin.CheckinHandler import com.intellij.openapi.vcs.impl.PartialChangesUtil @@ -26,12 +23,7 @@ class ChangesViewCommitWorkflow(project: Project) : AbstractCommitWorkflow(proje internal lateinit var commitState: CommitState init { - val connection = project.messageBus.connect() - connection.subscribe(VCS_CONFIGURATION_CHANGED, VcsListener { - Disposer.dispose(connection) - - runInEdt { updateVcses(vcsManager.allActiveVcss.toSet()) } - }) + updateVcses(vcsManager.allActiveVcss.toSet()) } internal fun getAffectedChangeList(changes: Collection): LocalChangeList = diff --git a/platform/vcs-impl/src/com/intellij/vcs/commit/ChangesViewCommitWorkflowHandler.kt b/platform/vcs-impl/src/com/intellij/vcs/commit/ChangesViewCommitWorkflowHandler.kt index 57fa516da96c..6b461cdad65a 100644 --- a/platform/vcs-impl/src/com/intellij/vcs/commit/ChangesViewCommitWorkflowHandler.kt +++ b/platform/vcs-impl/src/com/intellij/vcs/commit/ChangesViewCommitWorkflowHandler.kt @@ -32,7 +32,7 @@ class ChangesViewCommitWorkflowHandler( ui.addDataProvider(createDataProvider()) ui.addInclusionListener(this, this) - updateDefaultCommitAction() + vcsesChanged() // as currently vcses are set before handler subscribes to corresponding event } private fun ensureCommitOptions(): CommitOptions { diff --git a/platform/vcs-impl/src/com/intellij/vcs/commit/CommitWorkflowManager.kt b/platform/vcs-impl/src/com/intellij/vcs/commit/CommitWorkflowManager.kt new file mode 100644 index 000000000000..b4cb72a07c4e --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/vcs/commit/CommitWorkflowManager.kt @@ -0,0 +1,69 @@ +// 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. +package com.intellij.vcs.commit + +import com.intellij.application.subscribe +import com.intellij.openapi.application.runInEdt +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.registry.Registry +import com.intellij.openapi.util.registry.RegistryValue +import com.intellij.openapi.util.registry.RegistryValueListener +import com.intellij.openapi.vcs.ProjectLevelVcsManager +import com.intellij.openapi.vcs.ProjectLevelVcsManager.VCS_CONFIGURATION_CHANGED +import com.intellij.openapi.vcs.VcsApplicationSettings +import com.intellij.openapi.vcs.VcsListener +import com.intellij.openapi.vcs.VcsType +import com.intellij.openapi.vcs.changes.ChangesViewManager +import com.intellij.openapi.vcs.impl.ProjectLevelVcsManagerImpl +import com.intellij.openapi.vcs.impl.VcsInitObject +import com.intellij.util.messages.Topic +import java.util.* + +private val isNonModalCommit = Registry.get("vcs.non.modal.commit") +private val appSettings = VcsApplicationSettings.getInstance() + +internal class CommitWorkflowManager(private val project: Project) { + private val changesViewManager = ChangesViewManager.getInstance(project) as ChangesViewManager + private val vcsManager = ProjectLevelVcsManager.getInstance(project) as ProjectLevelVcsManagerImpl + + init { + vcsManager.addInitializationRequest(VcsInitObject.AFTER_COMMON) { + runInEdt { + subscribeToChanges() + updateWorkflow() + } + } + } + + private fun updateWorkflow() = changesViewManager.updateCommitWorkflow(isNonModal()) + + private fun isNonModal(): Boolean { + if (isNonModalCommit.asBoolean()) return true + if (!appSettings.COMMIT_FROM_LOCAL_CHANGES) return false + + return vcsManager.allActiveVcss.all { it.type == VcsType.distributed } + } + + private fun subscribeToChanges() { + isNonModalCommit.addListener(object : RegistryValueListener.Adapter() { + override fun afterValueChanged(value: RegistryValue) = updateWorkflow() + }, project) + + SETTINGS.subscribe(project, object : SettingsListener { + override fun settingsChanged() = updateWorkflow() + }) + + VCS_CONFIGURATION_CHANGED.subscribe(project, VcsListener { runInEdt { updateWorkflow() } }) + } + + companion object { + @JvmField + val SETTINGS: Topic = Topic.create("Commit Workflow Settings", SettingsListener::class.java) + + @JvmStatic + fun install(project: Project) = CommitWorkflowManager(project) + } + + interface SettingsListener : EventListener { + fun settingsChanged() + } +} \ No newline at end of file diff --git a/platform/vcs-impl/src/com/intellij/vcsUtil/VcsImplUtil.java b/platform/vcs-impl/src/com/intellij/vcsUtil/VcsImplUtil.java index 49fb55c756ec..6b5537646532 100644 --- a/platform/vcs-impl/src/com/intellij/vcsUtil/VcsImplUtil.java +++ b/platform/vcs-impl/src/com/intellij/vcsUtil/VcsImplUtil.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-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. package com.intellij.vcsUtil; import com.intellij.openapi.application.ReadAction; @@ -6,7 +6,6 @@ import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.vcs.AbstractVcs; import com.intellij.openapi.vcs.FileStatus; import com.intellij.openapi.vcs.changes.ChangeListManager; @@ -62,10 +61,6 @@ public class VcsImplUtil { return repositoryPath.isEmpty() ? root.getName() : repositoryPath; } - public static boolean isNonModalCommit() { - return Registry.is("vcs.non.modal.commit"); - } - @Nullable public static IgnoredFileContentProvider findIgnoredFileContentProvider(@NotNull Project project, @NotNull AbstractVcs vcs) { diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/table/RootCellRenderer.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/table/RootCellRenderer.java index 4f2aa4cbb25b..baaad76095a7 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/table/RootCellRenderer.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/table/RootCellRenderer.java @@ -5,6 +5,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.ScrollingUtil; import com.intellij.ui.SimpleColoredRenderer; import com.intellij.ui.scale.JBUIScale; +import com.intellij.util.ObjectUtils; import com.intellij.util.ui.UIUtil; import com.intellij.vcs.log.impl.VcsLogUiProperties; import com.intellij.vcs.log.ui.VcsLogColorManager; @@ -13,6 +14,7 @@ import org.jetbrains.annotations.NotNull; import javax.swing.*; import javax.swing.table.TableCellRenderer; import java.awt.*; +import java.util.Objects; import static com.intellij.vcs.log.impl.CommonUiProperties.SHOW_ROOT_NAMES; @@ -47,37 +49,19 @@ class RootCellRenderer extends SimpleColoredRenderer implements TableCellRendere @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { - String text; - Color color; - - if (value instanceof VirtualFile) { - VirtualFile root = (VirtualFile)value; - int readableRow = ScrollingUtil.getReadableRow(table, Math.round(table.getRowHeight() * 0.5f)); - if (row < readableRow) { - text = ""; - } - else if (row == 0 || !value.equals(table.getModel().getValueAt(row - 1, column)) || readableRow == row) { - text = root.getName(); - } - else { - text = ""; - } - color = VcsLogGraphTable.getRootBackgroundColor(root, myColorManager); - } - else { - text = ""; - color = UIUtil.getTableBackground(isSelected); - } - clear(); - myColor = color; - Color background = ((VcsLogGraphTable)table).getStyle(row, column, hasFocus, isSelected).getBackground(); - assert background != null; - myBorderColor = background; - setForeground(UIUtil.getTableForeground(false)); + + VirtualFile root = (VirtualFile)value; + + myColor = root == null ? UIUtil.getTableBackground(isSelected, hasFocus) : + VcsLogGraphTable.getRootBackgroundColor(root, myColorManager); + myBorderColor = ObjectUtils.assertNotNull(((VcsLogGraphTable)table).getStyle(row, column, hasFocus, isSelected).getBackground()); + setForeground(UIUtil.getTableForeground(false, hasFocus)); if (myProperties.exists(SHOW_ROOT_NAMES) && myProperties.get(SHOW_ROOT_NAMES)) { - append(text); + if (isTextShown(table, value, row, column)) { + append(root == null ? "" : root.getName()); + } isNarrow = false; } else { @@ -88,6 +72,14 @@ class RootCellRenderer extends SimpleColoredRenderer implements TableCellRendere return this; } + private static boolean isTextShown(JTable table, Object value, int row, int column) { + int readableRow = ScrollingUtil.getReadableRow(table, Math.round(table.getRowHeight() * 0.5f)); + if (row < readableRow) { + return false; + } + return row == 0 || readableRow == row || !Objects.equals(value, table.getModel().getValueAt(row - 1, column)); + } + @Override public void setBackground(Color bg) { myBorderColor = bg; diff --git a/platform/vcs-tests/testData/shelf/migrateWithResources/after/test.xml b/platform/vcs-tests/testData/shelf/migrateWithResources/after/test2.xml similarity index 85% rename from platform/vcs-tests/testData/shelf/migrateWithResources/after/test.xml rename to platform/vcs-tests/testData/shelf/migrateWithResources/after/test2.xml index 8969ab520b81..c90f50d6d421 100644 --- a/platform/vcs-tests/testData/shelf/migrateWithResources/after/test.xml +++ b/platform/vcs-tests/testData/shelf/migrateWithResources/after/test2.xml @@ -1,4 +1,4 @@ - +