From 44627476189908669c0f1f2d5521d863cb8078a8 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Fri, 3 Jun 2022 14:58:33 +0200 Subject: [PATCH 01/12] feat(gui): parse filters for usages/usefulness --- .../filters/AbstractPythonFilter.test.ts | 13 ++-- .../model/filters/AbstractPythonFilter.ts | 47 ++++++++------- .../model/filters/AnnotationFilter.ts | 23 +++---- .../model/filters/ConjunctiveFilter.ts | 19 +++--- .../model/filters/DeclarationTypeFilter.ts | 11 ++-- .../packageData/model/filters/NameFilter.ts | 23 +++---- .../model/filters/NegatedFilter.ts | 17 +++--- .../packageData/model/filters/UsageFilter.ts | 40 +++++++++++++ .../model/filters/UsefulnessFilter.ts | 40 +++++++++++++ .../model/filters/VisibilityFilter.ts | 23 +++---- .../packageData/model/filters/comparisons.ts | 19 ++++++ .../model/filters/filterFactory.test.ts | 36 +++++++++++ .../model/filters/filterFactory.ts | 60 +++++++++++++++++-- .../selectionView/SelectionView.tsx | 4 +- 14 files changed, 283 insertions(+), 92 deletions(-) create mode 100644 api-editor/gui/src/features/packageData/model/filters/UsageFilter.ts create mode 100644 api-editor/gui/src/features/packageData/model/filters/UsefulnessFilter.ts create mode 100644 api-editor/gui/src/features/packageData/model/filters/comparisons.ts diff --git a/api-editor/gui/src/features/packageData/model/filters/AbstractPythonFilter.test.ts b/api-editor/gui/src/features/packageData/model/filters/AbstractPythonFilter.test.ts index 0b5df105c..23f47721c 100644 --- a/api-editor/gui/src/features/packageData/model/filters/AbstractPythonFilter.test.ts +++ b/api-editor/gui/src/features/packageData/model/filters/AbstractPythonFilter.test.ts @@ -6,8 +6,9 @@ import PythonModule from '../PythonModule'; import PythonClass from '../PythonClass'; import PythonFunction from '../PythonFunction'; import NameFilter from './NameFilter'; -import { initialState } from '../../../annotations/annotationSlice'; +import { initialState as annotations } from '../../../annotations/annotationSlice'; import PythonDeclaration from '../PythonDeclaration'; +import {UsageCountStore} from "../../../usages/model/UsageCountStore"; let pythonPackage: PythonPackage; @@ -66,7 +67,7 @@ beforeEach(() => { describe('AbstractPythonFilter::applyToPackage', () => { test('keeps modules for which the filter returns true, their ancestors, and their descendants', () => { const filter = new NameFilter('test_module_1'); - const filteredPackage = filter.applyToPackage(pythonPackage, initialState); + const filteredPackage = filter.applyToPackage(pythonPackage, annotations, new UsageCountStore()); const modules = filteredPackage.modules; expect(names(modules)).toEqual(['test_module_1']); @@ -89,7 +90,7 @@ describe('AbstractPythonFilter::applyToPackage', () => { test('keeps classes for which the filter returns true, their ancestors, and their descendants', () => { const filter = new NameFilter('test_class_1'); - const filteredPackage = filter.applyToPackage(pythonPackage, initialState); + const filteredPackage = filter.applyToPackage(pythonPackage, annotations, new UsageCountStore()); const modules = filteredPackage.modules; expect(names(modules)).toEqual(['test_module_1']); @@ -109,7 +110,7 @@ describe('AbstractPythonFilter::applyToPackage', () => { test('keeps methods for which the filter returns true, their ancestors, and their descendants', () => { const filter = new NameFilter('test_method_1'); - const filteredPackage = filter.applyToPackage(pythonPackage, initialState); + const filteredPackage = filter.applyToPackage(pythonPackage, annotations, new UsageCountStore()); const modules = filteredPackage.modules; expect(names(modules)).toEqual(['test_module_1']); @@ -129,7 +130,7 @@ describe('AbstractPythonFilter::applyToPackage', () => { test('keeps global functions for which the filter returns true, their ancestors, and their descendants', () => { const filter = new NameFilter('test_global_function_1'); - const filteredPackage = filter.applyToPackage(pythonPackage, initialState); + const filteredPackage = filter.applyToPackage(pythonPackage, annotations, new UsageCountStore()); const modules = filteredPackage.modules; expect(names(modules)).toEqual(['test_module_1']); @@ -146,7 +147,7 @@ describe('AbstractPythonFilter::applyToPackage', () => { test('keeps parameters for which the filter returns true, their ancestors, and their descendants', () => { const filter = new NameFilter('test_parameter_1'); - const filteredPackage = filter.applyToPackage(pythonPackage, initialState); + const filteredPackage = filter.applyToPackage(pythonPackage, annotations, new UsageCountStore()); const modules = filteredPackage.modules; expect(names(modules)).toEqual(['test_module_1']); diff --git a/api-editor/gui/src/features/packageData/model/filters/AbstractPythonFilter.ts b/api-editor/gui/src/features/packageData/model/filters/AbstractPythonFilter.ts index 783a1d393..af055d05d 100644 --- a/api-editor/gui/src/features/packageData/model/filters/AbstractPythonFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/AbstractPythonFilter.ts @@ -3,9 +3,10 @@ import PythonFunction from '../PythonFunction'; import PythonModule from '../PythonModule'; import PythonParameter from '../PythonParameter'; import PythonPackage from '../PythonPackage'; -import { isEmptyList } from '../../../../common/util/listOperations'; +import {isEmptyList} from '../../../../common/util/listOperations'; import PythonDeclaration from '../PythonDeclaration'; -import { AnnotationsState } from '../../../annotations/annotationSlice'; +import {AnnotationsState} from '../../../annotations/annotationSlice'; +import {UsageCountStore} from "../../../usages/model/UsageCountStore"; /** * An abstract base class for filters of Python declarations. To create a new filter create a new subclass and override @@ -15,36 +16,36 @@ export default abstract class AbstractPythonFilter { /** * Whether the given module should be kept after filtering. */ - abstract shouldKeepModule(pythonModule: PythonModule, annotations: AnnotationsState): boolean; + abstract shouldKeepModule(pythonModule: PythonModule, annotations: AnnotationsState, usages: UsageCountStore): boolean; /** * Whether the given class should be kept after filtering. */ - abstract shouldKeepClass(pythonClass: PythonClass, annotations: AnnotationsState): boolean; + abstract shouldKeepClass(pythonClass: PythonClass, annotations: AnnotationsState, usages: UsageCountStore): boolean; /** * Whether the given function should be kept after filtering. */ - abstract shouldKeepFunction(pythonFunction: PythonFunction, annotations: AnnotationsState): boolean; + abstract shouldKeepFunction(pythonFunction: PythonFunction, annotations: AnnotationsState, usages: UsageCountStore): boolean; /** * Whether the given parameter should be kept after filtering. */ - abstract shouldKeepParameter(pythonParameter: PythonParameter, annotations: AnnotationsState): boolean; + abstract shouldKeepParameter(pythonParameter: PythonParameter, annotations: AnnotationsState, usages: UsageCountStore): boolean; /** * Whether the given declaration should be kept after filtering. This function generally does not need to be * overridden. */ - shouldKeepDeclaration(pythonDeclaration: PythonDeclaration, annotations: AnnotationsState): boolean { + shouldKeepDeclaration(pythonDeclaration: PythonDeclaration, annotations: AnnotationsState, usages: UsageCountStore): boolean { if (pythonDeclaration instanceof PythonModule) { - return this.shouldKeepModule(pythonDeclaration, annotations); + return this.shouldKeepModule(pythonDeclaration, annotations, usages); } else if (pythonDeclaration instanceof PythonClass) { - return this.shouldKeepClass(pythonDeclaration, annotations); + return this.shouldKeepClass(pythonDeclaration, annotations, usages); } else if (pythonDeclaration instanceof PythonFunction) { - return this.shouldKeepFunction(pythonDeclaration, annotations); + return this.shouldKeepFunction(pythonDeclaration, annotations, usages); } else if (pythonDeclaration instanceof PythonParameter) { - return this.shouldKeepParameter(pythonDeclaration, annotations); + return this.shouldKeepParameter(pythonDeclaration, annotations, usages); } else { return true; } @@ -54,10 +55,10 @@ export default abstract class AbstractPythonFilter { * Applies this filter to the given package and creates a package with filtered modules. This function should not be * overridden. */ - applyToPackage(pythonPackage: PythonPackage, annotations: AnnotationsState): PythonPackage { + applyToPackage(pythonPackage: PythonPackage, annotations: AnnotationsState, usages: UsageCountStore): PythonPackage { // Filter modules const modules = pythonPackage.modules - .map((it) => this.applyToModule(it, annotations)) + .map((it) => this.applyToModule(it, annotations, usages)) .filter((it) => it !== null); // Create filtered package @@ -73,20 +74,20 @@ export default abstract class AbstractPythonFilter { * Applies this filter to the given module and creates a module with filtered classes and functions. Returns null if * the module should be removed. */ - private applyToModule(pythonModule: PythonModule, annotations: AnnotationsState): PythonModule | null { + private applyToModule(pythonModule: PythonModule, annotations: AnnotationsState, usages: UsageCountStore): PythonModule | null { // If the module is kept, keep the entire subtree - if (this.shouldKeepModule(pythonModule, annotations)) { + if (this.shouldKeepModule(pythonModule, annotations, usages)) { return pythonModule; } // Filter classes const classes = pythonModule.classes - .map((it) => this.applyToClass(it, annotations)) + .map((it) => this.applyToClass(it, annotations, usages)) .filter((it) => it !== null); // Filter functions const functions = pythonModule.functions - .map((it) => this.applyToFunction(it, annotations)) + .map((it) => this.applyToFunction(it, annotations, usages)) .filter((it) => it !== null); // Return null if all classes and functions are removed @@ -108,15 +109,15 @@ export default abstract class AbstractPythonFilter { * Applies this filter to the given class and creates a class with filtered methods. Returns null if the class * should be removed. */ - private applyToClass(pythonClass: PythonClass, annotations: AnnotationsState): PythonClass | null { + private applyToClass(pythonClass: PythonClass, annotations: AnnotationsState, usages: UsageCountStore): PythonClass | null { // If the class is kept, keep the entire subtree - if (this.shouldKeepClass(pythonClass, annotations)) { + if (this.shouldKeepClass(pythonClass, annotations, usages)) { return pythonClass; } // Filter methods const methods = pythonClass.methods - .map((it) => this.applyToFunction(it, annotations)) + .map((it) => this.applyToFunction(it, annotations, usages)) .filter((it) => it !== null); // Return null if all methods are removed @@ -141,14 +142,14 @@ export default abstract class AbstractPythonFilter { * Applies this filter to the given function and creates a function with filtered parameters. Returns null if the * function should be removed. */ - private applyToFunction(pythonFunction: PythonFunction, annotations: AnnotationsState): PythonFunction | null { + private applyToFunction(pythonFunction: PythonFunction, annotations: AnnotationsState, usages: UsageCountStore): PythonFunction | null { // If the function is kept, keep the entire subtree - if (this.shouldKeepFunction(pythonFunction, annotations)) { + if (this.shouldKeepFunction(pythonFunction, annotations, usages)) { return pythonFunction; } // Filter parameters - const parameters = pythonFunction.parameters.filter((it) => this.shouldKeepParameter(it, annotations)); + const parameters = pythonFunction.parameters.filter((it) => this.shouldKeepParameter(it, annotations, usages)); // Return null if all parameters are removed if (isEmptyList(parameters)) { diff --git a/api-editor/gui/src/features/packageData/model/filters/AnnotationFilter.ts b/api-editor/gui/src/features/packageData/model/filters/AnnotationFilter.ts index 88b92e775..3975e70bf 100644 --- a/api-editor/gui/src/features/packageData/model/filters/AnnotationFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/AnnotationFilter.ts @@ -4,7 +4,8 @@ import PythonModule from '../PythonModule'; import PythonParameter from '../PythonParameter'; import AbstractPythonFilter from './AbstractPythonFilter'; import PythonDeclaration from '../PythonDeclaration'; -import { AnnotationsState } from '../../../annotations/annotationSlice'; +import {AnnotationsState} from '../../../annotations/annotationSlice'; +import {UsageCountStore} from "../../../usages/model/UsageCountStore"; /** * Keeps only declarations with either an arbitrary or a specific annotation. @@ -18,24 +19,24 @@ export default class AnnotationFilter extends AbstractPythonFilter { super(); } - shouldKeepModule(pythonModule: PythonModule, annotations: AnnotationsState): boolean { - return this.shouldKeepDeclaration(pythonModule, annotations); + shouldKeepModule(pythonModule: PythonModule, annotations: AnnotationsState, usages: UsageCountStore): boolean { + return this.shouldKeepDeclaration(pythonModule, annotations, usages); } - shouldKeepClass(pythonClass: PythonClass, annotations: AnnotationsState): boolean { - return this.shouldKeepDeclaration(pythonClass, annotations); + shouldKeepClass(pythonClass: PythonClass, annotations: AnnotationsState, usages: UsageCountStore): boolean { + return this.shouldKeepDeclaration(pythonClass, annotations, usages); } - shouldKeepFunction(pythonFunction: PythonFunction, annotations: AnnotationsState): boolean { - return this.shouldKeepDeclaration(pythonFunction, annotations); + shouldKeepFunction(pythonFunction: PythonFunction, annotations: AnnotationsState, usages: UsageCountStore): boolean { + return this.shouldKeepDeclaration(pythonFunction, annotations, usages); } - shouldKeepParameter(pythonParameter: PythonParameter, annotations: AnnotationsState): boolean { - return this.shouldKeepDeclaration(pythonParameter, annotations); + shouldKeepParameter(pythonParameter: PythonParameter, annotations: AnnotationsState, usages: UsageCountStore): boolean { + return this.shouldKeepDeclaration(pythonParameter, annotations, usages); } - shouldKeepDeclaration(declaration: PythonDeclaration, annotations: AnnotationsState): boolean { - const id = declaration.pathAsString(); + shouldKeepDeclaration(pythonDeclaration: PythonDeclaration, annotations: AnnotationsState, usages: UsageCountStore): boolean { + const id = pythonDeclaration.pathAsString(); switch (this.type) { case AnnotationType.Any: diff --git a/api-editor/gui/src/features/packageData/model/filters/ConjunctiveFilter.ts b/api-editor/gui/src/features/packageData/model/filters/ConjunctiveFilter.ts index ddbe181b1..d53edc458 100644 --- a/api-editor/gui/src/features/packageData/model/filters/ConjunctiveFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/ConjunctiveFilter.ts @@ -3,7 +3,8 @@ import PythonClass from '../PythonClass'; import PythonFunction from '../PythonFunction'; import PythonParameter from '../PythonParameter'; import PythonModule from '../PythonModule'; -import { AnnotationsState } from '../../../annotations/annotationSlice'; +import {AnnotationsState} from '../../../annotations/annotationSlice'; +import {UsageCountStore} from "../../../usages/model/UsageCountStore"; /** * Keeps declarations iff all contained filters keep it. @@ -16,19 +17,19 @@ export class ConjunctiveFilter extends AbstractPythonFilter { super(); } - shouldKeepModule(pythonModule: PythonModule, annotations: AnnotationsState): boolean { - return this.filters.every((it) => it.shouldKeepModule(pythonModule, annotations)); + shouldKeepModule(pythonModule: PythonModule, annotations: AnnotationsState, usages: UsageCountStore): boolean { + return this.filters.every((it) => it.shouldKeepModule(pythonModule, annotations, usages)); } - shouldKeepClass(pythonClass: PythonClass, annotations: AnnotationsState): boolean { - return this.filters.every((it) => it.shouldKeepClass(pythonClass, annotations)); + shouldKeepClass(pythonClass: PythonClass, annotations: AnnotationsState, usages: UsageCountStore): boolean { + return this.filters.every((it) => it.shouldKeepClass(pythonClass, annotations, usages)); } - shouldKeepFunction(pythonFunction: PythonFunction, annotations: AnnotationsState): boolean { - return this.filters.every((it) => it.shouldKeepFunction(pythonFunction, annotations)); + shouldKeepFunction(pythonFunction: PythonFunction, annotations: AnnotationsState, usages: UsageCountStore): boolean { + return this.filters.every((it) => it.shouldKeepFunction(pythonFunction, annotations, usages)); } - shouldKeepParameter(pythonParameter: PythonParameter, annotations: AnnotationsState): boolean { - return this.filters.every((it) => it.shouldKeepParameter(pythonParameter, annotations)); + shouldKeepParameter(pythonParameter: PythonParameter, annotations: AnnotationsState, usages: UsageCountStore): boolean { + return this.filters.every((it) => it.shouldKeepParameter(pythonParameter, annotations, usages)); } } diff --git a/api-editor/gui/src/features/packageData/model/filters/DeclarationTypeFilter.ts b/api-editor/gui/src/features/packageData/model/filters/DeclarationTypeFilter.ts index b337216f2..3604028fe 100644 --- a/api-editor/gui/src/features/packageData/model/filters/DeclarationTypeFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/DeclarationTypeFilter.ts @@ -3,7 +3,8 @@ import PythonFunction from '../PythonFunction'; import PythonModule from '../PythonModule'; import PythonParameter from '../PythonParameter'; import AbstractPythonFilter from './AbstractPythonFilter'; -import { AnnotationsState } from '../../../annotations/annotationSlice'; +import {AnnotationsState} from '../../../annotations/annotationSlice'; +import {UsageCountStore} from "../../../usages/model/UsageCountStore"; /** * Keeps only declarations of a specified type (module/class/function/parameter). @@ -16,19 +17,19 @@ export default class DeclarationTypeFilter extends AbstractPythonFilter { super(); } - shouldKeepModule(pythonModule: PythonModule, annotations: AnnotationsState): boolean { + shouldKeepModule(pythonModule: PythonModule, annotations: AnnotationsState, usages: UsageCountStore): boolean { return this.type === DeclarationType.Module; } - shouldKeepClass(pythonClass: PythonClass, annotations: AnnotationsState): boolean { + shouldKeepClass(pythonClass: PythonClass, annotations: AnnotationsState, usages: UsageCountStore): boolean { return this.type === DeclarationType.Class; } - shouldKeepFunction(pythonFunction: PythonFunction, annotations: AnnotationsState): boolean { + shouldKeepFunction(pythonFunction: PythonFunction, annotations: AnnotationsState, usages: UsageCountStore): boolean { return this.type === DeclarationType.Function; } - shouldKeepParameter(pythonParameter: PythonParameter, annotations: AnnotationsState): boolean { + shouldKeepParameter(pythonParameter: PythonParameter, annotations: AnnotationsState, usages: UsageCountStore): boolean { return this.type === DeclarationType.Parameter; } } diff --git a/api-editor/gui/src/features/packageData/model/filters/NameFilter.ts b/api-editor/gui/src/features/packageData/model/filters/NameFilter.ts index 6e1fb4350..4ba346859 100644 --- a/api-editor/gui/src/features/packageData/model/filters/NameFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/NameFilter.ts @@ -4,7 +4,8 @@ import PythonModule from '../PythonModule'; import PythonParameter from '../PythonParameter'; import AbstractPythonFilter from './AbstractPythonFilter'; import PythonDeclaration from '../PythonDeclaration'; -import { AnnotationsState } from '../../../annotations/annotationSlice'; +import {AnnotationsState} from '../../../annotations/annotationSlice'; +import {UsageCountStore} from "../../../usages/model/UsageCountStore"; /** * Keeps only declarations that have a given string in their name. @@ -17,23 +18,23 @@ export default class NameFilter extends AbstractPythonFilter { super(); } - shouldKeepModule(pythonModule: PythonModule, annotations: AnnotationsState): boolean { - return this.shouldKeepDeclaration(pythonModule, annotations); + shouldKeepModule(pythonModule: PythonModule, annotations: AnnotationsState, usages: UsageCountStore): boolean { + return this.shouldKeepDeclaration(pythonModule, annotations, usages); } - shouldKeepClass(pythonClass: PythonClass, annotations: AnnotationsState): boolean { - return this.shouldKeepDeclaration(pythonClass, annotations); + shouldKeepClass(pythonClass: PythonClass, annotations: AnnotationsState, usages: UsageCountStore): boolean { + return this.shouldKeepDeclaration(pythonClass, annotations, usages); } - shouldKeepFunction(pythonFunction: PythonFunction, annotations: AnnotationsState): boolean { - return this.shouldKeepDeclaration(pythonFunction, annotations); + shouldKeepFunction(pythonFunction: PythonFunction, annotations: AnnotationsState, usages: UsageCountStore): boolean { + return this.shouldKeepDeclaration(pythonFunction, annotations, usages); } - shouldKeepParameter(pythonParameter: PythonParameter, annotations: AnnotationsState): boolean { - return this.shouldKeepDeclaration(pythonParameter, annotations); + shouldKeepParameter(pythonParameter: PythonParameter, annotations: AnnotationsState, usages: UsageCountStore): boolean { + return this.shouldKeepDeclaration(pythonParameter, annotations, usages); } - shouldKeepDeclaration(declaration: PythonDeclaration, annotations: AnnotationsState): boolean { - return declaration.name.toLowerCase().includes(this.substring.toLowerCase()); + shouldKeepDeclaration(pythonDeclaration: PythonDeclaration, annotations: AnnotationsState, usages: UsageCountStore): boolean { + return pythonDeclaration.name.toLowerCase().includes(this.substring.toLowerCase()); } } diff --git a/api-editor/gui/src/features/packageData/model/filters/NegatedFilter.ts b/api-editor/gui/src/features/packageData/model/filters/NegatedFilter.ts index f4a2d4a57..49e97a0ba 100644 --- a/api-editor/gui/src/features/packageData/model/filters/NegatedFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/NegatedFilter.ts @@ -4,6 +4,7 @@ import PythonFunction from '../PythonFunction'; import PythonParameter from '../PythonParameter'; import PythonModule from '../PythonModule'; import { AnnotationsState } from '../../../annotations/annotationSlice'; +import {UsageCountStore} from "../../../usages/model/UsageCountStore"; /** * Keeps declarations iff the contained filter discards it. @@ -16,19 +17,19 @@ export class NegatedFilter extends AbstractPythonFilter { super(); } - shouldKeepModule(pythonModule: PythonModule, annotations: AnnotationsState): boolean { - return !this.filter.shouldKeepModule(pythonModule, annotations); + shouldKeepModule(pythonModule: PythonModule, annotations: AnnotationsState, usages: UsageCountStore): boolean { + return !this.filter.shouldKeepModule(pythonModule, annotations, usages); } - shouldKeepClass(pythonClass: PythonClass, annotations: AnnotationsState): boolean { - return !this.filter.shouldKeepClass(pythonClass, annotations); + shouldKeepClass(pythonClass: PythonClass, annotations: AnnotationsState, usages: UsageCountStore): boolean { + return !this.filter.shouldKeepClass(pythonClass, annotations, usages); } - shouldKeepFunction(pythonFunction: PythonFunction, annotations: AnnotationsState): boolean { - return !this.filter.shouldKeepFunction(pythonFunction, annotations); + shouldKeepFunction(pythonFunction: PythonFunction, annotations: AnnotationsState, usages: UsageCountStore): boolean { + return !this.filter.shouldKeepFunction(pythonFunction, annotations, usages); } - shouldKeepParameter(pythonParameter: PythonParameter, annotations: AnnotationsState): boolean { - return !this.filter.shouldKeepParameter(pythonParameter, annotations); + shouldKeepParameter(pythonParameter: PythonParameter, annotations: AnnotationsState, usages: UsageCountStore): boolean { + return !this.filter.shouldKeepParameter(pythonParameter, annotations, usages); } } diff --git a/api-editor/gui/src/features/packageData/model/filters/UsageFilter.ts b/api-editor/gui/src/features/packageData/model/filters/UsageFilter.ts new file mode 100644 index 000000000..6f2770594 --- /dev/null +++ b/api-editor/gui/src/features/packageData/model/filters/UsageFilter.ts @@ -0,0 +1,40 @@ +import PythonClass from '../PythonClass'; +import PythonFunction from '../PythonFunction'; +import PythonModule from '../PythonModule'; +import PythonParameter from '../PythonParameter'; +import AbstractPythonFilter from './AbstractPythonFilter'; +import {AnnotationsState} from '../../../annotations/annotationSlice'; +import {UsageCountStore} from "../../../usages/model/UsageCountStore"; + +/** + * Keeps only declarations are used a certain number of times. + */ +export default class UsageFilter extends AbstractPythonFilter { + + /** + * @param comparison How actual and expected usages should be compared. + * @param expectedUsage The expected number of usages. + */ + constructor( + readonly comparison: (actualUsage: number, expectedUsage: number) => boolean, + readonly expectedUsage: number + ) { + super(); + } + + shouldKeepModule(pythonModule: PythonModule, annotations: AnnotationsState, usages: UsageCountStore): boolean { + return false; + } + + shouldKeepClass(pythonClass: PythonClass, annotations: AnnotationsState, usages: UsageCountStore): boolean { + return false; // TODO + } + + shouldKeepFunction(pythonFunction: PythonFunction, annotations: AnnotationsState, usages: UsageCountStore): boolean { + return false; // TODO + } + + shouldKeepParameter(pythonParameter: PythonParameter, annotations: AnnotationsState, usages: UsageCountStore): boolean { + return false; // TODO + } +} diff --git a/api-editor/gui/src/features/packageData/model/filters/UsefulnessFilter.ts b/api-editor/gui/src/features/packageData/model/filters/UsefulnessFilter.ts new file mode 100644 index 000000000..5608890dc --- /dev/null +++ b/api-editor/gui/src/features/packageData/model/filters/UsefulnessFilter.ts @@ -0,0 +1,40 @@ +import PythonClass from '../PythonClass'; +import PythonFunction from '../PythonFunction'; +import PythonModule from '../PythonModule'; +import PythonParameter from '../PythonParameter'; +import AbstractPythonFilter from './AbstractPythonFilter'; +import {AnnotationsState} from '../../../annotations/annotationSlice'; +import {UsageCountStore} from "../../../usages/model/UsageCountStore"; + +/** + * Keeps only declarations have a certain usefulness. + */ +export default class UsefulnessFilter extends AbstractPythonFilter { + + /** + * @param comparison How actual and expected usefulness should be compared. + * @param expectedUsage The expected usefulness. + */ + constructor( + readonly comparison: (actualUsage: number, expectedUsage: number) => boolean, + readonly expectedUsage: number + ) { + super(); + } + + shouldKeepModule(pythonModule: PythonModule, annotations: AnnotationsState, usages: UsageCountStore): boolean { + return false; + } + + shouldKeepClass(pythonClass: PythonClass, annotations: AnnotationsState, usages: UsageCountStore): boolean { + return false; // TODO + } + + shouldKeepFunction(pythonFunction: PythonFunction, annotations: AnnotationsState, usages: UsageCountStore): boolean { + return false; // TODO + } + + shouldKeepParameter(pythonParameter: PythonParameter, annotations: AnnotationsState, usages: UsageCountStore): boolean { + return false; // TODO + } +} diff --git a/api-editor/gui/src/features/packageData/model/filters/VisibilityFilter.ts b/api-editor/gui/src/features/packageData/model/filters/VisibilityFilter.ts index c8d01ce01..b59170002 100644 --- a/api-editor/gui/src/features/packageData/model/filters/VisibilityFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/VisibilityFilter.ts @@ -4,7 +4,8 @@ import PythonModule from '../PythonModule'; import PythonParameter from '../PythonParameter'; import AbstractPythonFilter from './AbstractPythonFilter'; import PythonDeclaration from '../PythonDeclaration'; -import { AnnotationsState } from '../../../annotations/annotationSlice'; +import {AnnotationsState} from '../../../annotations/annotationSlice'; +import {UsageCountStore} from "../../../usages/model/UsageCountStore"; /** * Keeps only declarations with a specified visibility (public/internal) @@ -17,24 +18,24 @@ export default class VisibilityFilter extends AbstractPythonFilter { super(); } - shouldKeepModule(pythonModule: PythonModule, annotations: AnnotationsState): boolean { - return this.shouldKeepDeclaration(pythonModule, annotations); + shouldKeepModule(pythonModule: PythonModule, annotations: AnnotationsState, usages: UsageCountStore): boolean { + return this.shouldKeepDeclaration(pythonModule, annotations, usages); } - shouldKeepClass(pythonClass: PythonClass, annotations: AnnotationsState): boolean { - return this.shouldKeepDeclaration(pythonClass, annotations); + shouldKeepClass(pythonClass: PythonClass, annotations: AnnotationsState, usages: UsageCountStore): boolean { + return this.shouldKeepDeclaration(pythonClass, annotations, usages); } - shouldKeepFunction(pythonFunction: PythonFunction, annotations: AnnotationsState): boolean { - return this.shouldKeepDeclaration(pythonFunction, annotations); + shouldKeepFunction(pythonFunction: PythonFunction, annotations: AnnotationsState, usages: UsageCountStore): boolean { + return this.shouldKeepDeclaration(pythonFunction, annotations, usages); } - shouldKeepParameter(pythonParameter: PythonParameter, annotations: AnnotationsState): boolean { - return this.shouldKeepDeclaration(pythonParameter, annotations); + shouldKeepParameter(pythonParameter: PythonParameter, annotations: AnnotationsState, usages: UsageCountStore): boolean { + return this.shouldKeepDeclaration(pythonParameter, annotations, usages); } - shouldKeepDeclaration(declaration: PythonDeclaration, annotations: AnnotationsState): boolean { - return declaration.isPublicDeclaration() === (this.visibility === Visibility.Public); + shouldKeepDeclaration(pythonDeclaration: PythonDeclaration, annotations: AnnotationsState, usages: UsageCountStore): boolean { + return pythonDeclaration.isPublicDeclaration() === (this.visibility === Visibility.Public); } } diff --git a/api-editor/gui/src/features/packageData/model/filters/comparisons.ts b/api-editor/gui/src/features/packageData/model/filters/comparisons.ts new file mode 100644 index 000000000..8bac008d5 --- /dev/null +++ b/api-editor/gui/src/features/packageData/model/filters/comparisons.ts @@ -0,0 +1,19 @@ +export const lessThan = function (a: number, b: number): boolean { + return a < b; +} + +export const lessThanOrEqual = function (a: number, b: number): boolean { + return a <= b; +} + +export const equals = function (a: number, b: number): boolean { + return a === b; +} + +export const greaterThanOrEqual = function (a: number, b: number): boolean { + return a >= b; +} + +export const greaterThan = function (a: number, b: number): boolean { + return a > b; +} diff --git a/api-editor/gui/src/features/packageData/model/filters/filterFactory.test.ts b/api-editor/gui/src/features/packageData/model/filters/filterFactory.test.ts index 6359149e6..7a6b9fb9f 100644 --- a/api-editor/gui/src/features/packageData/model/filters/filterFactory.test.ts +++ b/api-editor/gui/src/features/packageData/model/filters/filterFactory.test.ts @@ -2,6 +2,10 @@ import { createFilterFromString } from './filterFactory'; import { ConjunctiveFilter } from './ConjunctiveFilter'; import VisibilityFilter, { Visibility } from './VisibilityFilter'; import { NegatedFilter } from './NegatedFilter'; +import NameFilter from "./NameFilter"; +import UsageFilter from "./UsageFilter"; +import UsefulnessFilter from "./UsefulnessFilter"; +import {greaterThan} from "./comparisons"; describe('createFilterFromString', () => { test('handles an empty string', () => { @@ -51,4 +55,36 @@ describe('createFilterFromString', () => { expect(positiveFilter2).toBeInstanceOf(VisibilityFilter); expect((positiveFilter2 as VisibilityFilter).visibility).toEqual(Visibility.Public); }); + + test('handles name filter', () => { + const completeFilter = createFilterFromString('name:foo'); + expect(completeFilter).toBeInstanceOf(ConjunctiveFilter); + expect((completeFilter as ConjunctiveFilter).filters).toHaveLength(1); + + const positiveFilter = (completeFilter as ConjunctiveFilter).filters[0]; + expect(positiveFilter).toBeInstanceOf(NameFilter); + expect((positiveFilter as NameFilter).substring).toEqual('foo'); + }) + + test('handles usages filter', () => { + const completeFilter = createFilterFromString('usages:>2'); + expect(completeFilter).toBeInstanceOf(ConjunctiveFilter); + expect((completeFilter as ConjunctiveFilter).filters).toHaveLength(1); + + const positiveFilter = (completeFilter as ConjunctiveFilter).filters[0]; + expect(positiveFilter).toBeInstanceOf(UsageFilter); + expect((positiveFilter as UsageFilter).comparison).toEqual(greaterThan); + expect((positiveFilter as UsageFilter).expectedUsage).toEqual(2); + }) + + test('handles usefulness filter', () => { + const completeFilter = createFilterFromString('usefulness:>2'); + expect(completeFilter).toBeInstanceOf(ConjunctiveFilter); + expect((completeFilter as ConjunctiveFilter).filters).toHaveLength(1); + + const positiveFilter = (completeFilter as ConjunctiveFilter).filters[0]; + expect(positiveFilter).toBeInstanceOf(UsefulnessFilter); + expect((positiveFilter as UsefulnessFilter).comparison).toEqual(greaterThan); + expect((positiveFilter as UsefulnessFilter).expectedUsage).toEqual(2); + }) }); diff --git a/api-editor/gui/src/features/packageData/model/filters/filterFactory.ts b/api-editor/gui/src/features/packageData/model/filters/filterFactory.ts index 989821417..be26345e3 100644 --- a/api-editor/gui/src/features/packageData/model/filters/filterFactory.ts +++ b/api-editor/gui/src/features/packageData/model/filters/filterFactory.ts @@ -1,11 +1,14 @@ -import { ConjunctiveFilter } from './ConjunctiveFilter'; +import {ConjunctiveFilter} from './ConjunctiveFilter'; import NameFilter from './NameFilter'; import AbstractPythonFilter from './AbstractPythonFilter'; -import DeclarationTypeFilter, { DeclarationType } from './DeclarationTypeFilter'; -import VisibilityFilter, { Visibility } from './VisibilityFilter'; -import { NegatedFilter } from './NegatedFilter'; -import { Optional } from '../../../../common/util/types'; -import AnnotationFilter, { AnnotationType } from './AnnotationFilter'; +import DeclarationTypeFilter, {DeclarationType} from './DeclarationTypeFilter'; +import VisibilityFilter, {Visibility} from './VisibilityFilter'; +import {NegatedFilter} from './NegatedFilter'; +import {Optional} from '../../../../common/util/types'; +import AnnotationFilter, {AnnotationType} from './AnnotationFilter'; +import UsageFilter from "./UsageFilter"; +import UsefulnessFilter from "./UsefulnessFilter"; +import {equals, greaterThan, greaterThanOrEqual, lessThan, lessThanOrEqual} from "./comparisons"; /** * Creates a filter from the given string. This method handles conjunctions, negations, and non-negated tokens. @@ -100,4 +103,49 @@ function parsePositiveToken(token: string): Optional { if (nameMatch) { return new NameFilter(nameMatch?.groups?.name as string); } + + // Usages + const usageMatch = /^usages(?:(<|<=|>=|>)?)(?\d+)$/.exec(token); + if (usageMatch) { + const comparisonOperator = usageMatch?.groups?.comparison as string; + const comparison = comparisonFunction(comparisonOperator); + if (!comparison) { + return; + } + + const expected = Number.parseInt(usageMatch?.groups?.expected as string, 10); + + return new UsageFilter(comparison, expected) + } + + // Usefulness + const usefulnessMatch = /^usefulness(?:(<|<=|>=|>)?)(?\d+)$/.exec(token); + if (usefulnessMatch) { + const comparisonOperator = usefulnessMatch?.groups?.comparison as string; + const comparison = comparisonFunction(comparisonOperator); + if (!comparison) { + return; + } + + const expected = Number.parseInt(usefulnessMatch?.groups?.expected as string, 10); + + return new UsefulnessFilter(comparison, expected) + } +} + +const comparisonFunction = function (comparisonOperator: string): ((a: number, b: number) => boolean) | null { + switch (comparisonOperator) { + case ':<': + return lessThan; + case ':<=': + return lessThanOrEqual; + case ':': + return equals; + case ':>=': + return greaterThanOrEqual; + case ':>': + return greaterThan; + default: + return null + } } diff --git a/api-editor/gui/src/features/packageData/selectionView/SelectionView.tsx b/api-editor/gui/src/features/packageData/selectionView/SelectionView.tsx index 3b91d7f58..f6251ae3c 100644 --- a/api-editor/gui/src/features/packageData/selectionView/SelectionView.tsx +++ b/api-editor/gui/src/features/packageData/selectionView/SelectionView.tsx @@ -88,7 +88,7 @@ const getNextElementPath = function ( ): string | null { const nextElement = getNextElementInTree(current); if (nextElement != null) { - if (filter.shouldKeepDeclaration(nextElement, annotations)) { + if (filter.shouldKeepDeclaration(nextElement, annotations, null)) { return nextElement.pathAsString(); } return getNextElementPath(nextElement, filter, annotations); @@ -127,7 +127,7 @@ const getPreviousElementPath = function ( ): string | null { const previousElement = getPreviousElementInTree(current); if (previousElement != null) { - if (filter.shouldKeepDeclaration(previousElement, annotations)) { + if (filter.shouldKeepDeclaration(previousElement, annotations, null)) { return previousElement.pathAsString(); } return getPreviousElementPath(previousElement, filter, annotations); From 4604d5f63b973a7f08a234d827e38018f079d7d9 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Fri, 3 Jun 2022 15:06:24 +0200 Subject: [PATCH 02/12] feat(gui): update filter help text --- .../gui/src/common/FilterHelpButton.tsx | 119 ++++++++++++++++++ api-editor/gui/src/common/MenuBar.tsx | 103 ++------------- 2 files changed, 128 insertions(+), 94 deletions(-) create mode 100644 api-editor/gui/src/common/FilterHelpButton.tsx diff --git a/api-editor/gui/src/common/FilterHelpButton.tsx b/api-editor/gui/src/common/FilterHelpButton.tsx new file mode 100644 index 000000000..6100ca979 --- /dev/null +++ b/api-editor/gui/src/common/FilterHelpButton.tsx @@ -0,0 +1,119 @@ +import { + Box, + Icon, + IconButton, + ListItem, + Popover, + PopoverArrow, + PopoverBody, + PopoverCloseButton, + PopoverContent, + PopoverHeader, + PopoverTrigger, + Text as ChakraText, + UnorderedList, +} from '@chakra-ui/react'; +import React from 'react'; + +export const FilterHelpButton = function () { + return ( + + + + } aria-label="help"/> + + + + + Filter Options + + + + + is:[type] + + + Displays only elements that are of the given type. Replace [type] with one of{' '} + module, class, function, parameter. + + + + + is:[visibility] + + + Displays only elements that have the given visibility. Replace [visibility] with one + of public, internal. + + + + + name:xy + + + Displays only elements with names that contain the given string xy. + + + + + annotation:any + + Displays only elements that have been annotated. + + + + annotation:[type] + + + Displays only elements that are annotated with the given type xy. Replace [type] + with one of{' '} + + @attribute, @boundary, @calledAfter, @constant, @enum, @group, @move, @optional, + @pure, @remove, @renaming, @required + + . + + + + + usages:[operator][expected] + + + Displays only elements that are used a certain number of times. Replace [operator] + with one of{' '} + + <, <=, >=, > + + {' '}or omit it to match by equality. Replace [expected] with the expected number of + usages. + + + + + usefulness:[operator][expected] + + + Displays only elements that have a certain usefulness. Replace [operator] + with one of{' '} + + <, <=, >=, > + + {' '}or omit it to match by equality. Replace [expected] with the expected usefulness. + + + + + !filter + + + Displays only elements that do not match the given filter. Possible filters are any + in this list. + + + + + + + + ); +}; diff --git a/api-editor/gui/src/common/MenuBar.tsx b/api-editor/gui/src/common/MenuBar.tsx index ae269e51a..fb4d53ce8 100644 --- a/api-editor/gui/src/common/MenuBar.tsx +++ b/api-editor/gui/src/common/MenuBar.tsx @@ -11,9 +11,7 @@ import { Heading, HStack, Icon, - IconButton, Input, - ListItem, Menu, MenuButton, MenuDivider, @@ -22,28 +20,21 @@ import { MenuItemOption, MenuList, MenuOptionGroup, - Popover, - PopoverArrow, - PopoverBody, - PopoverCloseButton, - PopoverContent, - PopoverHeader, - PopoverTrigger, Spacer, Text as ChakraText, - UnorderedList, useColorMode, VStack, } from '@chakra-ui/react'; -import React, { useRef, useState } from 'react'; -import { FaChevronDown } from 'react-icons/fa'; -import { useAppDispatch, useAppSelector } from '../app/hooks'; -import { resetAnnotations, toggleAnnotationImportDialog } from '../features/annotations/annotationSlice'; +import React, {useRef, useState} from 'react'; +import {FaChevronDown} from 'react-icons/fa'; +import {useAppDispatch, useAppSelector} from '../app/hooks'; +import {resetAnnotations, toggleAnnotationImportDialog} from '../features/annotations/annotationSlice'; import AnnotatedPythonPackageBuilder from '../features/annotatedPackageData/model/AnnotatedPythonPackageBuilder'; import PythonPackage from '../features/packageData/model/PythonPackage'; -import { togglePackageDataImportDialog } from '../features/packageData/packageDataSlice'; -import { Setter } from './util/types'; -import { toggleUsageImportDialog } from '../features/usages/usageSlice'; +import {togglePackageDataImportDialog} from '../features/packageData/packageDataSlice'; +import {Setter} from './util/types'; +import {toggleUsageImportDialog} from '../features/usages/usageSlice'; +import {FilterHelpButton} from "./FilterHelpButton"; interface MenuBarProps { pythonPackage: PythonPackage; @@ -52,82 +43,6 @@ interface MenuBarProps { displayInferErrors: (errors: string[]) => void; } -const HelpButton = function () { - return ( - - - - } aria-label="help" /> - - - - - Filter Options - - - - - is:[type] - - - Displays only elements that are of the given type. Replace [type] with one of{' '} - module, class, function, parameter. - - - - - is:[visibility] - - - Displays only elements that have the given visibility. Replace [visibility] with one - of public, internal. - - - - - name:xy - - - Displays only elements with names that contain the given string xy. - - - - - annotation:any - - Displays only elements that have been annotated. - - - - annotation:[type] - - - Displays only elements that are annotated with the given type xy. Replace [type] - with one of{' '} - - @attribute, @boundary, @calledAfter, @constant, @enum, @group, @move, @optional, - @pure, @remove, @renaming, @required - - . - - - - - !filter - - - Displays only elements that do not match the given filter. Possible filters are any - in this list. - - - - - - - - ); -}; - const DeleteAllAnnotations = function () { const dispatch = useAppDispatch(); const [isOpen, setIsOpen] = useState(false); @@ -284,7 +199,7 @@ const MenuBar: React.FC = function ({ pythonPackage, filter, setFi spellCheck={false} minWidth="400px" /> - + ); From 0b2df18b6a3beec5822eaba8528f606ae951c8cc Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Fri, 3 Jun 2022 15:23:39 +0200 Subject: [PATCH 03/12] feat(gui): pass usage data around --- api-editor/gui/src/app/App.tsx | 52 ++++++++++--------- .../packageData/model/filters/UsageFilter.ts | 7 ++- .../selectionView/SelectionView.tsx | 18 ++++--- .../packageData/treeView/ClassNode.tsx | 11 ++-- .../packageData/treeView/FunctionNode.tsx | 11 ++-- .../packageData/treeView/ModuleNode.tsx | 6 ++- .../packageData/treeView/ParameterNode.tsx | 6 ++- .../packageData/treeView/TreeNode.tsx | 6 ++- .../packageData/treeView/TreeView.tsx | 15 +++--- 9 files changed, 80 insertions(+), 52 deletions(-) diff --git a/api-editor/gui/src/app/App.tsx b/api-editor/gui/src/app/App.tsx index 7e93d5a7d..72376d1e8 100644 --- a/api-editor/gui/src/app/App.tsx +++ b/api-editor/gui/src/app/App.tsx @@ -11,10 +11,10 @@ import { UnorderedList, } from '@chakra-ui/react'; import * as idb from 'idb-keyval'; -import React, { useEffect, useState } from 'react'; -import { useLocation } from 'react-router'; +import React, {useEffect, useState} from 'react'; +import {useLocation} from 'react-router'; import MenuBar from '../common/MenuBar'; -import { Setter } from '../common/util/types'; +import {Setter} from '../common/util/types'; import AnnotationImportDialog from '../features/annotations/AnnotationImportDialog'; import { AnnotationsState, @@ -33,7 +33,7 @@ import MoveForm from '../features/annotations/forms/MoveForm'; import OptionalForm from '../features/annotations/forms/OptionalForm'; import RenameForm from '../features/annotations/forms/RenameForm'; import PythonPackage from '../features/packageData/model/PythonPackage'; -import { parsePythonPackageJson, PythonPackageJson } from '../features/packageData/model/PythonPackageBuilder'; +import {parsePythonPackageJson, PythonPackageJson} from '../features/packageData/model/PythonPackageBuilder'; import PackageDataImportDialog from '../features/packageData/PackageDataImportDialog'; import { selectShowPackageDataImportDialog, @@ -41,13 +41,13 @@ import { } from '../features/packageData/packageDataSlice'; import SelectionView from '../features/packageData/selectionView/SelectionView'; import TreeView from '../features/packageData/treeView/TreeView'; -import { useAppDispatch, useAppSelector } from './hooks'; +import {useAppDispatch, useAppSelector} from './hooks'; import PythonFunction from '../features/packageData/model/PythonFunction'; import AttributeForm from '../features/annotations/forms/AttributeForm'; -import { UsageCountJson, UsageCountStore } from '../features/usages/model/UsageCountStore'; -import { selectShowUsageImportDialog } from '../features/usages/usageSlice'; +import {UsageCountJson, UsageCountStore} from '../features/usages/model/UsageCountStore'; +import {selectShowUsageImportDialog} from '../features/usages/usageSlice'; import UsageImportDialog from '../features/usages/UsageImportDialog'; -import { createFilterFromString } from '../features/packageData/model/filters/filterFactory'; +import {createFilterFromString} from '../features/packageData/model/filters/filterFactory'; const App: React.FC = function () { const dispatch = useAppDispatch(); @@ -94,7 +94,11 @@ const App: React.FC = function () { const [filter, setFilter] = useState('is:public'); const pythonFilter = createFilterFromString(filter); - const filteredPythonPackage = pythonFilter.applyToPackage(pythonPackage, useAppSelector(selectAnnotations)); + const filteredPythonPackage = pythonFilter.applyToPackage( + pythonPackage, + useAppSelector(selectAnnotations), + usages + ); const userActionTarget = pythonPackage.getByRelativePathAsString(currentUserAction.target); @@ -137,18 +141,18 @@ const App: React.FC = function () { resize="horizontal" > {currentUserAction.type === 'attribute' && ( - + )} {currentUserAction.type === 'boundary' && ( - + )} {currentUserAction.type === 'calledAfter' && userActionTarget instanceof PythonFunction && ( - + )} {currentUserAction.type === 'constant' && ( - + )} - {currentUserAction.type === 'enum' && } + {currentUserAction.type === 'enum' && } {currentUserAction.type === 'group' && ( )} - {currentUserAction.type === 'move' && } + {currentUserAction.type === 'move' && } {currentUserAction.type === 'none' && ( - + )} {currentUserAction.type === 'optional' && ( - + )} - {currentUserAction.type === 'rename' && } + {currentUserAction.type === 'rename' && } - + - {showAnnotationImportDialog && } + {showAnnotationImportDialog && } {showPackageDataImportDialog && ( - + )} - {showUsagesImportDialog && } + {showUsagesImportDialog && } - + Infer errors - + {inferErrors.map((error, index) => ( diff --git a/api-editor/gui/src/features/packageData/model/filters/UsageFilter.ts b/api-editor/gui/src/features/packageData/model/filters/UsageFilter.ts index 6f2770594..20c06cbbe 100644 --- a/api-editor/gui/src/features/packageData/model/filters/UsageFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/UsageFilter.ts @@ -27,7 +27,12 @@ export default class UsageFilter extends AbstractPythonFilter { } shouldKeepClass(pythonClass: PythonClass, annotations: AnnotationsState, usages: UsageCountStore): boolean { - return false; // TODO + const classUsages = usages.classUsages.get(pythonClass.qualifiedName) + if (classUsages === undefined) { + return false; + } + + return this.comparison(classUsages, this.expectedUsage); } shouldKeepFunction(pythonFunction: PythonFunction, annotations: AnnotationsState, usages: UsageCountStore): boolean { diff --git a/api-editor/gui/src/features/packageData/selectionView/SelectionView.tsx b/api-editor/gui/src/features/packageData/selectionView/SelectionView.tsx index f6251ae3c..069817fc1 100644 --- a/api-editor/gui/src/features/packageData/selectionView/SelectionView.tsx +++ b/api-editor/gui/src/features/packageData/selectionView/SelectionView.tsx @@ -15,13 +15,15 @@ import { useAppDispatch, useAppSelector } from '../../../app/hooks'; import PythonDeclaration from '../model/PythonDeclaration'; import AbstractPythonFilter from '../model/filters/AbstractPythonFilter'; import { AnnotationsState, selectAnnotations } from '../../annotations/annotationSlice'; +import {UsageCountStore} from "../../usages/model/UsageCountStore"; interface SelectionViewProps { pythonPackage: PythonPackage; pythonFilter: AbstractPythonFilter; + usages: UsageCountStore; } -const SelectionView: React.FC = function ({ pythonPackage, pythonFilter }) { +const SelectionView: React.FC = function ({ pythonPackage, pythonFilter, usages }) { const dispatch = useAppDispatch(); const navigate = useNavigate(); @@ -48,7 +50,7 @@ const SelectionView: React.FC = function ({ pythonPackage, p