From 2cfeb0161c1244e764eb81d8d63c69ecd852d0f5 Mon Sep 17 00:00:00 2001 From: Arsam Islami Date: Fri, 20 May 2022 11:36:44 +0200 Subject: [PATCH 01/45] feat: Removed all previous filters and added new filter "hasName" --- api-editor/gui/src/app/App.tsx | 4 +- api-editor/gui/src/common/MenuBar.tsx | 27 +++--- .../packageData/model/AbstractPythonFilter.ts | 30 +++++++ .../packageData/model/CompoundFilter.ts | 63 ++++++++++++++ .../features/packageData/model/NullFilter.ts | 46 ++++++++++ .../features/packageData/model/PythonClass.ts | 11 +-- .../packageData/model/PythonFilter.ts | 87 ------------------- .../packageData/model/PythonFunction.ts | 12 +-- .../packageData/model/PythonHasName.ts | 41 +++++++++ .../packageData/model/PythonModule.ts | 21 ++--- .../packageData/model/PythonPackage.ts | 12 +-- .../packageData/model/filterFactory.ts | 22 +++++ 12 files changed, 236 insertions(+), 140 deletions(-) create mode 100644 api-editor/gui/src/features/packageData/model/AbstractPythonFilter.ts create mode 100644 api-editor/gui/src/features/packageData/model/CompoundFilter.ts create mode 100644 api-editor/gui/src/features/packageData/model/NullFilter.ts delete mode 100644 api-editor/gui/src/features/packageData/model/PythonFilter.ts create mode 100644 api-editor/gui/src/features/packageData/model/PythonHasName.ts create mode 100644 api-editor/gui/src/features/packageData/model/filterFactory.ts diff --git a/api-editor/gui/src/app/App.tsx b/api-editor/gui/src/app/App.tsx index 97c4ad7a1..0b8b858d9 100644 --- a/api-editor/gui/src/app/App.tsx +++ b/api-editor/gui/src/app/App.tsx @@ -32,7 +32,6 @@ import GroupForm from '../features/annotations/forms/GroupForm'; import MoveForm from '../features/annotations/forms/MoveForm'; import OptionalForm from '../features/annotations/forms/OptionalForm'; import RenameForm from '../features/annotations/forms/RenameForm'; -import { PythonFilter } from '../features/packageData/model/PythonFilter'; import PythonPackage from '../features/packageData/model/PythonPackage'; import { parsePythonPackageJson, PythonPackageJson } from '../features/packageData/model/PythonPackageBuilder'; import PackageDataImportDialog from '../features/packageData/PackageDataImportDialog'; @@ -48,6 +47,7 @@ import AttributeForm from '../features/annotations/forms/AttributeForm'; 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/filterFactory"; const App: React.FC = function () { const dispatch = useAppDispatch(); @@ -93,7 +93,7 @@ const App: React.FC = function () { }, []); const [filter, setFilter] = useState(''); - const pythonFilter = PythonFilter.fromFilterBoxInput(filter); + const pythonFilter = createFilterFromString(filter); const filteredPythonPackage = pythonPackage.filter(pythonFilter); const userActionTarget = pythonPackage.getByRelativePathAsString(currentUserAction.target); diff --git a/api-editor/gui/src/common/MenuBar.tsx b/api-editor/gui/src/common/MenuBar.tsx index 04b76b608..50ad03f5c 100644 --- a/api-editor/gui/src/common/MenuBar.tsx +++ b/api-editor/gui/src/common/MenuBar.tsx @@ -41,7 +41,6 @@ import { NavLink } from 'react-router-dom'; import { useAppDispatch, useAppSelector } from '../app/hooks'; import { resetAnnotations, toggleAnnotationImportDialog } from '../features/annotations/annotationSlice'; import AnnotatedPythonPackageBuilder from '../features/annotatedPackageData/model/AnnotatedPythonPackageBuilder'; -import { PythonFilter } from '../features/packageData/model/PythonFilter'; import PythonPackage from '../features/packageData/model/PythonPackage'; import { selectShowPrivateDeclarations, @@ -204,7 +203,9 @@ const MenuBar: React.FC = function ({ pythonPackage, filter, setFi - + = function ({ pythonPackage, filter, setFi placeholder="Filter..." value={filter} onChange={(event) => setFilter(event.target.value)} - isInvalid={!PythonFilter.fromFilterBoxInput(filter)} - borderColor={ - PythonFilter.fromFilterBoxInput(filter)?.isFilteringModules() - ? 'green' - : 'inherit' - } + // isInvalid={!PythonFilter.fromFilterBoxInput(filter)} + // borderColor={ + // PythonFilter.fromFilterBoxInput(filter)?.isFilteringModules() + // ? 'green' + // : 'inherit' + // } spellCheck={false} /> - {PythonFilter.fromFilterBoxInput(filter)?.isFilteringModules() && ( - - - - )} + {/*{PythonFilter.fromFilterBoxInput(filter)?.isFilteringModules() && (*/} + {/* */} + {/* */} + {/* */} + {/*)}*/} diff --git a/api-editor/gui/src/features/packageData/model/AbstractPythonFilter.ts b/api-editor/gui/src/features/packageData/model/AbstractPythonFilter.ts new file mode 100644 index 000000000..05bdfd93d --- /dev/null +++ b/api-editor/gui/src/features/packageData/model/AbstractPythonFilter.ts @@ -0,0 +1,30 @@ +import PythonClass from './PythonClass'; +import PythonFunction from './PythonFunction'; +import PythonModule from "./PythonModule"; +import PythonParameter from "./PythonParameter"; + +export default abstract class AbstractPythonFilter { + abstract filterModules(pythonModule: PythonModule): boolean + + abstract filterClasses(pythonClass: PythonClass): boolean + + abstract filterFunctions(pythonFunction: PythonFunction): boolean + + abstract filterParameters(pythonParameter: PythonParameter): boolean + + isFilteringModules(): boolean { + return true; + } + + isFilteringClasses(): boolean { + return true; + } + + isFilteringFunctions(): boolean { + return true; + } + + isFilteringParameters(): boolean { + return true; + } +} diff --git a/api-editor/gui/src/features/packageData/model/CompoundFilter.ts b/api-editor/gui/src/features/packageData/model/CompoundFilter.ts new file mode 100644 index 000000000..d353fad2d --- /dev/null +++ b/api-editor/gui/src/features/packageData/model/CompoundFilter.ts @@ -0,0 +1,63 @@ +import AbstractPythonFilter from "./AbstractPythonFilter"; +import PythonClass from "./PythonClass"; +import PythonFunction from "./PythonFunction"; +import PythonParameter from "./PythonParameter"; +import PythonModule from "./PythonModule"; + +export type FilterString = string; + +export class CompoundFilter extends AbstractPythonFilter { + constructor( + readonly filters: AbstractPythonFilter[] + ) { + super(); + } + + filterClasses(pythonClass: PythonClass): boolean { + return this.filters.every( + (it) => it.filterClasses(pythonClass) + ); + } + + filterFunctions(pythonFunction: PythonFunction): boolean { + return this.filters.every( + (it) => it.filterFunctions(pythonFunction) + ); + } + + filterModules(pythonModule: PythonModule): boolean { + return this.filters.every( + (it) => it.filterModules(pythonModule) + ); + } + + filterParameters(pythonParameter: PythonParameter): boolean { + return this.filters.every( + (it) => it.filterParameters(pythonParameter) + ); + } + + isFilteringModules(): boolean { + return this.filters.some( + (it) => it.isFilteringModules() + ); + } + + isFilteringClasses(): boolean { + return this.filters.some( + (it) => it.isFilteringClasses() + ); + } + + isFilteringFunctions(): boolean { + return this.filters.some( + (it) => it.isFilteringFunctions() + ); + } + + isFilteringParameters(): boolean { + return this.filters.some( + (it) => it.isFilteringParameters() + ); + } +} diff --git a/api-editor/gui/src/features/packageData/model/NullFilter.ts b/api-editor/gui/src/features/packageData/model/NullFilter.ts new file mode 100644 index 000000000..3b823fc5b --- /dev/null +++ b/api-editor/gui/src/features/packageData/model/NullFilter.ts @@ -0,0 +1,46 @@ +import { isEmptyList } from '../../../common/util/listOperations'; +import { Optional } from '../../../common/util/types'; +import PythonClass from './PythonClass'; +import PythonDeclaration from './PythonDeclaration'; +import { PythonFilter } from './PythonFilter'; +import PythonFromImport from './PythonFromImport'; +import PythonFunction from './PythonFunction'; +import PythonImport from './PythonImport'; +import PythonPackage from './PythonPackage'; +import PythonModule from "./PythonModule"; +import PythonParameter from "./PythonParameter"; +import AbstractPythonFilter from "./AbstractPythonFilter"; + +export default class NullFilter extends AbstractPythonFilter { + filterModules(pythonModule: PythonModule): boolean { + return true; + } + + filterClasses(pythonClass: PythonClass): boolean { + return true; + } + + filterFunctions(pythonFunction: PythonFunction): boolean { + return true; + } + + filterParameters(pythonParameter: PythonParameter): boolean { + return true; + } + + isFilteringModules(): boolean { + return false; + } + + isFilteringClasses(): boolean { + return false; + } + + isFilteringFunctions(): boolean { + return false; + } + + isFilteringParameters(): boolean { + return false; + } +} diff --git a/api-editor/gui/src/features/packageData/model/PythonClass.ts b/api-editor/gui/src/features/packageData/model/PythonClass.ts index 13eb2d312..87013b81d 100644 --- a/api-editor/gui/src/features/packageData/model/PythonClass.ts +++ b/api-editor/gui/src/features/packageData/model/PythonClass.ts @@ -4,6 +4,7 @@ import PythonDeclaration from './PythonDeclaration'; import { PythonFilter } from './PythonFilter'; import PythonFunction from './PythonFunction'; import PythonModule from './PythonModule'; +import AbstractPythonFilter from "./AbstractPythonFilter"; export default class PythonClass extends PythonDeclaration { containingModule: Optional; @@ -56,8 +57,8 @@ export default class PythonClass extends PythonDeclaration { return result; } - filter(pythonFilter: PythonFilter | void): PythonClass { - if (!pythonFilter || !pythonFilter.isFilteringFunctions()) { + filter(pythonFilter: AbstractPythonFilter): PythonClass { + if (!pythonFilter.isFilteringFunctions()) { return this; } @@ -65,11 +66,7 @@ export default class PythonClass extends PythonDeclaration { .map((it) => it.filter(pythonFilter)) .filter( (it) => - it.name - .toLowerCase() - .includes( - (pythonFilter.pythonFunction || '').toLowerCase(), - ) && + pythonFilter.filterFunctions(it) || // Don't exclude functions without parameters when we don't filter parameters (!pythonFilter.isFilteringParameters() || !isEmptyList(it.parameters)), diff --git a/api-editor/gui/src/features/packageData/model/PythonFilter.ts b/api-editor/gui/src/features/packageData/model/PythonFilter.ts deleted file mode 100644 index 3b2661b16..000000000 --- a/api-editor/gui/src/features/packageData/model/PythonFilter.ts +++ /dev/null @@ -1,87 +0,0 @@ -export type FilterString = string; - -export class PythonFilter { - constructor( - readonly pythonModule: FilterString | void, - readonly pythonClass: FilterString | void, - readonly pythonFunction: FilterString | void, - readonly pythonParameter: FilterString | void, - ) {} - - static fromFilterBoxInput(filterBoxInput: string): PythonFilter | void { - let pythonModule; - let pythonClass; - let pythonFunction; - let pythonParameter; - - for (const match of filterBoxInput.matchAll(/(\w+):([^\s:]+)/gu)) { - if (match.length === 3) { - const [, scope, filterString] = match; - - switch (scope) { - case 'module': - if (pythonModule) { - return undefined; - } else { - pythonModule = filterString; - } - break; - case 'class': - if (pythonClass) { - return undefined; - } else { - pythonClass = filterString; - } - break; - case 'function': - if (pythonFunction) { - return undefined; - } else { - pythonFunction = filterString; - } - break; - case 'parameter': - if (pythonParameter) { - return undefined; - } else { - pythonParameter = filterString; - } - break; - // no default - } - } - } - - return new PythonFilter( - pythonModule, - pythonClass, - pythonFunction, - pythonParameter, - ); - } - - isFilteringModules(): boolean { - return ( - Boolean(this.pythonModule) || - Boolean(this.pythonClass) || - Boolean(this.pythonFunction) || - Boolean(this.pythonParameter) - ); - } - - isFilteringClasses(): boolean { - return ( - Boolean(this.pythonClass) || - Boolean(this.pythonFunction) || - Boolean(this.pythonParameter) - ); - } - - isFilteringFunctions(): boolean { - return Boolean(this.pythonFunction) || Boolean(this.pythonParameter); - } - - isFilteringParameters(): boolean { - return Boolean(this.pythonParameter); - } -} diff --git a/api-editor/gui/src/features/packageData/model/PythonFunction.ts b/api-editor/gui/src/features/packageData/model/PythonFunction.ts index 739370bdf..5575a0fb1 100644 --- a/api-editor/gui/src/features/packageData/model/PythonFunction.ts +++ b/api-editor/gui/src/features/packageData/model/PythonFunction.ts @@ -5,6 +5,7 @@ import { PythonFilter } from './PythonFilter'; import PythonModule from './PythonModule'; import PythonParameter from './PythonParameter'; import PythonResult from './PythonResult'; +import AbstractPythonFilter from "./AbstractPythonFilter"; export default class PythonFunction extends PythonDeclaration { containingModuleOrClass: Optional; @@ -96,19 +97,14 @@ export default class PythonFunction extends PythonDeclaration { return result; } - filter(pythonFilter: PythonFilter | void): PythonFunction { - if (!pythonFilter || !pythonFilter.isFilteringParameters()) { + filter(pythonFilter: AbstractPythonFilter): PythonFunction { + if (!pythonFilter.isFilteringParameters()) { return this; } const parameters = this.parameters .map((it) => it.clone()) - .filter((it) => - it.name - .toLowerCase() - .includes( - (pythonFilter.pythonParameter || '').toLowerCase(), - ), + .filter((it) => pythonFilter.filterParameters(it) ); const results = this.results.map((it) => it.clone()); diff --git a/api-editor/gui/src/features/packageData/model/PythonHasName.ts b/api-editor/gui/src/features/packageData/model/PythonHasName.ts new file mode 100644 index 000000000..4b70de390 --- /dev/null +++ b/api-editor/gui/src/features/packageData/model/PythonHasName.ts @@ -0,0 +1,41 @@ +import { Optional } from '../../../common/util/types'; +import PythonClass from './PythonClass'; +import PythonFunction from './PythonFunction'; +import PythonPackage from './PythonPackage'; +import PythonModule from "./PythonModule"; +import PythonParameter from "./PythonParameter"; +import AbstractPythonFilter from "./AbstractPythonFilter"; + +export default class PythonHasNameFilter extends AbstractPythonFilter { + containingPackage: Optional; + + constructor( + readonly name: string, + ) { + super(); + } + + filterModules(pythonModule: PythonModule): boolean { + return this.filter(pythonModule.name) + } + + filterClasses(pythonClass: PythonClass): boolean { + return this.filter(pythonClass.name) + } + + filterFunctions(pythonFunction: PythonFunction): boolean { + return this.filter(pythonFunction.name) + } + + filterParameters(pythonParameter: PythonParameter): boolean { + return this.filter(pythonParameter.name) + } + + private filter(declerationName: string): boolean { + return declerationName + .toLowerCase() + .includes( + this.name.toLowerCase() + ) + } +} diff --git a/api-editor/gui/src/features/packageData/model/PythonModule.ts b/api-editor/gui/src/features/packageData/model/PythonModule.ts index 2e8377c52..a1c58fc9b 100644 --- a/api-editor/gui/src/features/packageData/model/PythonModule.ts +++ b/api-editor/gui/src/features/packageData/model/PythonModule.ts @@ -2,11 +2,11 @@ import { isEmptyList } from '../../../common/util/listOperations'; import { Optional } from '../../../common/util/types'; import PythonClass from './PythonClass'; import PythonDeclaration from './PythonDeclaration'; -import { PythonFilter } from './PythonFilter'; import PythonFromImport from './PythonFromImport'; import PythonFunction from './PythonFunction'; import PythonImport from './PythonImport'; import PythonPackage from './PythonPackage'; +import AbstractPythonFilter from "./AbstractPythonFilter"; export default class PythonModule extends PythonDeclaration { containingPackage: Optional; @@ -43,9 +43,9 @@ export default class PythonModule extends PythonDeclaration { return `Module "${this.name}"`; } - filter(pythonFilter: PythonFilter | void): PythonModule { + filter(pythonFilter: AbstractPythonFilter): PythonModule { // isFilteringClasses is also true if we are filtering functions - if (!pythonFilter || !pythonFilter.isFilteringClasses()) { + if (!pythonFilter.isFilteringClasses()) { return this; } @@ -53,26 +53,17 @@ export default class PythonModule extends PythonDeclaration { .map((it) => it.filter(pythonFilter)) .filter( (it) => - it.name - .toLowerCase() - .includes( - (pythonFilter.pythonClass || '').toLowerCase(), - ) && + pythonFilter.filterClasses(it) && // Don't exclude empty classes when we only filter modules or classes (!pythonFilter.isFilteringFunctions() || !isEmptyList(it.methods)), ); + const functions = this.functions .map((it) => it.filter(pythonFilter)) .filter( - (it) => - !pythonFilter.pythonClass && // if the class filter is active we hide all top-level functions - it.name - .toLowerCase() - .includes( - (pythonFilter.pythonFunction || '').toLowerCase(), - ) && + (it) => pythonFilter.filterFunctions(it) || // Don't exclude functions without parameters when we don't filter parameters (!pythonFilter.isFilteringParameters() || !isEmptyList(it.parameters)), diff --git a/api-editor/gui/src/features/packageData/model/PythonPackage.ts b/api-editor/gui/src/features/packageData/model/PythonPackage.ts index e31689928..0905711d4 100644 --- a/api-editor/gui/src/features/packageData/model/PythonPackage.ts +++ b/api-editor/gui/src/features/packageData/model/PythonPackage.ts @@ -1,7 +1,7 @@ import { isEmptyList } from '../../../common/util/listOperations'; import PythonDeclaration from './PythonDeclaration'; -import { PythonFilter } from './PythonFilter'; import PythonModule from './PythonModule'; +import AbstractPythonFilter from "./AbstractPythonFilter"; export default class PythonPackage extends PythonDeclaration { constructor( @@ -29,8 +29,8 @@ export default class PythonPackage extends PythonDeclaration { return `Package "${this.distribution}/${this.name} v${this.version}"`; } - filter(pythonFilter: PythonFilter | void): PythonPackage { - if (!pythonFilter || !pythonFilter.isFilteringModules()) { + filter(pythonFilter: AbstractPythonFilter): PythonPackage { + if (!pythonFilter.isFilteringModules()) { return this; } @@ -38,11 +38,7 @@ export default class PythonPackage extends PythonDeclaration { .map((it) => it.filter(pythonFilter)) .filter( (it) => - it.name - .toLowerCase() - .includes( - (pythonFilter.pythonModule || '').toLowerCase(), - ) && + pythonFilter.filterModules(it) || // Don't exclude empty modules when we only filter modules (!pythonFilter.isFilteringClasses() || !isEmptyList(it.classes) || diff --git a/api-editor/gui/src/features/packageData/model/filterFactory.ts b/api-editor/gui/src/features/packageData/model/filterFactory.ts new file mode 100644 index 000000000..65c876de9 --- /dev/null +++ b/api-editor/gui/src/features/packageData/model/filterFactory.ts @@ -0,0 +1,22 @@ +import {CompoundFilter} from "./CompoundFilter"; +import PythonHasNameFilter from "./PythonHasName"; +import AbstractPythonFilter from "./AbstractPythonFilter"; + +export function createFilterFromString(filterBoxInput: string): AbstractPythonFilter { + const filters = [] + + for (const match of filterBoxInput.matchAll(/(\w+):([^\s:]+)/gu)) { + if (match.length === 3) { + const [, scope, filterString] = match; + + switch (scope) { + case 'hasName': + filters.push(new PythonHasNameFilter(filterString)) + break; + // no default + } + } + } + + return new CompoundFilter(filters); +} From 75f4335553fbdbee6a6a8d4ce1c33941852b6813 Mon Sep 17 00:00:00 2001 From: Arsam Islami Date: Fri, 20 May 2022 11:41:06 +0200 Subject: [PATCH 02/45] refactor: New names for abstract AbstractPythonFilter.ts functions --- api-editor/gui/src/common/MenuBar.tsx | 17 ++++++++--------- .../packageData/model/AbstractPythonFilter.ts | 8 ++++---- .../packageData/model/CompoundFilter.ts | 16 ++++++++-------- .../features/packageData/model/NullFilter.ts | 15 ++++----------- .../features/packageData/model/PythonClass.ts | 2 +- .../packageData/model/PythonFunction.ts | 2 +- .../features/packageData/model/PythonHasName.ts | 8 ++++---- .../features/packageData/model/PythonModule.ts | 4 ++-- .../features/packageData/model/PythonPackage.ts | 2 +- 9 files changed, 33 insertions(+), 41 deletions(-) diff --git a/api-editor/gui/src/common/MenuBar.tsx b/api-editor/gui/src/common/MenuBar.tsx index 50ad03f5c..50c6066db 100644 --- a/api-editor/gui/src/common/MenuBar.tsx +++ b/api-editor/gui/src/common/MenuBar.tsx @@ -18,7 +18,6 @@ import { Image, Input, InputGroup, - InputRightElement, Link, Menu, MenuButton, @@ -34,12 +33,12 @@ import { useColorMode, VStack, } from '@chakra-ui/react'; -import React, { useRef, useState } from 'react'; -import { FaCheck, FaChevronDown } from 'react-icons/fa'; -import { useLocation } from 'react-router'; -import { NavLink } from 'react-router-dom'; -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 {useLocation} from 'react-router'; +import {NavLink} from 'react-router-dom'; +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 { @@ -47,8 +46,8 @@ import { togglePackageDataImportDialog, toggleShowPrivateDeclarations, } from '../features/packageData/packageDataSlice'; -import { Setter } from './util/types'; -import { toggleUsageImportDialog } from '../features/usages/usageSlice'; +import {Setter} from './util/types'; +import {toggleUsageImportDialog} from '../features/usages/usageSlice'; interface MenuBarProps { pythonPackage: PythonPackage; diff --git a/api-editor/gui/src/features/packageData/model/AbstractPythonFilter.ts b/api-editor/gui/src/features/packageData/model/AbstractPythonFilter.ts index 05bdfd93d..21cca531b 100644 --- a/api-editor/gui/src/features/packageData/model/AbstractPythonFilter.ts +++ b/api-editor/gui/src/features/packageData/model/AbstractPythonFilter.ts @@ -4,13 +4,13 @@ import PythonModule from "./PythonModule"; import PythonParameter from "./PythonParameter"; export default abstract class AbstractPythonFilter { - abstract filterModules(pythonModule: PythonModule): boolean + abstract shouldKeepModule(pythonModule: PythonModule): boolean - abstract filterClasses(pythonClass: PythonClass): boolean + abstract shouldKeepClass(pythonClass: PythonClass): boolean - abstract filterFunctions(pythonFunction: PythonFunction): boolean + abstract shouldKeepFunction(pythonFunction: PythonFunction): boolean - abstract filterParameters(pythonParameter: PythonParameter): boolean + abstract shouldKeepParameter(pythonParameter: PythonParameter): boolean isFilteringModules(): boolean { return true; diff --git a/api-editor/gui/src/features/packageData/model/CompoundFilter.ts b/api-editor/gui/src/features/packageData/model/CompoundFilter.ts index d353fad2d..fcfbcb927 100644 --- a/api-editor/gui/src/features/packageData/model/CompoundFilter.ts +++ b/api-editor/gui/src/features/packageData/model/CompoundFilter.ts @@ -13,27 +13,27 @@ export class CompoundFilter extends AbstractPythonFilter { super(); } - filterClasses(pythonClass: PythonClass): boolean { + shouldKeepClass(pythonClass: PythonClass): boolean { return this.filters.every( - (it) => it.filterClasses(pythonClass) + (it) => it.shouldKeepClass(pythonClass) ); } - filterFunctions(pythonFunction: PythonFunction): boolean { + shouldKeepFunction(pythonFunction: PythonFunction): boolean { return this.filters.every( - (it) => it.filterFunctions(pythonFunction) + (it) => it.shouldKeepFunction(pythonFunction) ); } - filterModules(pythonModule: PythonModule): boolean { + shouldKeepModule(pythonModule: PythonModule): boolean { return this.filters.every( - (it) => it.filterModules(pythonModule) + (it) => it.shouldKeepModule(pythonModule) ); } - filterParameters(pythonParameter: PythonParameter): boolean { + shouldKeepParameter(pythonParameter: PythonParameter): boolean { return this.filters.every( - (it) => it.filterParameters(pythonParameter) + (it) => it.shouldKeepParameter(pythonParameter) ); } diff --git a/api-editor/gui/src/features/packageData/model/NullFilter.ts b/api-editor/gui/src/features/packageData/model/NullFilter.ts index 3b823fc5b..9f52bda1b 100644 --- a/api-editor/gui/src/features/packageData/model/NullFilter.ts +++ b/api-editor/gui/src/features/packageData/model/NullFilter.ts @@ -1,30 +1,23 @@ -import { isEmptyList } from '../../../common/util/listOperations'; -import { Optional } from '../../../common/util/types'; import PythonClass from './PythonClass'; -import PythonDeclaration from './PythonDeclaration'; -import { PythonFilter } from './PythonFilter'; -import PythonFromImport from './PythonFromImport'; import PythonFunction from './PythonFunction'; -import PythonImport from './PythonImport'; -import PythonPackage from './PythonPackage'; import PythonModule from "./PythonModule"; import PythonParameter from "./PythonParameter"; import AbstractPythonFilter from "./AbstractPythonFilter"; export default class NullFilter extends AbstractPythonFilter { - filterModules(pythonModule: PythonModule): boolean { + shouldKeepModule(pythonModule: PythonModule): boolean { return true; } - filterClasses(pythonClass: PythonClass): boolean { + shouldKeepClass(pythonClass: PythonClass): boolean { return true; } - filterFunctions(pythonFunction: PythonFunction): boolean { + shouldKeepFunction(pythonFunction: PythonFunction): boolean { return true; } - filterParameters(pythonParameter: PythonParameter): boolean { + shouldKeepParameter(pythonParameter: PythonParameter): boolean { return true; } diff --git a/api-editor/gui/src/features/packageData/model/PythonClass.ts b/api-editor/gui/src/features/packageData/model/PythonClass.ts index 87013b81d..59922a397 100644 --- a/api-editor/gui/src/features/packageData/model/PythonClass.ts +++ b/api-editor/gui/src/features/packageData/model/PythonClass.ts @@ -66,7 +66,7 @@ export default class PythonClass extends PythonDeclaration { .map((it) => it.filter(pythonFilter)) .filter( (it) => - pythonFilter.filterFunctions(it) || + pythonFilter.shouldKeepFunction(it) || // Don't exclude functions without parameters when we don't filter parameters (!pythonFilter.isFilteringParameters() || !isEmptyList(it.parameters)), diff --git a/api-editor/gui/src/features/packageData/model/PythonFunction.ts b/api-editor/gui/src/features/packageData/model/PythonFunction.ts index 5575a0fb1..be793a185 100644 --- a/api-editor/gui/src/features/packageData/model/PythonFunction.ts +++ b/api-editor/gui/src/features/packageData/model/PythonFunction.ts @@ -104,7 +104,7 @@ export default class PythonFunction extends PythonDeclaration { const parameters = this.parameters .map((it) => it.clone()) - .filter((it) => pythonFilter.filterParameters(it) + .filter((it) => pythonFilter.shouldKeepParameter(it) ); const results = this.results.map((it) => it.clone()); diff --git a/api-editor/gui/src/features/packageData/model/PythonHasName.ts b/api-editor/gui/src/features/packageData/model/PythonHasName.ts index 4b70de390..30f4fff31 100644 --- a/api-editor/gui/src/features/packageData/model/PythonHasName.ts +++ b/api-editor/gui/src/features/packageData/model/PythonHasName.ts @@ -15,19 +15,19 @@ export default class PythonHasNameFilter extends AbstractPythonFilter { super(); } - filterModules(pythonModule: PythonModule): boolean { + shouldKeepModule(pythonModule: PythonModule): boolean { return this.filter(pythonModule.name) } - filterClasses(pythonClass: PythonClass): boolean { + shouldKeepClass(pythonClass: PythonClass): boolean { return this.filter(pythonClass.name) } - filterFunctions(pythonFunction: PythonFunction): boolean { + shouldKeepFunction(pythonFunction: PythonFunction): boolean { return this.filter(pythonFunction.name) } - filterParameters(pythonParameter: PythonParameter): boolean { + shouldKeepParameter(pythonParameter: PythonParameter): boolean { return this.filter(pythonParameter.name) } diff --git a/api-editor/gui/src/features/packageData/model/PythonModule.ts b/api-editor/gui/src/features/packageData/model/PythonModule.ts index a1c58fc9b..eac144c42 100644 --- a/api-editor/gui/src/features/packageData/model/PythonModule.ts +++ b/api-editor/gui/src/features/packageData/model/PythonModule.ts @@ -53,7 +53,7 @@ export default class PythonModule extends PythonDeclaration { .map((it) => it.filter(pythonFilter)) .filter( (it) => - pythonFilter.filterClasses(it) && + pythonFilter.shouldKeepClass(it) && // Don't exclude empty classes when we only filter modules or classes (!pythonFilter.isFilteringFunctions() || !isEmptyList(it.methods)), @@ -63,7 +63,7 @@ export default class PythonModule extends PythonDeclaration { const functions = this.functions .map((it) => it.filter(pythonFilter)) .filter( - (it) => pythonFilter.filterFunctions(it) || + (it) => pythonFilter.shouldKeepFunction(it) || // Don't exclude functions without parameters when we don't filter parameters (!pythonFilter.isFilteringParameters() || !isEmptyList(it.parameters)), diff --git a/api-editor/gui/src/features/packageData/model/PythonPackage.ts b/api-editor/gui/src/features/packageData/model/PythonPackage.ts index 0905711d4..8394b0f95 100644 --- a/api-editor/gui/src/features/packageData/model/PythonPackage.ts +++ b/api-editor/gui/src/features/packageData/model/PythonPackage.ts @@ -38,7 +38,7 @@ export default class PythonPackage extends PythonDeclaration { .map((it) => it.filter(pythonFilter)) .filter( (it) => - pythonFilter.filterModules(it) || + pythonFilter.shouldKeepModule(it) || // Don't exclude empty modules when we only filter modules (!pythonFilter.isFilteringClasses() || !isEmptyList(it.classes) || From 124f9a08ddf3e8cf09ff01938b3207a2915e7465 Mon Sep 17 00:00:00 2001 From: Arsam Islami Date: Fri, 20 May 2022 13:54:03 +0200 Subject: [PATCH 03/45] feat: Added is-filter, not working yet tho --- .../features/packageData/model/PythonClass.ts | 1 - .../model/PythonDeclerationType.ts | 49 +++++++++++++++++++ .../packageData/model/PythonFunction.ts | 1 - .../packageData/model/PythonHasName.ts | 4 -- .../packageData/model/filterFactory.ts | 19 +++++++ 5 files changed, 68 insertions(+), 6 deletions(-) create mode 100644 api-editor/gui/src/features/packageData/model/PythonDeclerationType.ts diff --git a/api-editor/gui/src/features/packageData/model/PythonClass.ts b/api-editor/gui/src/features/packageData/model/PythonClass.ts index 59922a397..c4833cc06 100644 --- a/api-editor/gui/src/features/packageData/model/PythonClass.ts +++ b/api-editor/gui/src/features/packageData/model/PythonClass.ts @@ -1,7 +1,6 @@ import { isEmptyList } from '../../../common/util/listOperations'; import { Optional } from '../../../common/util/types'; import PythonDeclaration from './PythonDeclaration'; -import { PythonFilter } from './PythonFilter'; import PythonFunction from './PythonFunction'; import PythonModule from './PythonModule'; import AbstractPythonFilter from "./AbstractPythonFilter"; diff --git a/api-editor/gui/src/features/packageData/model/PythonDeclerationType.ts b/api-editor/gui/src/features/packageData/model/PythonDeclerationType.ts new file mode 100644 index 000000000..f7657d63c --- /dev/null +++ b/api-editor/gui/src/features/packageData/model/PythonDeclerationType.ts @@ -0,0 +1,49 @@ +import PythonClass from './PythonClass'; +import PythonFunction from './PythonFunction'; +import PythonModule from "./PythonModule"; +import PythonParameter from "./PythonParameter"; +import AbstractPythonFilter from "./AbstractPythonFilter"; + +export default class PythonDeclerationTypeFilter extends AbstractPythonFilter { + constructor( + readonly type: DeclerationType, + ) { + super(); + } + + shouldKeepModule(pythonModule: PythonModule): boolean { + return this.type == DeclerationType.module; + } + + shouldKeepClass(pythonClass: PythonClass): boolean { + return this.type == DeclerationType.class; + } + + shouldKeepFunction(pythonFunction: PythonFunction): boolean { + return this.type == DeclerationType.function + } + + shouldKeepParameter(pythonParameter: PythonParameter): boolean { + return this.type == DeclerationType.parameter; + } + + isFilteringModules(): boolean { + return this.type != DeclerationType.module; + } + + isFilteringClasses(): boolean { + return this.type != DeclerationType.class; + } + + isFilteringFunctions(): boolean { + return this.type != DeclerationType.function + } + + isFilteringParameters(): boolean { + return this.type != DeclerationType.parameter; + } +} + +export enum DeclerationType { + module, class, function, parameter +} diff --git a/api-editor/gui/src/features/packageData/model/PythonFunction.ts b/api-editor/gui/src/features/packageData/model/PythonFunction.ts index be793a185..1992435c8 100644 --- a/api-editor/gui/src/features/packageData/model/PythonFunction.ts +++ b/api-editor/gui/src/features/packageData/model/PythonFunction.ts @@ -1,7 +1,6 @@ import { Optional } from '../../../common/util/types'; import PythonClass from './PythonClass'; import PythonDeclaration from './PythonDeclaration'; -import { PythonFilter } from './PythonFilter'; import PythonModule from './PythonModule'; import PythonParameter from './PythonParameter'; import PythonResult from './PythonResult'; diff --git a/api-editor/gui/src/features/packageData/model/PythonHasName.ts b/api-editor/gui/src/features/packageData/model/PythonHasName.ts index 30f4fff31..a395cf35e 100644 --- a/api-editor/gui/src/features/packageData/model/PythonHasName.ts +++ b/api-editor/gui/src/features/packageData/model/PythonHasName.ts @@ -1,14 +1,10 @@ -import { Optional } from '../../../common/util/types'; import PythonClass from './PythonClass'; import PythonFunction from './PythonFunction'; -import PythonPackage from './PythonPackage'; import PythonModule from "./PythonModule"; import PythonParameter from "./PythonParameter"; import AbstractPythonFilter from "./AbstractPythonFilter"; export default class PythonHasNameFilter extends AbstractPythonFilter { - containingPackage: Optional; - constructor( readonly name: string, ) { diff --git a/api-editor/gui/src/features/packageData/model/filterFactory.ts b/api-editor/gui/src/features/packageData/model/filterFactory.ts index 65c876de9..25a82c42e 100644 --- a/api-editor/gui/src/features/packageData/model/filterFactory.ts +++ b/api-editor/gui/src/features/packageData/model/filterFactory.ts @@ -1,6 +1,7 @@ import {CompoundFilter} from "./CompoundFilter"; import PythonHasNameFilter from "./PythonHasName"; import AbstractPythonFilter from "./AbstractPythonFilter"; +import PythonDeclerationTypeFilter, {DeclerationType} from "./PythonDeclerationType"; export function createFilterFromString(filterBoxInput: string): AbstractPythonFilter { const filters = [] @@ -12,6 +13,24 @@ export function createFilterFromString(filterBoxInput: string): AbstractPythonFi switch (scope) { case 'hasName': filters.push(new PythonHasNameFilter(filterString)) + break; + case 'is': + switch (filterString) { + case 'module': + filters.push(new PythonDeclerationTypeFilter(DeclerationType.module)) + break + case 'class': + filters.push(new PythonDeclerationTypeFilter(DeclerationType.class)) + break + case 'function': + filters.push(new PythonDeclerationTypeFilter(DeclerationType.function)) + break + case 'parameter': + filters.push(new PythonDeclerationTypeFilter(DeclerationType.parameter)) + break + // no default + } + break; // no default } From 973a4ee6e935b06e2a12f20f56d6f065042934c1 Mon Sep 17 00:00:00 2001 From: Arsam Islami Date: Fri, 20 May 2022 16:30:54 +0200 Subject: [PATCH 04/45] feat: Adjustments for filtering --- api-editor/gui/src/features/packageData/model/PythonClass.ts | 2 +- .../gui/src/features/packageData/model/PythonFunction.ts | 2 +- api-editor/gui/src/features/packageData/model/PythonModule.ts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api-editor/gui/src/features/packageData/model/PythonClass.ts b/api-editor/gui/src/features/packageData/model/PythonClass.ts index c4833cc06..a6051f32f 100644 --- a/api-editor/gui/src/features/packageData/model/PythonClass.ts +++ b/api-editor/gui/src/features/packageData/model/PythonClass.ts @@ -57,7 +57,7 @@ export default class PythonClass extends PythonDeclaration { } filter(pythonFilter: AbstractPythonFilter): PythonClass { - if (!pythonFilter.isFilteringFunctions()) { + if (!pythonFilter.isFilteringFunctions() || pythonFilter.isFilteringClasses()) { return this; } diff --git a/api-editor/gui/src/features/packageData/model/PythonFunction.ts b/api-editor/gui/src/features/packageData/model/PythonFunction.ts index 1992435c8..d5712a8a9 100644 --- a/api-editor/gui/src/features/packageData/model/PythonFunction.ts +++ b/api-editor/gui/src/features/packageData/model/PythonFunction.ts @@ -97,7 +97,7 @@ export default class PythonFunction extends PythonDeclaration { } filter(pythonFilter: AbstractPythonFilter): PythonFunction { - if (!pythonFilter.isFilteringParameters()) { + if (!pythonFilter.isFilteringParameters() || pythonFilter.isFilteringFunctions()) { return this; } diff --git a/api-editor/gui/src/features/packageData/model/PythonModule.ts b/api-editor/gui/src/features/packageData/model/PythonModule.ts index eac144c42..59f610c3c 100644 --- a/api-editor/gui/src/features/packageData/model/PythonModule.ts +++ b/api-editor/gui/src/features/packageData/model/PythonModule.ts @@ -45,7 +45,7 @@ export default class PythonModule extends PythonDeclaration { filter(pythonFilter: AbstractPythonFilter): PythonModule { // isFilteringClasses is also true if we are filtering functions - if (!pythonFilter.isFilteringClasses()) { + if (pythonFilter.isFilteringModules()) { return this; } @@ -53,7 +53,7 @@ export default class PythonModule extends PythonDeclaration { .map((it) => it.filter(pythonFilter)) .filter( (it) => - pythonFilter.shouldKeepClass(it) && + pythonFilter.shouldKeepClass(it) || // Don't exclude empty classes when we only filter modules or classes (!pythonFilter.isFilteringFunctions() || !isEmptyList(it.methods)), From ef4971587d475e5d6007b4efb686205122155628 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Fri, 20 May 2022 18:27:33 +0200 Subject: [PATCH 05/45] fix: update menu bar again --- api-editor/gui/src/common/MenuBar.tsx | 58 ++++++++++----------------- 1 file changed, 22 insertions(+), 36 deletions(-) diff --git a/api-editor/gui/src/common/MenuBar.tsx b/api-editor/gui/src/common/MenuBar.tsx index ad29424c0..5b0173494 100644 --- a/api-editor/gui/src/common/MenuBar.tsx +++ b/api-editor/gui/src/common/MenuBar.tsx @@ -14,7 +14,6 @@ import { IconButton, Input, InputGroup, - InputRightElement, ListItem, Menu, MenuButton, @@ -37,20 +36,19 @@ import { useColorMode, VStack, } from '@chakra-ui/react'; -import React, { useRef, useState } from 'react'; -import { FaCheck, 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 { PythonFilter } from '../features/packageData/model/PythonFilter'; import PythonPackage from '../features/packageData/model/PythonPackage'; import { selectShowPrivateDeclarations, togglePackageDataImportDialog, toggleShowPrivateDeclarations, } from '../features/packageData/packageDataSlice'; -import { Setter } from './util/types'; -import { toggleUsageImportDialog } from '../features/usages/usageSlice'; +import {Setter} from './util/types'; +import {toggleUsageImportDialog} from '../features/usages/usageSlice'; interface MenuBarProps { pythonPackage: PythonPackage; @@ -60,20 +58,6 @@ interface MenuBarProps { } const HelpButton = function () { - const dispatch = useAppDispatch(); - const [isOpen, setIsOpen] = useState(false); - const cancelRef = useRef(null); - - // Event handlers ---------------------------------------------------------- - - const handleConfirm = () => { - dispatch(resetAnnotations()); - setIsOpen(false); - }; - const handleCancel = () => setIsOpen(false); - - // Render ------------------------------------------------------------------ - return ( @@ -81,10 +65,9 @@ const HelpButton = function () { variant="ghost" icon={} aria-label="help" - onClick={() => setIsOpen(true)} /> - + Filter Options @@ -290,7 +273,10 @@ const MenuBar: React.FC = function ({ pythonPackage, filter, setFi - + = function ({ pythonPackage, filter, setFi placeholder="Filter..." value={filter} onChange={(event) => setFilter(event.target.value)} - isInvalid={!PythonFilter.fromFilterBoxInput(filter)} - borderColor={ - PythonFilter.fromFilterBoxInput(filter)?.isFilteringModules() - ? 'green' - : 'inherit' - } + // isInvalid={!PythonFilter.fromFilterBoxInput(filter)} + // borderColor={ + // PythonFilter.fromFilterBoxInput(filter)?.isFilteringModules() + // ? 'green' + // : 'inherit' + // } spellCheck={false} minWidth="400px" /> - {PythonFilter.fromFilterBoxInput(filter)?.isFilteringModules() && ( - - - - )} + {/*{PythonFilter.fromFilterBoxInput(filter)?.isFilteringModules() && (*/} + {/* */} + {/* */} + {/* */} + {/*)}*/} From 52ba9f625c979b638dd27320036da8c97e21515f Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Fri, 20 May 2022 18:50:49 +0200 Subject: [PATCH 06/45] fix: filter logic (probably not perfect yet) --- api-editor/gui/src/app/App.tsx | 2 +- .../packageData/model/AbstractPythonFilter.ts | 30 ---------- .../features/packageData/model/PythonClass.ts | 6 +- .../model/PythonDeclerationType.ts | 49 ---------------- .../packageData/model/PythonFunction.ts | 4 +- .../packageData/model/PythonHasName.ts | 37 ------------ .../packageData/model/PythonModule.ts | 9 ++- .../packageData/model/PythonPackage.ts | 6 +- .../model/filters/AbstractPythonFilter.ts | 58 +++++++++++++++++++ .../ConjunctiveFilter.ts} | 39 +++++++------ .../model/filters/DeclarationTypeFilter.ts | 55 ++++++++++++++++++ .../model/filters/HasNameFilter.ts | 38 ++++++++++++ .../model/{ => filters}/NullFilter.ts | 19 +++--- .../model/{ => filters}/filterFactory.ts | 18 +++--- 14 files changed, 204 insertions(+), 166 deletions(-) delete mode 100644 api-editor/gui/src/features/packageData/model/AbstractPythonFilter.ts delete mode 100644 api-editor/gui/src/features/packageData/model/PythonDeclerationType.ts delete mode 100644 api-editor/gui/src/features/packageData/model/PythonHasName.ts create mode 100644 api-editor/gui/src/features/packageData/model/filters/AbstractPythonFilter.ts rename api-editor/gui/src/features/packageData/model/{CompoundFilter.ts => filters/ConjunctiveFilter.ts} (53%) create mode 100644 api-editor/gui/src/features/packageData/model/filters/DeclarationTypeFilter.ts create mode 100644 api-editor/gui/src/features/packageData/model/filters/HasNameFilter.ts rename api-editor/gui/src/features/packageData/model/{ => filters}/NullFilter.ts (62%) rename api-editor/gui/src/features/packageData/model/{ => filters}/filterFactory.ts (57%) diff --git a/api-editor/gui/src/app/App.tsx b/api-editor/gui/src/app/App.tsx index 0b8b858d9..735bad15f 100644 --- a/api-editor/gui/src/app/App.tsx +++ b/api-editor/gui/src/app/App.tsx @@ -47,7 +47,7 @@ import AttributeForm from '../features/annotations/forms/AttributeForm'; 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/filterFactory"; +import {createFilterFromString} from "../features/packageData/model/filters/filterFactory"; const App: React.FC = function () { const dispatch = useAppDispatch(); diff --git a/api-editor/gui/src/features/packageData/model/AbstractPythonFilter.ts b/api-editor/gui/src/features/packageData/model/AbstractPythonFilter.ts deleted file mode 100644 index 21cca531b..000000000 --- a/api-editor/gui/src/features/packageData/model/AbstractPythonFilter.ts +++ /dev/null @@ -1,30 +0,0 @@ -import PythonClass from './PythonClass'; -import PythonFunction from './PythonFunction'; -import PythonModule from "./PythonModule"; -import PythonParameter from "./PythonParameter"; - -export default abstract class AbstractPythonFilter { - abstract shouldKeepModule(pythonModule: PythonModule): boolean - - abstract shouldKeepClass(pythonClass: PythonClass): boolean - - abstract shouldKeepFunction(pythonFunction: PythonFunction): boolean - - abstract shouldKeepParameter(pythonParameter: PythonParameter): boolean - - isFilteringModules(): boolean { - return true; - } - - isFilteringClasses(): boolean { - return true; - } - - isFilteringFunctions(): boolean { - return true; - } - - isFilteringParameters(): boolean { - return true; - } -} diff --git a/api-editor/gui/src/features/packageData/model/PythonClass.ts b/api-editor/gui/src/features/packageData/model/PythonClass.ts index a6051f32f..ce1bd2dbd 100644 --- a/api-editor/gui/src/features/packageData/model/PythonClass.ts +++ b/api-editor/gui/src/features/packageData/model/PythonClass.ts @@ -3,7 +3,7 @@ import { Optional } from '../../../common/util/types'; import PythonDeclaration from './PythonDeclaration'; import PythonFunction from './PythonFunction'; import PythonModule from './PythonModule'; -import AbstractPythonFilter from "./AbstractPythonFilter"; +import AbstractPythonFilter from "./filters/AbstractPythonFilter"; export default class PythonClass extends PythonDeclaration { containingModule: Optional; @@ -57,7 +57,7 @@ export default class PythonClass extends PythonDeclaration { } filter(pythonFilter: AbstractPythonFilter): PythonClass { - if (!pythonFilter.isFilteringFunctions() || pythonFilter.isFilteringClasses()) { + if (pythonFilter.canSkipClassUpdate() || pythonFilter.shouldKeepClass(this)) { return this; } @@ -67,7 +67,7 @@ export default class PythonClass extends PythonDeclaration { (it) => pythonFilter.shouldKeepFunction(it) || // Don't exclude functions without parameters when we don't filter parameters - (!pythonFilter.isFilteringParameters() || + (pythonFilter.canSkipFunctionUpdate() || !isEmptyList(it.parameters)), ); diff --git a/api-editor/gui/src/features/packageData/model/PythonDeclerationType.ts b/api-editor/gui/src/features/packageData/model/PythonDeclerationType.ts deleted file mode 100644 index f7657d63c..000000000 --- a/api-editor/gui/src/features/packageData/model/PythonDeclerationType.ts +++ /dev/null @@ -1,49 +0,0 @@ -import PythonClass from './PythonClass'; -import PythonFunction from './PythonFunction'; -import PythonModule from "./PythonModule"; -import PythonParameter from "./PythonParameter"; -import AbstractPythonFilter from "./AbstractPythonFilter"; - -export default class PythonDeclerationTypeFilter extends AbstractPythonFilter { - constructor( - readonly type: DeclerationType, - ) { - super(); - } - - shouldKeepModule(pythonModule: PythonModule): boolean { - return this.type == DeclerationType.module; - } - - shouldKeepClass(pythonClass: PythonClass): boolean { - return this.type == DeclerationType.class; - } - - shouldKeepFunction(pythonFunction: PythonFunction): boolean { - return this.type == DeclerationType.function - } - - shouldKeepParameter(pythonParameter: PythonParameter): boolean { - return this.type == DeclerationType.parameter; - } - - isFilteringModules(): boolean { - return this.type != DeclerationType.module; - } - - isFilteringClasses(): boolean { - return this.type != DeclerationType.class; - } - - isFilteringFunctions(): boolean { - return this.type != DeclerationType.function - } - - isFilteringParameters(): boolean { - return this.type != DeclerationType.parameter; - } -} - -export enum DeclerationType { - module, class, function, parameter -} diff --git a/api-editor/gui/src/features/packageData/model/PythonFunction.ts b/api-editor/gui/src/features/packageData/model/PythonFunction.ts index d5712a8a9..8cace9e2c 100644 --- a/api-editor/gui/src/features/packageData/model/PythonFunction.ts +++ b/api-editor/gui/src/features/packageData/model/PythonFunction.ts @@ -4,7 +4,7 @@ import PythonDeclaration from './PythonDeclaration'; import PythonModule from './PythonModule'; import PythonParameter from './PythonParameter'; import PythonResult from './PythonResult'; -import AbstractPythonFilter from "./AbstractPythonFilter"; +import AbstractPythonFilter from "./filters/AbstractPythonFilter"; export default class PythonFunction extends PythonDeclaration { containingModuleOrClass: Optional; @@ -97,7 +97,7 @@ export default class PythonFunction extends PythonDeclaration { } filter(pythonFilter: AbstractPythonFilter): PythonFunction { - if (!pythonFilter.isFilteringParameters() || pythonFilter.isFilteringFunctions()) { + if (pythonFilter.canSkipFunctionUpdate() || pythonFilter.shouldKeepFunction(this)) { return this; } diff --git a/api-editor/gui/src/features/packageData/model/PythonHasName.ts b/api-editor/gui/src/features/packageData/model/PythonHasName.ts deleted file mode 100644 index a395cf35e..000000000 --- a/api-editor/gui/src/features/packageData/model/PythonHasName.ts +++ /dev/null @@ -1,37 +0,0 @@ -import PythonClass from './PythonClass'; -import PythonFunction from './PythonFunction'; -import PythonModule from "./PythonModule"; -import PythonParameter from "./PythonParameter"; -import AbstractPythonFilter from "./AbstractPythonFilter"; - -export default class PythonHasNameFilter extends AbstractPythonFilter { - constructor( - readonly name: string, - ) { - super(); - } - - shouldKeepModule(pythonModule: PythonModule): boolean { - return this.filter(pythonModule.name) - } - - shouldKeepClass(pythonClass: PythonClass): boolean { - return this.filter(pythonClass.name) - } - - shouldKeepFunction(pythonFunction: PythonFunction): boolean { - return this.filter(pythonFunction.name) - } - - shouldKeepParameter(pythonParameter: PythonParameter): boolean { - return this.filter(pythonParameter.name) - } - - private filter(declerationName: string): boolean { - return declerationName - .toLowerCase() - .includes( - this.name.toLowerCase() - ) - } -} diff --git a/api-editor/gui/src/features/packageData/model/PythonModule.ts b/api-editor/gui/src/features/packageData/model/PythonModule.ts index 59f610c3c..23a1f7030 100644 --- a/api-editor/gui/src/features/packageData/model/PythonModule.ts +++ b/api-editor/gui/src/features/packageData/model/PythonModule.ts @@ -6,7 +6,7 @@ import PythonFromImport from './PythonFromImport'; import PythonFunction from './PythonFunction'; import PythonImport from './PythonImport'; import PythonPackage from './PythonPackage'; -import AbstractPythonFilter from "./AbstractPythonFilter"; +import AbstractPythonFilter from "./filters/AbstractPythonFilter"; export default class PythonModule extends PythonDeclaration { containingPackage: Optional; @@ -44,8 +44,7 @@ export default class PythonModule extends PythonDeclaration { } filter(pythonFilter: AbstractPythonFilter): PythonModule { - // isFilteringClasses is also true if we are filtering functions - if (pythonFilter.isFilteringModules()) { + if (pythonFilter.canSkipModuleUpdate() || pythonFilter.shouldKeepModule(this)) { return this; } @@ -55,7 +54,7 @@ export default class PythonModule extends PythonDeclaration { (it) => pythonFilter.shouldKeepClass(it) || // Don't exclude empty classes when we only filter modules or classes - (!pythonFilter.isFilteringFunctions() || + (pythonFilter.canSkipClassUpdate() || !isEmptyList(it.methods)), ); @@ -65,7 +64,7 @@ export default class PythonModule extends PythonDeclaration { .filter( (it) => pythonFilter.shouldKeepFunction(it) || // Don't exclude functions without parameters when we don't filter parameters - (!pythonFilter.isFilteringParameters() || + (pythonFilter.canSkipFunctionUpdate() || !isEmptyList(it.parameters)), ); diff --git a/api-editor/gui/src/features/packageData/model/PythonPackage.ts b/api-editor/gui/src/features/packageData/model/PythonPackage.ts index 8394b0f95..036631922 100644 --- a/api-editor/gui/src/features/packageData/model/PythonPackage.ts +++ b/api-editor/gui/src/features/packageData/model/PythonPackage.ts @@ -1,7 +1,7 @@ import { isEmptyList } from '../../../common/util/listOperations'; import PythonDeclaration from './PythonDeclaration'; import PythonModule from './PythonModule'; -import AbstractPythonFilter from "./AbstractPythonFilter"; +import AbstractPythonFilter from "./filters/AbstractPythonFilter"; export default class PythonPackage extends PythonDeclaration { constructor( @@ -30,7 +30,7 @@ export default class PythonPackage extends PythonDeclaration { } filter(pythonFilter: AbstractPythonFilter): PythonPackage { - if (!pythonFilter.isFilteringModules()) { + if (pythonFilter.canSkipPackageUpdate()) { return this; } @@ -40,7 +40,7 @@ export default class PythonPackage extends PythonDeclaration { (it) => pythonFilter.shouldKeepModule(it) || // Don't exclude empty modules when we only filter modules - (!pythonFilter.isFilteringClasses() || + (pythonFilter.canSkipModuleUpdate() || !isEmptyList(it.classes) || !isEmptyList(it.functions)), ); diff --git a/api-editor/gui/src/features/packageData/model/filters/AbstractPythonFilter.ts b/api-editor/gui/src/features/packageData/model/filters/AbstractPythonFilter.ts new file mode 100644 index 000000000..428eab5c7 --- /dev/null +++ b/api-editor/gui/src/features/packageData/model/filters/AbstractPythonFilter.ts @@ -0,0 +1,58 @@ +import PythonClass from '../PythonClass'; +import PythonFunction from '../PythonFunction'; +import PythonModule from "../PythonModule"; +import PythonParameter from "../PythonParameter"; + +/** + * An abstract base class for filters of Python declarations. + */ +export default abstract class AbstractPythonFilter { + + /** + * Whether the given module should be kept after filtering. + */ + abstract shouldKeepModule(pythonModule: PythonModule): boolean + + /** + * Whether the given module should be kept after filtering. + */ + abstract shouldKeepClass(pythonClass: PythonClass): boolean + + /** + * Whether the given module should be kept after filtering. + */ + abstract shouldKeepFunction(pythonFunction: PythonFunction): boolean + + /** + * Whether the given module should be kept after filtering. + */ + abstract shouldKeepParameter(pythonParameter: PythonParameter): boolean + + /** + * Returns whether the package can be returned unchanged after filtering. + */ + canSkipPackageUpdate(): boolean { + return false; + } + + /** + * Returns whether the module can be returned unchanged after filtering. + */ + canSkipModuleUpdate(): boolean { + return false; + } + + /** + * Returns whether the class can be returned unchanged after filtering. + */ + canSkipClassUpdate(): boolean { + return false; + } + + /** + * Returns whether the function can be returned unchanged after filtering. + */ + canSkipFunctionUpdate(): boolean { + return false; + } +} diff --git a/api-editor/gui/src/features/packageData/model/CompoundFilter.ts b/api-editor/gui/src/features/packageData/model/filters/ConjunctiveFilter.ts similarity index 53% rename from api-editor/gui/src/features/packageData/model/CompoundFilter.ts rename to api-editor/gui/src/features/packageData/model/filters/ConjunctiveFilter.ts index fcfbcb927..76ec1d3ce 100644 --- a/api-editor/gui/src/features/packageData/model/CompoundFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/ConjunctiveFilter.ts @@ -1,12 +1,13 @@ import AbstractPythonFilter from "./AbstractPythonFilter"; -import PythonClass from "./PythonClass"; -import PythonFunction from "./PythonFunction"; -import PythonParameter from "./PythonParameter"; -import PythonModule from "./PythonModule"; +import PythonClass from "../PythonClass"; +import PythonFunction from "../PythonFunction"; +import PythonParameter from "../PythonParameter"; +import PythonModule from "../PythonModule"; -export type FilterString = string; - -export class CompoundFilter extends AbstractPythonFilter { +/** + * Keeps declarations iff all contained filters want to keep it. + */ +export class ConjunctiveFilter extends AbstractPythonFilter { constructor( readonly filters: AbstractPythonFilter[] ) { @@ -37,27 +38,27 @@ export class CompoundFilter extends AbstractPythonFilter { ); } - isFilteringModules(): boolean { - return this.filters.some( - (it) => it.isFilteringModules() + canSkipPackageUpdate(): boolean { + return this.filters.every( + (it) => it.canSkipPackageUpdate() ); } - isFilteringClasses(): boolean { - return this.filters.some( - (it) => it.isFilteringClasses() + canSkipModuleUpdate(): boolean { + return this.filters.every( + (it) => it.canSkipModuleUpdate() ); } - isFilteringFunctions(): boolean { - return this.filters.some( - (it) => it.isFilteringFunctions() + canSkipClassUpdate(): boolean { + return this.filters.every( + (it) => it.canSkipClassUpdate() ); } - isFilteringParameters(): boolean { - return this.filters.some( - (it) => it.isFilteringParameters() + canSkipFunctionUpdate(): boolean { + return this.filters.every( + (it) => it.canSkipFunctionUpdate() ); } } diff --git a/api-editor/gui/src/features/packageData/model/filters/DeclarationTypeFilter.ts b/api-editor/gui/src/features/packageData/model/filters/DeclarationTypeFilter.ts new file mode 100644 index 000000000..1b057b37f --- /dev/null +++ b/api-editor/gui/src/features/packageData/model/filters/DeclarationTypeFilter.ts @@ -0,0 +1,55 @@ +import PythonClass from '../PythonClass'; +import PythonFunction from '../PythonFunction'; +import PythonModule from "../PythonModule"; +import PythonParameter from "../PythonParameter"; +import AbstractPythonFilter from "./AbstractPythonFilter"; + +export default class DeclarationTypeFilter extends AbstractPythonFilter { + constructor( + readonly type: DeclarationType, + ) { + super(); + } + + shouldKeepModule(pythonModule: PythonModule): boolean { + return this.type == DeclarationType.Module; + } + + shouldKeepClass(pythonClass: PythonClass): boolean { + return this.type == DeclarationType.Class; + } + + shouldKeepFunction(pythonFunction: PythonFunction): boolean { + return this.type == DeclarationType.Function + } + + shouldKeepParameter(pythonParameter: PythonParameter): boolean { + return this.type == DeclarationType.Parameter; + } + + canSkipPackageUpdate(): boolean { + return this.type == DeclarationType.Module; + } + + canSkipModuleUpdate(): boolean { + return this.type == DeclarationType.Module; + } + + canSkipClassUpdate(): boolean { + return this.type == DeclarationType.Module || + this.type == DeclarationType.Class; + } + + canSkipFunctionUpdate(): boolean { + return this.type == DeclarationType.Module || + this.type == DeclarationType.Class || + this.type == DeclarationType.Function; + } +} + +export enum DeclarationType { + Module, + Class, + Function, + Parameter +} diff --git a/api-editor/gui/src/features/packageData/model/filters/HasNameFilter.ts b/api-editor/gui/src/features/packageData/model/filters/HasNameFilter.ts new file mode 100644 index 000000000..bbafb0ecb --- /dev/null +++ b/api-editor/gui/src/features/packageData/model/filters/HasNameFilter.ts @@ -0,0 +1,38 @@ +import PythonClass from '../PythonClass'; +import PythonFunction from '../PythonFunction'; +import PythonModule from "../PythonModule"; +import PythonParameter from "../PythonParameter"; +import AbstractPythonFilter from "./AbstractPythonFilter"; +import PythonDeclaration from "../PythonDeclaration"; + +export default class HasNameFilter extends AbstractPythonFilter { + constructor( + readonly name: string, + ) { + super(); + } + + shouldKeepModule(pythonModule: PythonModule): boolean { + return this.filter(pythonModule) + } + + shouldKeepClass(pythonClass: PythonClass): boolean { + return this.filter(pythonClass) + } + + shouldKeepFunction(pythonFunction: PythonFunction): boolean { + return this.filter(pythonFunction) + } + + shouldKeepParameter(pythonParameter: PythonParameter): boolean { + return this.filter(pythonParameter) + } + + private filter(declaration: PythonDeclaration): boolean { + return declaration.name + .toLowerCase() + .includes( + this.name.toLowerCase() + ) + } +} diff --git a/api-editor/gui/src/features/packageData/model/NullFilter.ts b/api-editor/gui/src/features/packageData/model/filters/NullFilter.ts similarity index 62% rename from api-editor/gui/src/features/packageData/model/NullFilter.ts rename to api-editor/gui/src/features/packageData/model/filters/NullFilter.ts index 9f52bda1b..85517ecb7 100644 --- a/api-editor/gui/src/features/packageData/model/NullFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/NullFilter.ts @@ -1,9 +1,12 @@ -import PythonClass from './PythonClass'; -import PythonFunction from './PythonFunction'; -import PythonModule from "./PythonModule"; -import PythonParameter from "./PythonParameter"; +import PythonClass from '../PythonClass'; +import PythonFunction from '../PythonFunction'; +import PythonModule from "../PythonModule"; +import PythonParameter from "../PythonParameter"; import AbstractPythonFilter from "./AbstractPythonFilter"; +/** + * Keeps all declarations. + */ export default class NullFilter extends AbstractPythonFilter { shouldKeepModule(pythonModule: PythonModule): boolean { return true; @@ -21,19 +24,19 @@ export default class NullFilter extends AbstractPythonFilter { return true; } - isFilteringModules(): boolean { + canSkipPackageUpdate(): boolean { return false; } - isFilteringClasses(): boolean { + canSkipModuleUpdate(): boolean { return false; } - isFilteringFunctions(): boolean { + canSkipClassUpdate(): boolean { return false; } - isFilteringParameters(): boolean { + canSkipFunctionUpdate(): boolean { return false; } } diff --git a/api-editor/gui/src/features/packageData/model/filterFactory.ts b/api-editor/gui/src/features/packageData/model/filters/filterFactory.ts similarity index 57% rename from api-editor/gui/src/features/packageData/model/filterFactory.ts rename to api-editor/gui/src/features/packageData/model/filters/filterFactory.ts index 25a82c42e..8daab2a24 100644 --- a/api-editor/gui/src/features/packageData/model/filterFactory.ts +++ b/api-editor/gui/src/features/packageData/model/filters/filterFactory.ts @@ -1,7 +1,7 @@ -import {CompoundFilter} from "./CompoundFilter"; -import PythonHasNameFilter from "./PythonHasName"; +import {ConjunctiveFilter} from "./ConjunctiveFilter"; +import HasNameFilter from "./HasNameFilter"; import AbstractPythonFilter from "./AbstractPythonFilter"; -import PythonDeclerationTypeFilter, {DeclerationType} from "./PythonDeclerationType"; +import DeclarationTypeFilter, {DeclarationType} from "./DeclarationTypeFilter"; export function createFilterFromString(filterBoxInput: string): AbstractPythonFilter { const filters = [] @@ -12,21 +12,21 @@ export function createFilterFromString(filterBoxInput: string): AbstractPythonFi switch (scope) { case 'hasName': - filters.push(new PythonHasNameFilter(filterString)) + filters.push(new HasNameFilter(filterString)) break; case 'is': switch (filterString) { case 'module': - filters.push(new PythonDeclerationTypeFilter(DeclerationType.module)) + filters.push(new DeclarationTypeFilter(DeclarationType.Module)) break case 'class': - filters.push(new PythonDeclerationTypeFilter(DeclerationType.class)) + filters.push(new DeclarationTypeFilter(DeclarationType.Class)) break case 'function': - filters.push(new PythonDeclerationTypeFilter(DeclerationType.function)) + filters.push(new DeclarationTypeFilter(DeclarationType.Function)) break case 'parameter': - filters.push(new PythonDeclerationTypeFilter(DeclerationType.parameter)) + filters.push(new DeclarationTypeFilter(DeclarationType.Parameter)) break // no default } @@ -37,5 +37,5 @@ export function createFilterFromString(filterBoxInput: string): AbstractPythonFi } } - return new CompoundFilter(filters); + return new ConjunctiveFilter(filters); } From 04bffcd443963764b2a13534a95455c10b7d7069 Mon Sep 17 00:00:00 2001 From: lars-reimann Date: Fri, 20 May 2022 16:54:33 +0000 Subject: [PATCH 07/45] style: apply automatic fixes of linters --- api-editor/gui/src/app/App.tsx | 2 +- api-editor/gui/src/common/MenuBar.tsx | 29 ++++++------ .../features/packageData/model/PythonClass.ts | 6 +-- .../packageData/model/PythonFunction.ts | 16 ++----- .../packageData/model/PythonModule.ts | 22 ++++----- .../packageData/model/PythonPackage.ts | 15 ++---- .../model/filters/AbstractPythonFilter.ts | 13 +++--- .../model/filters/ConjunctiveFilter.ts | 46 ++++++------------- .../model/filters/DeclarationTypeFilter.ts | 25 +++++----- .../model/filters/HasNameFilter.ts | 26 ++++------- .../packageData/model/filters/NullFilter.ts | 6 +-- .../model/filters/filterFactory.ts | 28 +++++------ 12 files changed, 94 insertions(+), 140 deletions(-) diff --git a/api-editor/gui/src/app/App.tsx b/api-editor/gui/src/app/App.tsx index 735bad15f..4bf881fe4 100644 --- a/api-editor/gui/src/app/App.tsx +++ b/api-editor/gui/src/app/App.tsx @@ -47,7 +47,7 @@ import AttributeForm from '../features/annotations/forms/AttributeForm'; 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(); diff --git a/api-editor/gui/src/common/MenuBar.tsx b/api-editor/gui/src/common/MenuBar.tsx index 5b0173494..3504dcac7 100644 --- a/api-editor/gui/src/common/MenuBar.tsx +++ b/api-editor/gui/src/common/MenuBar.tsx @@ -36,10 +36,10 @@ import { 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 { @@ -47,8 +47,8 @@ import { togglePackageDataImportDialog, toggleShowPrivateDeclarations, } from '../features/packageData/packageDataSlice'; -import {Setter} from './util/types'; -import {toggleUsageImportDialog} from '../features/usages/usageSlice'; +import { Setter } from './util/types'; +import { toggleUsageImportDialog } from '../features/usages/usageSlice'; interface MenuBarProps { pythonPackage: PythonPackage; @@ -61,11 +61,7 @@ const HelpButton = function () { return ( - } - aria-label="help" - /> + } aria-label="help" /> @@ -273,10 +269,13 @@ const MenuBar: React.FC = function ({ pythonPackage, filter, setFi - + ; @@ -67,8 +67,8 @@ export default class PythonClass extends PythonDeclaration { (it) => pythonFilter.shouldKeepFunction(it) || // Don't exclude functions without parameters when we don't filter parameters - (pythonFilter.canSkipFunctionUpdate() || - !isEmptyList(it.parameters)), + pythonFilter.canSkipFunctionUpdate() || + !isEmptyList(it.parameters), ); return new PythonClass( diff --git a/api-editor/gui/src/features/packageData/model/PythonFunction.ts b/api-editor/gui/src/features/packageData/model/PythonFunction.ts index 8cace9e2c..ea09582c4 100644 --- a/api-editor/gui/src/features/packageData/model/PythonFunction.ts +++ b/api-editor/gui/src/features/packageData/model/PythonFunction.ts @@ -4,7 +4,7 @@ import PythonDeclaration from './PythonDeclaration'; import PythonModule from './PythonModule'; import PythonParameter from './PythonParameter'; import PythonResult from './PythonResult'; -import AbstractPythonFilter from "./filters/AbstractPythonFilter"; +import AbstractPythonFilter from './filters/AbstractPythonFilter'; export default class PythonFunction extends PythonDeclaration { containingModuleOrClass: Optional; @@ -74,10 +74,7 @@ export default class PythonFunction extends PythonDeclaration { return ( (this.parent() ?.children() - .filter( - (it) => - it instanceof PythonFunction && it.name !== this.name, - ) as PythonFunction[]) ?? [] + .filter((it) => it instanceof PythonFunction && it.name !== this.name) as PythonFunction[]) ?? [] ); } @@ -89,9 +86,7 @@ export default class PythonFunction extends PythonDeclaration { result += ' '; } - result += `def ${this.name}(${this.parameters - .map((it) => it.name) - .join(', ')})`; + result += `def ${this.name}(${this.parameters.map((it) => it.name).join(', ')})`; return result; } @@ -101,10 +96,7 @@ export default class PythonFunction extends PythonDeclaration { return this; } - const parameters = this.parameters - .map((it) => it.clone()) - .filter((it) => pythonFilter.shouldKeepParameter(it) - ); + const parameters = this.parameters.map((it) => it.clone()).filter((it) => pythonFilter.shouldKeepParameter(it)); const results = this.results.map((it) => it.clone()); diff --git a/api-editor/gui/src/features/packageData/model/PythonModule.ts b/api-editor/gui/src/features/packageData/model/PythonModule.ts index 23a1f7030..ab579278b 100644 --- a/api-editor/gui/src/features/packageData/model/PythonModule.ts +++ b/api-editor/gui/src/features/packageData/model/PythonModule.ts @@ -6,7 +6,7 @@ import PythonFromImport from './PythonFromImport'; import PythonFunction from './PythonFunction'; import PythonImport from './PythonImport'; import PythonPackage from './PythonPackage'; -import AbstractPythonFilter from "./filters/AbstractPythonFilter"; +import AbstractPythonFilter from './filters/AbstractPythonFilter'; export default class PythonModule extends PythonDeclaration { containingPackage: Optional; @@ -54,26 +54,20 @@ export default class PythonModule extends PythonDeclaration { (it) => pythonFilter.shouldKeepClass(it) || // Don't exclude empty classes when we only filter modules or classes - (pythonFilter.canSkipClassUpdate() || - !isEmptyList(it.methods)), + pythonFilter.canSkipClassUpdate() || + !isEmptyList(it.methods), ); - const functions = this.functions .map((it) => it.filter(pythonFilter)) .filter( - (it) => pythonFilter.shouldKeepFunction(it) || + (it) => + pythonFilter.shouldKeepFunction(it) || // Don't exclude functions without parameters when we don't filter parameters - (pythonFilter.canSkipFunctionUpdate() || - !isEmptyList(it.parameters)), + pythonFilter.canSkipFunctionUpdate() || + !isEmptyList(it.parameters), ); - return new PythonModule( - this.name, - this.imports, - this.fromImports, - classes, - functions, - ); + return new PythonModule(this.name, this.imports, this.fromImports, classes, functions); } } diff --git a/api-editor/gui/src/features/packageData/model/PythonPackage.ts b/api-editor/gui/src/features/packageData/model/PythonPackage.ts index 036631922..b91853659 100644 --- a/api-editor/gui/src/features/packageData/model/PythonPackage.ts +++ b/api-editor/gui/src/features/packageData/model/PythonPackage.ts @@ -1,7 +1,7 @@ import { isEmptyList } from '../../../common/util/listOperations'; import PythonDeclaration from './PythonDeclaration'; import PythonModule from './PythonModule'; -import AbstractPythonFilter from "./filters/AbstractPythonFilter"; +import AbstractPythonFilter from './filters/AbstractPythonFilter'; export default class PythonPackage extends PythonDeclaration { constructor( @@ -40,16 +40,11 @@ export default class PythonPackage extends PythonDeclaration { (it) => pythonFilter.shouldKeepModule(it) || // Don't exclude empty modules when we only filter modules - (pythonFilter.canSkipModuleUpdate() || - !isEmptyList(it.classes) || - !isEmptyList(it.functions)), + pythonFilter.canSkipModuleUpdate() || + !isEmptyList(it.classes) || + !isEmptyList(it.functions), ); - return new PythonPackage( - this.distribution, - this.name, - this.version, - modules, - ); + return new PythonPackage(this.distribution, this.name, this.version, modules); } } 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 428eab5c7..127e3288c 100644 --- a/api-editor/gui/src/features/packageData/model/filters/AbstractPythonFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/AbstractPythonFilter.ts @@ -1,32 +1,31 @@ import PythonClass from '../PythonClass'; import PythonFunction from '../PythonFunction'; -import PythonModule from "../PythonModule"; -import PythonParameter from "../PythonParameter"; +import PythonModule from '../PythonModule'; +import PythonParameter from '../PythonParameter'; /** * An abstract base class for filters of Python declarations. */ export default abstract class AbstractPythonFilter { - /** * Whether the given module should be kept after filtering. */ - abstract shouldKeepModule(pythonModule: PythonModule): boolean + abstract shouldKeepModule(pythonModule: PythonModule): boolean; /** * Whether the given module should be kept after filtering. */ - abstract shouldKeepClass(pythonClass: PythonClass): boolean + abstract shouldKeepClass(pythonClass: PythonClass): boolean; /** * Whether the given module should be kept after filtering. */ - abstract shouldKeepFunction(pythonFunction: PythonFunction): boolean + abstract shouldKeepFunction(pythonFunction: PythonFunction): boolean; /** * Whether the given module should be kept after filtering. */ - abstract shouldKeepParameter(pythonParameter: PythonParameter): boolean + abstract shouldKeepParameter(pythonParameter: PythonParameter): boolean; /** * Returns whether the package can be returned unchanged after filtering. 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 76ec1d3ce..928fa38d7 100644 --- a/api-editor/gui/src/features/packageData/model/filters/ConjunctiveFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/ConjunctiveFilter.ts @@ -1,64 +1,46 @@ -import AbstractPythonFilter from "./AbstractPythonFilter"; -import PythonClass from "../PythonClass"; -import PythonFunction from "../PythonFunction"; -import PythonParameter from "../PythonParameter"; -import PythonModule from "../PythonModule"; +import AbstractPythonFilter from './AbstractPythonFilter'; +import PythonClass from '../PythonClass'; +import PythonFunction from '../PythonFunction'; +import PythonParameter from '../PythonParameter'; +import PythonModule from '../PythonModule'; /** * Keeps declarations iff all contained filters want to keep it. */ export class ConjunctiveFilter extends AbstractPythonFilter { - constructor( - readonly filters: AbstractPythonFilter[] - ) { + constructor(readonly filters: AbstractPythonFilter[]) { super(); } shouldKeepClass(pythonClass: PythonClass): boolean { - return this.filters.every( - (it) => it.shouldKeepClass(pythonClass) - ); + return this.filters.every((it) => it.shouldKeepClass(pythonClass)); } shouldKeepFunction(pythonFunction: PythonFunction): boolean { - return this.filters.every( - (it) => it.shouldKeepFunction(pythonFunction) - ); + return this.filters.every((it) => it.shouldKeepFunction(pythonFunction)); } shouldKeepModule(pythonModule: PythonModule): boolean { - return this.filters.every( - (it) => it.shouldKeepModule(pythonModule) - ); + return this.filters.every((it) => it.shouldKeepModule(pythonModule)); } shouldKeepParameter(pythonParameter: PythonParameter): boolean { - return this.filters.every( - (it) => it.shouldKeepParameter(pythonParameter) - ); + return this.filters.every((it) => it.shouldKeepParameter(pythonParameter)); } canSkipPackageUpdate(): boolean { - return this.filters.every( - (it) => it.canSkipPackageUpdate() - ); + return this.filters.every((it) => it.canSkipPackageUpdate()); } canSkipModuleUpdate(): boolean { - return this.filters.every( - (it) => it.canSkipModuleUpdate() - ); + return this.filters.every((it) => it.canSkipModuleUpdate()); } canSkipClassUpdate(): boolean { - return this.filters.every( - (it) => it.canSkipClassUpdate() - ); + return this.filters.every((it) => it.canSkipClassUpdate()); } canSkipFunctionUpdate(): boolean { - return this.filters.every( - (it) => it.canSkipFunctionUpdate() - ); + return this.filters.every((it) => it.canSkipFunctionUpdate()); } } 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 1b057b37f..a4503c0d2 100644 --- a/api-editor/gui/src/features/packageData/model/filters/DeclarationTypeFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/DeclarationTypeFilter.ts @@ -1,13 +1,11 @@ import PythonClass from '../PythonClass'; import PythonFunction from '../PythonFunction'; -import PythonModule from "../PythonModule"; -import PythonParameter from "../PythonParameter"; -import AbstractPythonFilter from "./AbstractPythonFilter"; +import PythonModule from '../PythonModule'; +import PythonParameter from '../PythonParameter'; +import AbstractPythonFilter from './AbstractPythonFilter'; export default class DeclarationTypeFilter extends AbstractPythonFilter { - constructor( - readonly type: DeclarationType, - ) { + constructor(readonly type: DeclarationType) { super(); } @@ -20,7 +18,7 @@ export default class DeclarationTypeFilter extends AbstractPythonFilter { } shouldKeepFunction(pythonFunction: PythonFunction): boolean { - return this.type == DeclarationType.Function + return this.type == DeclarationType.Function; } shouldKeepParameter(pythonParameter: PythonParameter): boolean { @@ -36,14 +34,15 @@ export default class DeclarationTypeFilter extends AbstractPythonFilter { } canSkipClassUpdate(): boolean { - return this.type == DeclarationType.Module || - this.type == DeclarationType.Class; + return this.type == DeclarationType.Module || this.type == DeclarationType.Class; } canSkipFunctionUpdate(): boolean { - return this.type == DeclarationType.Module || - this.type == DeclarationType.Class || - this.type == DeclarationType.Function; + return ( + this.type == DeclarationType.Module || + this.type == DeclarationType.Class || + this.type == DeclarationType.Function + ); } } @@ -51,5 +50,5 @@ export enum DeclarationType { Module, Class, Function, - Parameter + Parameter, } diff --git a/api-editor/gui/src/features/packageData/model/filters/HasNameFilter.ts b/api-editor/gui/src/features/packageData/model/filters/HasNameFilter.ts index bbafb0ecb..8c7eef19b 100644 --- a/api-editor/gui/src/features/packageData/model/filters/HasNameFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/HasNameFilter.ts @@ -1,38 +1,32 @@ import PythonClass from '../PythonClass'; import PythonFunction from '../PythonFunction'; -import PythonModule from "../PythonModule"; -import PythonParameter from "../PythonParameter"; -import AbstractPythonFilter from "./AbstractPythonFilter"; -import PythonDeclaration from "../PythonDeclaration"; +import PythonModule from '../PythonModule'; +import PythonParameter from '../PythonParameter'; +import AbstractPythonFilter from './AbstractPythonFilter'; +import PythonDeclaration from '../PythonDeclaration'; export default class HasNameFilter extends AbstractPythonFilter { - constructor( - readonly name: string, - ) { + constructor(readonly name: string) { super(); } shouldKeepModule(pythonModule: PythonModule): boolean { - return this.filter(pythonModule) + return this.filter(pythonModule); } shouldKeepClass(pythonClass: PythonClass): boolean { - return this.filter(pythonClass) + return this.filter(pythonClass); } shouldKeepFunction(pythonFunction: PythonFunction): boolean { - return this.filter(pythonFunction) + return this.filter(pythonFunction); } shouldKeepParameter(pythonParameter: PythonParameter): boolean { - return this.filter(pythonParameter) + return this.filter(pythonParameter); } private filter(declaration: PythonDeclaration): boolean { - return declaration.name - .toLowerCase() - .includes( - this.name.toLowerCase() - ) + return declaration.name.toLowerCase().includes(this.name.toLowerCase()); } } diff --git a/api-editor/gui/src/features/packageData/model/filters/NullFilter.ts b/api-editor/gui/src/features/packageData/model/filters/NullFilter.ts index 85517ecb7..4172ec883 100644 --- a/api-editor/gui/src/features/packageData/model/filters/NullFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/NullFilter.ts @@ -1,8 +1,8 @@ import PythonClass from '../PythonClass'; import PythonFunction from '../PythonFunction'; -import PythonModule from "../PythonModule"; -import PythonParameter from "../PythonParameter"; -import AbstractPythonFilter from "./AbstractPythonFilter"; +import PythonModule from '../PythonModule'; +import PythonParameter from '../PythonParameter'; +import AbstractPythonFilter from './AbstractPythonFilter'; /** * Keeps all declarations. 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 8daab2a24..6df6ae8b2 100644 --- a/api-editor/gui/src/features/packageData/model/filters/filterFactory.ts +++ b/api-editor/gui/src/features/packageData/model/filters/filterFactory.ts @@ -1,10 +1,10 @@ -import {ConjunctiveFilter} from "./ConjunctiveFilter"; -import HasNameFilter from "./HasNameFilter"; -import AbstractPythonFilter from "./AbstractPythonFilter"; -import DeclarationTypeFilter, {DeclarationType} from "./DeclarationTypeFilter"; +import { ConjunctiveFilter } from './ConjunctiveFilter'; +import HasNameFilter from './HasNameFilter'; +import AbstractPythonFilter from './AbstractPythonFilter'; +import DeclarationTypeFilter, { DeclarationType } from './DeclarationTypeFilter'; export function createFilterFromString(filterBoxInput: string): AbstractPythonFilter { - const filters = [] + const filters = []; for (const match of filterBoxInput.matchAll(/(\w+):([^\s:]+)/gu)) { if (match.length === 3) { @@ -12,22 +12,22 @@ export function createFilterFromString(filterBoxInput: string): AbstractPythonFi switch (scope) { case 'hasName': - filters.push(new HasNameFilter(filterString)) + filters.push(new HasNameFilter(filterString)); break; case 'is': switch (filterString) { case 'module': - filters.push(new DeclarationTypeFilter(DeclarationType.Module)) - break + filters.push(new DeclarationTypeFilter(DeclarationType.Module)); + break; case 'class': - filters.push(new DeclarationTypeFilter(DeclarationType.Class)) - break + filters.push(new DeclarationTypeFilter(DeclarationType.Class)); + break; case 'function': - filters.push(new DeclarationTypeFilter(DeclarationType.Function)) - break + filters.push(new DeclarationTypeFilter(DeclarationType.Function)); + break; case 'parameter': - filters.push(new DeclarationTypeFilter(DeclarationType.Parameter)) - break + filters.push(new DeclarationTypeFilter(DeclarationType.Parameter)); + break; // no default } From 0c2c9673da5e8441679f5714a5be3f89a0778f20 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sat, 21 May 2022 16:11:10 +0200 Subject: [PATCH 08/45] refactor: move filter logic completely into the filters --- api-editor/gui/src/app/App.tsx | 2 +- .../features/packageData/model/PythonClass.ts | 31 +--- .../packageData/model/PythonFunction.ts | 35 +--- .../packageData/model/PythonModule.ts | 32 +--- .../packageData/model/PythonPackage.ts | 21 --- .../model/filters/AbstractPythonFilter.ts | 174 ++++++++++++++++-- .../model/filters/ConjunctiveFilter.ts | 52 ++++-- 7 files changed, 206 insertions(+), 141 deletions(-) diff --git a/api-editor/gui/src/app/App.tsx b/api-editor/gui/src/app/App.tsx index 4bf881fe4..da6748e72 100644 --- a/api-editor/gui/src/app/App.tsx +++ b/api-editor/gui/src/app/App.tsx @@ -94,7 +94,7 @@ const App: React.FC = function () { const [filter, setFilter] = useState(''); const pythonFilter = createFilterFromString(filter); - const filteredPythonPackage = pythonPackage.filter(pythonFilter); + const filteredPythonPackage = pythonFilter.applyToPackage(pythonPackage); const userActionTarget = pythonPackage.getByRelativePathAsString(currentUserAction.target); diff --git a/api-editor/gui/src/features/packageData/model/PythonClass.ts b/api-editor/gui/src/features/packageData/model/PythonClass.ts index 027182d7d..134305507 100644 --- a/api-editor/gui/src/features/packageData/model/PythonClass.ts +++ b/api-editor/gui/src/features/packageData/model/PythonClass.ts @@ -1,9 +1,7 @@ -import { isEmptyList } from '../../../common/util/listOperations'; -import { Optional } from '../../../common/util/types'; +import {Optional} from '../../../common/util/types'; import PythonDeclaration from './PythonDeclaration'; import PythonFunction from './PythonFunction'; import PythonModule from './PythonModule'; -import AbstractPythonFilter from './filters/AbstractPythonFilter'; export default class PythonClass extends PythonDeclaration { containingModule: Optional; @@ -55,31 +53,4 @@ export default class PythonClass extends PythonDeclaration { return result; } - - filter(pythonFilter: AbstractPythonFilter): PythonClass { - if (pythonFilter.canSkipClassUpdate() || pythonFilter.shouldKeepClass(this)) { - return this; - } - - const methods = this.methods - .map((it) => it.filter(pythonFilter)) - .filter( - (it) => - pythonFilter.shouldKeepFunction(it) || - // Don't exclude functions without parameters when we don't filter parameters - pythonFilter.canSkipFunctionUpdate() || - !isEmptyList(it.parameters), - ); - - return new PythonClass( - this.name, - this.qualifiedName, - this.decorators, - this.superclasses, - methods, - this.isPublic, - this.description, - this.fullDocstring, - ); - } } diff --git a/api-editor/gui/src/features/packageData/model/PythonFunction.ts b/api-editor/gui/src/features/packageData/model/PythonFunction.ts index ea09582c4..be9bf1421 100644 --- a/api-editor/gui/src/features/packageData/model/PythonFunction.ts +++ b/api-editor/gui/src/features/packageData/model/PythonFunction.ts @@ -1,10 +1,9 @@ -import { Optional } from '../../../common/util/types'; +import {Optional} from '../../../common/util/types'; import PythonClass from './PythonClass'; import PythonDeclaration from './PythonDeclaration'; import PythonModule from './PythonModule'; import PythonParameter from './PythonParameter'; import PythonResult from './PythonResult'; -import AbstractPythonFilter from './filters/AbstractPythonFilter'; export default class PythonFunction extends PythonDeclaration { containingModuleOrClass: Optional; @@ -74,7 +73,10 @@ export default class PythonFunction extends PythonDeclaration { return ( (this.parent() ?.children() - .filter((it) => it instanceof PythonFunction && it.name !== this.name) as PythonFunction[]) ?? [] + .filter( + (it) => + it instanceof PythonFunction && it.name !== this.name, + ) as PythonFunction[]) ?? [] ); } @@ -86,31 +88,10 @@ export default class PythonFunction extends PythonDeclaration { result += ' '; } - result += `def ${this.name}(${this.parameters.map((it) => it.name).join(', ')})`; + result += `def ${this.name}(${this.parameters + .map((it) => it.name) + .join(', ')})`; return result; } - - filter(pythonFilter: AbstractPythonFilter): PythonFunction { - if (pythonFilter.canSkipFunctionUpdate() || pythonFilter.shouldKeepFunction(this)) { - return this; - } - - const parameters = this.parameters.map((it) => it.clone()).filter((it) => pythonFilter.shouldKeepParameter(it)); - - const results = this.results.map((it) => it.clone()); - - return new PythonFunction( - this.name, - this.uniqueName, - this.qualifiedName, - this.uniqueQualifiedName, - this.decorators, - parameters, - results, - this.isPublic, - this.description, - this.fullDocstring, - ); - } } diff --git a/api-editor/gui/src/features/packageData/model/PythonModule.ts b/api-editor/gui/src/features/packageData/model/PythonModule.ts index ab579278b..f1783c457 100644 --- a/api-editor/gui/src/features/packageData/model/PythonModule.ts +++ b/api-editor/gui/src/features/packageData/model/PythonModule.ts @@ -1,12 +1,10 @@ -import { isEmptyList } from '../../../common/util/listOperations'; -import { Optional } from '../../../common/util/types'; +import {Optional} from '../../../common/util/types'; import PythonClass from './PythonClass'; import PythonDeclaration from './PythonDeclaration'; import PythonFromImport from './PythonFromImport'; import PythonFunction from './PythonFunction'; import PythonImport from './PythonImport'; import PythonPackage from './PythonPackage'; -import AbstractPythonFilter from './filters/AbstractPythonFilter'; export default class PythonModule extends PythonDeclaration { containingPackage: Optional; @@ -42,32 +40,4 @@ export default class PythonModule extends PythonDeclaration { toString(): string { return `Module "${this.name}"`; } - - filter(pythonFilter: AbstractPythonFilter): PythonModule { - if (pythonFilter.canSkipModuleUpdate() || pythonFilter.shouldKeepModule(this)) { - return this; - } - - const classes = this.classes - .map((it) => it.filter(pythonFilter)) - .filter( - (it) => - pythonFilter.shouldKeepClass(it) || - // Don't exclude empty classes when we only filter modules or classes - pythonFilter.canSkipClassUpdate() || - !isEmptyList(it.methods), - ); - - const functions = this.functions - .map((it) => it.filter(pythonFilter)) - .filter( - (it) => - pythonFilter.shouldKeepFunction(it) || - // Don't exclude functions without parameters when we don't filter parameters - pythonFilter.canSkipFunctionUpdate() || - !isEmptyList(it.parameters), - ); - - return new PythonModule(this.name, this.imports, this.fromImports, classes, functions); - } } diff --git a/api-editor/gui/src/features/packageData/model/PythonPackage.ts b/api-editor/gui/src/features/packageData/model/PythonPackage.ts index b91853659..8773ae4f4 100644 --- a/api-editor/gui/src/features/packageData/model/PythonPackage.ts +++ b/api-editor/gui/src/features/packageData/model/PythonPackage.ts @@ -1,7 +1,5 @@ -import { isEmptyList } from '../../../common/util/listOperations'; import PythonDeclaration from './PythonDeclaration'; import PythonModule from './PythonModule'; -import AbstractPythonFilter from './filters/AbstractPythonFilter'; export default class PythonPackage extends PythonDeclaration { constructor( @@ -28,23 +26,4 @@ export default class PythonPackage extends PythonDeclaration { toString(): string { return `Package "${this.distribution}/${this.name} v${this.version}"`; } - - filter(pythonFilter: AbstractPythonFilter): PythonPackage { - if (pythonFilter.canSkipPackageUpdate()) { - return this; - } - - const modules = this.modules - .map((it) => it.filter(pythonFilter)) - .filter( - (it) => - pythonFilter.shouldKeepModule(it) || - // Don't exclude empty modules when we only filter modules - pythonFilter.canSkipModuleUpdate() || - !isEmptyList(it.classes) || - !isEmptyList(it.functions), - ); - - return new PythonPackage(this.distribution, this.name, this.version, modules); - } } 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 127e3288c..886e05c41 100644 --- a/api-editor/gui/src/features/packageData/model/filters/AbstractPythonFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/AbstractPythonFilter.ts @@ -1,57 +1,203 @@ import PythonClass from '../PythonClass'; import PythonFunction from '../PythonFunction'; -import PythonModule from '../PythonModule'; -import PythonParameter from '../PythonParameter'; +import PythonModule from "../PythonModule"; +import PythonParameter from "../PythonParameter"; +import PythonPackage from "../PythonPackage"; +import {isEmptyList} from "../../../../common/util/listOperations"; /** - * An abstract base class for filters of Python declarations. + * An abstract base class for filters of Python declarations. To create a new filter create a new subclass and override + * the shouldKeepXXX methods. To speed up the filter, you can also override the canSkipXXXUpdate methods. */ export default abstract class AbstractPythonFilter { + /** * Whether the given module should be kept after filtering. */ - abstract shouldKeepModule(pythonModule: PythonModule): boolean; + abstract shouldKeepModule(pythonModule: PythonModule): boolean /** - * Whether the given module should be kept after filtering. + * Whether the given class should be kept after filtering. */ - abstract shouldKeepClass(pythonClass: PythonClass): boolean; + abstract shouldKeepClass(pythonClass: PythonClass): boolean /** - * Whether the given module should be kept after filtering. + * Whether the given function should be kept after filtering. */ - abstract shouldKeepFunction(pythonFunction: PythonFunction): boolean; + abstract shouldKeepFunction(pythonFunction: PythonFunction): boolean /** - * Whether the given module should be kept after filtering. + * Whether the given parameter should be kept after filtering. */ - abstract shouldKeepParameter(pythonParameter: PythonParameter): boolean; + abstract shouldKeepParameter(pythonParameter: PythonParameter): boolean /** - * Returns whether the package can be returned unchanged after filtering. + * Returns whether the package can be returned unchanged after filtering. Use this to improve the performance of + * the filter but ensure correctness. */ canSkipPackageUpdate(): boolean { return false; } /** - * Returns whether the module can be returned unchanged after filtering. + * Returns whether the module can be returned unchanged after filtering. Use this to improve the performance of + * the filter but ensure correctness. */ canSkipModuleUpdate(): boolean { return false; } /** - * Returns whether the class can be returned unchanged after filtering. + * Returns whether the class can be returned unchanged after filtering. Use this to improve the performance of + * the filter but ensure correctness. */ canSkipClassUpdate(): boolean { return false; } /** - * Returns whether the function can be returned unchanged after filtering. + * Returns whether the function can be returned unchanged after filtering. Use this to improve the performance of + * the filter but ensure correctness. */ canSkipFunctionUpdate(): boolean { return false; } + + /** + * Apply this filter to the given package and create a package with filtered modules. + */ + applyToPackage(pythonPackage: PythonPackage): PythonPackage { + + // If the package is kept, keep the entire subtree + if (this.canSkipPackageUpdate()) { + return pythonPackage; + } + + // Filter modules + const modules = pythonPackage.modules + .map((it) => this.applyToModule(it)) + .filter( + (it) => + this.shouldKeepModule(it) || + // Don't exclude empty modules when we only filter modules + (this.canSkipModuleUpdate() || + !isEmptyList(it.classes) || + !isEmptyList(it.functions)), + ); + + // Create filtered package + return new PythonPackage( + pythonPackage.distribution, + pythonPackage.name, + pythonPackage.version, + modules, + ); + } + + /** + * Apply this filter to the given module and create a module with filtered classes and functions. + */ + applyToModule(pythonModule: PythonModule): PythonModule { + + // If the module is kept, keep the entire subtree + if (this.canSkipModuleUpdate() || this.shouldKeepModule(pythonModule)) { + return pythonModule; + } + + // Filter classes + const classes = pythonModule.classes + .map((it) => this.applyToClass(it)) + .filter( + (it) => + this.shouldKeepClass(it) || + // Don't exclude empty classes when we only filter modules or classes + (this.canSkipClassUpdate() || + !isEmptyList(it.methods)), + ); + + // Filter functions + const functions = pythonModule.functions + .map((it) => this.applyToFunction(it)) + .filter( + (it) => this.shouldKeepFunction(it) || + // Don't exclude functions without parameters when we don't filter parameters + (this.canSkipFunctionUpdate() || + !isEmptyList(it.parameters)), + ); + + // Create filtered module + return new PythonModule( + pythonModule.name, + pythonModule.imports, + pythonModule.fromImports, + classes, + functions, + ); + } + + /** + * Apply this filter to the given class and create a class with filtered methods. + */ + applyToClass(pythonClass: PythonClass): PythonClass { + + // If the class is kept, keep the entire subtree + if (this.canSkipClassUpdate() || this.shouldKeepClass(pythonClass)) { + return pythonClass; + } + + // Filter methods + const methods = pythonClass.methods + .map((it) => this.applyToFunction(it)) + .filter( + (it) => + this.shouldKeepFunction(it) || + (this.canSkipFunctionUpdate() || + !isEmptyList(it.parameters)), + ); + + // Create filtered class + return new PythonClass( + pythonClass.name, + pythonClass.qualifiedName, + pythonClass.decorators, + pythonClass.superclasses, + methods, + pythonClass.isPublic, + pythonClass.description, + pythonClass.fullDocstring, + ); + } + + /** + * Apply this filter to the given function and create a function with filtered parameters. + */ + applyToFunction(pythonFunction: PythonFunction): PythonFunction { + + // If the function is kept, keep the entire subtree + if (this.canSkipFunctionUpdate() || this.shouldKeepFunction(pythonFunction)) { + return pythonFunction; + } + + // Filter parameters + const parameters = pythonFunction.parameters + .map((it) => it.clone()) + .filter((it) => this.shouldKeepParameter(it)); + + // Filter results + const results = pythonFunction.results.map((it) => it.clone()); + + // Create filtered function + return new PythonFunction( + pythonFunction.name, + pythonFunction.uniqueName, + pythonFunction.qualifiedName, + pythonFunction.uniqueQualifiedName, + pythonFunction.decorators, + parameters, + results, + pythonFunction.isPublic, + pythonFunction.description, + pythonFunction.fullDocstring, + ); + } } 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 928fa38d7..b26ae2fa7 100644 --- a/api-editor/gui/src/features/packageData/model/filters/ConjunctiveFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/ConjunctiveFilter.ts @@ -1,46 +1,64 @@ -import AbstractPythonFilter from './AbstractPythonFilter'; -import PythonClass from '../PythonClass'; -import PythonFunction from '../PythonFunction'; -import PythonParameter from '../PythonParameter'; -import PythonModule from '../PythonModule'; +import AbstractPythonFilter from "./AbstractPythonFilter"; +import PythonClass from "../PythonClass"; +import PythonFunction from "../PythonFunction"; +import PythonParameter from "../PythonParameter"; +import PythonModule from "../PythonModule"; /** * Keeps declarations iff all contained filters want to keep it. */ export class ConjunctiveFilter extends AbstractPythonFilter { - constructor(readonly filters: AbstractPythonFilter[]) { + constructor( + readonly filters: AbstractPythonFilter[] + ) { super(); } - shouldKeepClass(pythonClass: PythonClass): boolean { - return this.filters.every((it) => it.shouldKeepClass(pythonClass)); + shouldKeepModule(pythonModule: PythonModule): boolean { + return this.filters.every( + (it) => it.shouldKeepModule(pythonModule) + ); } - shouldKeepFunction(pythonFunction: PythonFunction): boolean { - return this.filters.every((it) => it.shouldKeepFunction(pythonFunction)); + shouldKeepClass(pythonClass: PythonClass): boolean { + return this.filters.every( + (it) => it.shouldKeepClass(pythonClass) + ); } - shouldKeepModule(pythonModule: PythonModule): boolean { - return this.filters.every((it) => it.shouldKeepModule(pythonModule)); + shouldKeepFunction(pythonFunction: PythonFunction): boolean { + return this.filters.every( + (it) => it.shouldKeepFunction(pythonFunction) + ); } shouldKeepParameter(pythonParameter: PythonParameter): boolean { - return this.filters.every((it) => it.shouldKeepParameter(pythonParameter)); + return this.filters.every( + (it) => it.shouldKeepParameter(pythonParameter) + ); } canSkipPackageUpdate(): boolean { - return this.filters.every((it) => it.canSkipPackageUpdate()); + return this.filters.every( + (it) => it.canSkipPackageUpdate() + ); } canSkipModuleUpdate(): boolean { - return this.filters.every((it) => it.canSkipModuleUpdate()); + return this.filters.every( + (it) => it.canSkipModuleUpdate() + ); } canSkipClassUpdate(): boolean { - return this.filters.every((it) => it.canSkipClassUpdate()); + return this.filters.every( + (it) => it.canSkipClassUpdate() + ); } canSkipFunctionUpdate(): boolean { - return this.filters.every((it) => it.canSkipFunctionUpdate()); + return this.filters.every( + (it) => it.canSkipFunctionUpdate() + ); } } From a7e3deddc7cd8d3a0dd55cb93cc9b6759f798cf4 Mon Sep 17 00:00:00 2001 From: lars-reimann Date: Sat, 21 May 2022 14:15:50 +0000 Subject: [PATCH 09/45] style: apply automatic fixes of linters --- .../features/packageData/model/PythonClass.ts | 2 +- .../packageData/model/PythonFunction.ts | 11 +--- .../packageData/model/PythonModule.ts | 2 +- .../model/filters/AbstractPythonFilter.ts | 60 +++++++------------ .../model/filters/ConjunctiveFilter.ts | 46 +++++--------- 5 files changed, 39 insertions(+), 82 deletions(-) diff --git a/api-editor/gui/src/features/packageData/model/PythonClass.ts b/api-editor/gui/src/features/packageData/model/PythonClass.ts index 134305507..cd24de6e0 100644 --- a/api-editor/gui/src/features/packageData/model/PythonClass.ts +++ b/api-editor/gui/src/features/packageData/model/PythonClass.ts @@ -1,4 +1,4 @@ -import {Optional} from '../../../common/util/types'; +import { Optional } from '../../../common/util/types'; import PythonDeclaration from './PythonDeclaration'; import PythonFunction from './PythonFunction'; import PythonModule from './PythonModule'; diff --git a/api-editor/gui/src/features/packageData/model/PythonFunction.ts b/api-editor/gui/src/features/packageData/model/PythonFunction.ts index be9bf1421..02dcfc0ab 100644 --- a/api-editor/gui/src/features/packageData/model/PythonFunction.ts +++ b/api-editor/gui/src/features/packageData/model/PythonFunction.ts @@ -1,4 +1,4 @@ -import {Optional} from '../../../common/util/types'; +import { Optional } from '../../../common/util/types'; import PythonClass from './PythonClass'; import PythonDeclaration from './PythonDeclaration'; import PythonModule from './PythonModule'; @@ -73,10 +73,7 @@ export default class PythonFunction extends PythonDeclaration { return ( (this.parent() ?.children() - .filter( - (it) => - it instanceof PythonFunction && it.name !== this.name, - ) as PythonFunction[]) ?? [] + .filter((it) => it instanceof PythonFunction && it.name !== this.name) as PythonFunction[]) ?? [] ); } @@ -88,9 +85,7 @@ export default class PythonFunction extends PythonDeclaration { result += ' '; } - result += `def ${this.name}(${this.parameters - .map((it) => it.name) - .join(', ')})`; + result += `def ${this.name}(${this.parameters.map((it) => it.name).join(', ')})`; return result; } diff --git a/api-editor/gui/src/features/packageData/model/PythonModule.ts b/api-editor/gui/src/features/packageData/model/PythonModule.ts index f1783c457..59ebad0ff 100644 --- a/api-editor/gui/src/features/packageData/model/PythonModule.ts +++ b/api-editor/gui/src/features/packageData/model/PythonModule.ts @@ -1,4 +1,4 @@ -import {Optional} from '../../../common/util/types'; +import { Optional } from '../../../common/util/types'; import PythonClass from './PythonClass'; import PythonDeclaration from './PythonDeclaration'; import PythonFromImport from './PythonFromImport'; 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 886e05c41..e43d91c69 100644 --- a/api-editor/gui/src/features/packageData/model/filters/AbstractPythonFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/AbstractPythonFilter.ts @@ -1,35 +1,34 @@ import PythonClass from '../PythonClass'; import PythonFunction from '../PythonFunction'; -import PythonModule from "../PythonModule"; -import PythonParameter from "../PythonParameter"; -import PythonPackage from "../PythonPackage"; -import {isEmptyList} from "../../../../common/util/listOperations"; +import PythonModule from '../PythonModule'; +import PythonParameter from '../PythonParameter'; +import PythonPackage from '../PythonPackage'; +import { isEmptyList } from '../../../../common/util/listOperations'; /** * An abstract base class for filters of Python declarations. To create a new filter create a new subclass and override * the shouldKeepXXX methods. To speed up the filter, you can also override the canSkipXXXUpdate methods. */ export default abstract class AbstractPythonFilter { - /** * Whether the given module should be kept after filtering. */ - abstract shouldKeepModule(pythonModule: PythonModule): boolean + abstract shouldKeepModule(pythonModule: PythonModule): boolean; /** * Whether the given class should be kept after filtering. */ - abstract shouldKeepClass(pythonClass: PythonClass): boolean + abstract shouldKeepClass(pythonClass: PythonClass): boolean; /** * Whether the given function should be kept after filtering. */ - abstract shouldKeepFunction(pythonFunction: PythonFunction): boolean + abstract shouldKeepFunction(pythonFunction: PythonFunction): boolean; /** * Whether the given parameter should be kept after filtering. */ - abstract shouldKeepParameter(pythonParameter: PythonParameter): boolean + abstract shouldKeepParameter(pythonParameter: PythonParameter): boolean; /** * Returns whether the package can be returned unchanged after filtering. Use this to improve the performance of @@ -67,7 +66,6 @@ export default abstract class AbstractPythonFilter { * Apply this filter to the given package and create a package with filtered modules. */ applyToPackage(pythonPackage: PythonPackage): PythonPackage { - // If the package is kept, keep the entire subtree if (this.canSkipPackageUpdate()) { return pythonPackage; @@ -80,25 +78,19 @@ export default abstract class AbstractPythonFilter { (it) => this.shouldKeepModule(it) || // Don't exclude empty modules when we only filter modules - (this.canSkipModuleUpdate() || - !isEmptyList(it.classes) || - !isEmptyList(it.functions)), + this.canSkipModuleUpdate() || + !isEmptyList(it.classes) || + !isEmptyList(it.functions), ); // Create filtered package - return new PythonPackage( - pythonPackage.distribution, - pythonPackage.name, - pythonPackage.version, - modules, - ); + return new PythonPackage(pythonPackage.distribution, pythonPackage.name, pythonPackage.version, modules); } /** * Apply this filter to the given module and create a module with filtered classes and functions. */ applyToModule(pythonModule: PythonModule): PythonModule { - // If the module is kept, keep the entire subtree if (this.canSkipModuleUpdate() || this.shouldKeepModule(pythonModule)) { return pythonModule; @@ -111,35 +103,29 @@ export default abstract class AbstractPythonFilter { (it) => this.shouldKeepClass(it) || // Don't exclude empty classes when we only filter modules or classes - (this.canSkipClassUpdate() || - !isEmptyList(it.methods)), + this.canSkipClassUpdate() || + !isEmptyList(it.methods), ); // Filter functions const functions = pythonModule.functions .map((it) => this.applyToFunction(it)) .filter( - (it) => this.shouldKeepFunction(it) || + (it) => + this.shouldKeepFunction(it) || // Don't exclude functions without parameters when we don't filter parameters - (this.canSkipFunctionUpdate() || - !isEmptyList(it.parameters)), + this.canSkipFunctionUpdate() || + !isEmptyList(it.parameters), ); // Create filtered module - return new PythonModule( - pythonModule.name, - pythonModule.imports, - pythonModule.fromImports, - classes, - functions, - ); + return new PythonModule(pythonModule.name, pythonModule.imports, pythonModule.fromImports, classes, functions); } /** * Apply this filter to the given class and create a class with filtered methods. */ applyToClass(pythonClass: PythonClass): PythonClass { - // If the class is kept, keep the entire subtree if (this.canSkipClassUpdate() || this.shouldKeepClass(pythonClass)) { return pythonClass; @@ -148,12 +134,7 @@ export default abstract class AbstractPythonFilter { // Filter methods const methods = pythonClass.methods .map((it) => this.applyToFunction(it)) - .filter( - (it) => - this.shouldKeepFunction(it) || - (this.canSkipFunctionUpdate() || - !isEmptyList(it.parameters)), - ); + .filter((it) => this.shouldKeepFunction(it) || this.canSkipFunctionUpdate() || !isEmptyList(it.parameters)); // Create filtered class return new PythonClass( @@ -172,7 +153,6 @@ export default abstract class AbstractPythonFilter { * Apply this filter to the given function and create a function with filtered parameters. */ applyToFunction(pythonFunction: PythonFunction): PythonFunction { - // If the function is kept, keep the entire subtree if (this.canSkipFunctionUpdate() || this.shouldKeepFunction(pythonFunction)) { return pythonFunction; 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 b26ae2fa7..127975c0c 100644 --- a/api-editor/gui/src/features/packageData/model/filters/ConjunctiveFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/ConjunctiveFilter.ts @@ -1,64 +1,46 @@ -import AbstractPythonFilter from "./AbstractPythonFilter"; -import PythonClass from "../PythonClass"; -import PythonFunction from "../PythonFunction"; -import PythonParameter from "../PythonParameter"; -import PythonModule from "../PythonModule"; +import AbstractPythonFilter from './AbstractPythonFilter'; +import PythonClass from '../PythonClass'; +import PythonFunction from '../PythonFunction'; +import PythonParameter from '../PythonParameter'; +import PythonModule from '../PythonModule'; /** * Keeps declarations iff all contained filters want to keep it. */ export class ConjunctiveFilter extends AbstractPythonFilter { - constructor( - readonly filters: AbstractPythonFilter[] - ) { + constructor(readonly filters: AbstractPythonFilter[]) { super(); } shouldKeepModule(pythonModule: PythonModule): boolean { - return this.filters.every( - (it) => it.shouldKeepModule(pythonModule) - ); + return this.filters.every((it) => it.shouldKeepModule(pythonModule)); } shouldKeepClass(pythonClass: PythonClass): boolean { - return this.filters.every( - (it) => it.shouldKeepClass(pythonClass) - ); + return this.filters.every((it) => it.shouldKeepClass(pythonClass)); } shouldKeepFunction(pythonFunction: PythonFunction): boolean { - return this.filters.every( - (it) => it.shouldKeepFunction(pythonFunction) - ); + return this.filters.every((it) => it.shouldKeepFunction(pythonFunction)); } shouldKeepParameter(pythonParameter: PythonParameter): boolean { - return this.filters.every( - (it) => it.shouldKeepParameter(pythonParameter) - ); + return this.filters.every((it) => it.shouldKeepParameter(pythonParameter)); } canSkipPackageUpdate(): boolean { - return this.filters.every( - (it) => it.canSkipPackageUpdate() - ); + return this.filters.every((it) => it.canSkipPackageUpdate()); } canSkipModuleUpdate(): boolean { - return this.filters.every( - (it) => it.canSkipModuleUpdate() - ); + return this.filters.every((it) => it.canSkipModuleUpdate()); } canSkipClassUpdate(): boolean { - return this.filters.every( - (it) => it.canSkipClassUpdate() - ); + return this.filters.every((it) => it.canSkipClassUpdate()); } canSkipFunctionUpdate(): boolean { - return this.filters.every( - (it) => it.canSkipFunctionUpdate() - ); + return this.filters.every((it) => it.canSkipFunctionUpdate()); } } From 9e009f1de87ff411034462893a3590b08e3a7111 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sat, 21 May 2022 16:24:30 +0200 Subject: [PATCH 10/45] fix: filter logic --- .../model/filters/AbstractPythonFilter.ts | 108 ++++++++++-------- 1 file changed, 60 insertions(+), 48 deletions(-) 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 e43d91c69..e74a2ac01 100644 --- a/api-editor/gui/src/features/packageData/model/filters/AbstractPythonFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/AbstractPythonFilter.ts @@ -1,34 +1,35 @@ import PythonClass from '../PythonClass'; import PythonFunction from '../PythonFunction'; -import PythonModule from '../PythonModule'; -import PythonParameter from '../PythonParameter'; -import PythonPackage from '../PythonPackage'; -import { isEmptyList } from '../../../../common/util/listOperations'; +import PythonModule from "../PythonModule"; +import PythonParameter from "../PythonParameter"; +import PythonPackage from "../PythonPackage"; +import {isEmptyList} from "../../../../common/util/listOperations"; /** * An abstract base class for filters of Python declarations. To create a new filter create a new subclass and override * the shouldKeepXXX methods. To speed up the filter, you can also override the canSkipXXXUpdate methods. */ export default abstract class AbstractPythonFilter { + /** * Whether the given module should be kept after filtering. */ - abstract shouldKeepModule(pythonModule: PythonModule): boolean; + abstract shouldKeepModule(pythonModule: PythonModule): boolean /** * Whether the given class should be kept after filtering. */ - abstract shouldKeepClass(pythonClass: PythonClass): boolean; + abstract shouldKeepClass(pythonClass: PythonClass): boolean /** * Whether the given function should be kept after filtering. */ - abstract shouldKeepFunction(pythonFunction: PythonFunction): boolean; + abstract shouldKeepFunction(pythonFunction: PythonFunction): boolean /** * Whether the given parameter should be kept after filtering. */ - abstract shouldKeepParameter(pythonParameter: PythonParameter): boolean; + abstract shouldKeepParameter(pythonParameter: PythonParameter): boolean /** * Returns whether the package can be returned unchanged after filtering. Use this to improve the performance of @@ -63,9 +64,10 @@ export default abstract class AbstractPythonFilter { } /** - * Apply this filter to the given package and create a package with filtered modules. + * Applies this filter to the given package and creates a package with filtered modules. */ applyToPackage(pythonPackage: PythonPackage): PythonPackage { + // If the package is kept, keep the entire subtree if (this.canSkipPackageUpdate()) { return pythonPackage; @@ -74,23 +76,23 @@ export default abstract class AbstractPythonFilter { // Filter modules const modules = pythonPackage.modules .map((it) => this.applyToModule(it)) - .filter( - (it) => - this.shouldKeepModule(it) || - // Don't exclude empty modules when we only filter modules - this.canSkipModuleUpdate() || - !isEmptyList(it.classes) || - !isEmptyList(it.functions), - ); + .filter((it) => it !== null); // Create filtered package - return new PythonPackage(pythonPackage.distribution, pythonPackage.name, pythonPackage.version, modules); + return new PythonPackage( + pythonPackage.distribution, + pythonPackage.name, + pythonPackage.version, + modules as PythonModule[], + ); } /** - * Apply this filter to the given module and create a module with filtered classes and functions. + * Applies this filter to the given module and creates a module with filtered classes and functions. Returns null if + * the module should be removed. */ - applyToModule(pythonModule: PythonModule): PythonModule { + private applyToModule(pythonModule: PythonModule): PythonModule | null { + // If the module is kept, keep the entire subtree if (this.canSkipModuleUpdate() || this.shouldKeepModule(pythonModule)) { return pythonModule; @@ -99,33 +101,34 @@ export default abstract class AbstractPythonFilter { // Filter classes const classes = pythonModule.classes .map((it) => this.applyToClass(it)) - .filter( - (it) => - this.shouldKeepClass(it) || - // Don't exclude empty classes when we only filter modules or classes - this.canSkipClassUpdate() || - !isEmptyList(it.methods), - ); + .filter((it) => it !== null); // Filter functions const functions = pythonModule.functions .map((it) => this.applyToFunction(it)) - .filter( - (it) => - this.shouldKeepFunction(it) || - // Don't exclude functions without parameters when we don't filter parameters - this.canSkipFunctionUpdate() || - !isEmptyList(it.parameters), - ); - - // Create filtered module - return new PythonModule(pythonModule.name, pythonModule.imports, pythonModule.fromImports, classes, functions); + .filter((it) => it !== null); + + // Return null if all classes and functions are removed + if (isEmptyList(classes) && isEmptyList(functions)) { + return null + } + + // Otherwise, create filtered module + return new PythonModule( + pythonModule.name, + pythonModule.imports, + pythonModule.fromImports, + classes as PythonClass[], + functions as PythonFunction[], + ); } /** - * Apply this filter to the given class and create a class with filtered methods. + * Applies this filter to the given class and creates a class with filtered methods. Returns null if the class + * should be removed. */ - applyToClass(pythonClass: PythonClass): PythonClass { + private applyToClass(pythonClass: PythonClass): PythonClass | null { + // If the class is kept, keep the entire subtree if (this.canSkipClassUpdate() || this.shouldKeepClass(pythonClass)) { return pythonClass; @@ -134,15 +137,20 @@ export default abstract class AbstractPythonFilter { // Filter methods const methods = pythonClass.methods .map((it) => this.applyToFunction(it)) - .filter((it) => this.shouldKeepFunction(it) || this.canSkipFunctionUpdate() || !isEmptyList(it.parameters)); + .filter((it) => it !== null); + + // Return null if all methods are removed + if (isEmptyList(methods)) { + return null + } - // Create filtered class + // Otherwise, create filtered class return new PythonClass( pythonClass.name, pythonClass.qualifiedName, pythonClass.decorators, pythonClass.superclasses, - methods, + methods as PythonFunction[], pythonClass.isPublic, pythonClass.description, pythonClass.fullDocstring, @@ -150,9 +158,11 @@ export default abstract class AbstractPythonFilter { } /** - * Apply this filter to the given function and create a function with filtered parameters. + * Applies this filter to the given function and creates a function with filtered parameters. Returns null if the + * function should be removed. */ - applyToFunction(pythonFunction: PythonFunction): PythonFunction { + private applyToFunction(pythonFunction: PythonFunction): PythonFunction | null { + // If the function is kept, keep the entire subtree if (this.canSkipFunctionUpdate() || this.shouldKeepFunction(pythonFunction)) { return pythonFunction; @@ -163,10 +173,12 @@ export default abstract class AbstractPythonFilter { .map((it) => it.clone()) .filter((it) => this.shouldKeepParameter(it)); - // Filter results - const results = pythonFunction.results.map((it) => it.clone()); + // Return null if all parameters are removed + if (isEmptyList(parameters)) { + return null + } - // Create filtered function + // Otherwise, create filtered function return new PythonFunction( pythonFunction.name, pythonFunction.uniqueName, @@ -174,7 +186,7 @@ export default abstract class AbstractPythonFilter { pythonFunction.uniqueQualifiedName, pythonFunction.decorators, parameters, - results, + pythonFunction.results, pythonFunction.isPublic, pythonFunction.description, pythonFunction.fullDocstring, From 71af8e0702b3ac461d2d0dbeb998c454758b9694 Mon Sep 17 00:00:00 2001 From: lars-reimann Date: Sat, 21 May 2022 14:28:29 +0000 Subject: [PATCH 11/45] style: apply automatic fixes of linters --- .../model/filters/AbstractPythonFilter.ts | 43 +++++++------------ 1 file changed, 15 insertions(+), 28 deletions(-) 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 e74a2ac01..0f65a2d87 100644 --- a/api-editor/gui/src/features/packageData/model/filters/AbstractPythonFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/AbstractPythonFilter.ts @@ -1,35 +1,34 @@ import PythonClass from '../PythonClass'; import PythonFunction from '../PythonFunction'; -import PythonModule from "../PythonModule"; -import PythonParameter from "../PythonParameter"; -import PythonPackage from "../PythonPackage"; -import {isEmptyList} from "../../../../common/util/listOperations"; +import PythonModule from '../PythonModule'; +import PythonParameter from '../PythonParameter'; +import PythonPackage from '../PythonPackage'; +import { isEmptyList } from '../../../../common/util/listOperations'; /** * An abstract base class for filters of Python declarations. To create a new filter create a new subclass and override * the shouldKeepXXX methods. To speed up the filter, you can also override the canSkipXXXUpdate methods. */ export default abstract class AbstractPythonFilter { - /** * Whether the given module should be kept after filtering. */ - abstract shouldKeepModule(pythonModule: PythonModule): boolean + abstract shouldKeepModule(pythonModule: PythonModule): boolean; /** * Whether the given class should be kept after filtering. */ - abstract shouldKeepClass(pythonClass: PythonClass): boolean + abstract shouldKeepClass(pythonClass: PythonClass): boolean; /** * Whether the given function should be kept after filtering. */ - abstract shouldKeepFunction(pythonFunction: PythonFunction): boolean + abstract shouldKeepFunction(pythonFunction: PythonFunction): boolean; /** * Whether the given parameter should be kept after filtering. */ - abstract shouldKeepParameter(pythonParameter: PythonParameter): boolean + abstract shouldKeepParameter(pythonParameter: PythonParameter): boolean; /** * Returns whether the package can be returned unchanged after filtering. Use this to improve the performance of @@ -67,16 +66,13 @@ export default abstract class AbstractPythonFilter { * Applies this filter to the given package and creates a package with filtered modules. */ applyToPackage(pythonPackage: PythonPackage): PythonPackage { - // If the package is kept, keep the entire subtree if (this.canSkipPackageUpdate()) { return pythonPackage; } // Filter modules - const modules = pythonPackage.modules - .map((it) => this.applyToModule(it)) - .filter((it) => it !== null); + const modules = pythonPackage.modules.map((it) => this.applyToModule(it)).filter((it) => it !== null); // Create filtered package return new PythonPackage( @@ -92,25 +88,20 @@ export default abstract class AbstractPythonFilter { * the module should be removed. */ private applyToModule(pythonModule: PythonModule): PythonModule | null { - // If the module is kept, keep the entire subtree if (this.canSkipModuleUpdate() || this.shouldKeepModule(pythonModule)) { return pythonModule; } // Filter classes - const classes = pythonModule.classes - .map((it) => this.applyToClass(it)) - .filter((it) => it !== null); + const classes = pythonModule.classes.map((it) => this.applyToClass(it)).filter((it) => it !== null); // Filter functions - const functions = pythonModule.functions - .map((it) => this.applyToFunction(it)) - .filter((it) => it !== null); + const functions = pythonModule.functions.map((it) => this.applyToFunction(it)).filter((it) => it !== null); // Return null if all classes and functions are removed if (isEmptyList(classes) && isEmptyList(functions)) { - return null + return null; } // Otherwise, create filtered module @@ -128,20 +119,17 @@ export default abstract class AbstractPythonFilter { * should be removed. */ private applyToClass(pythonClass: PythonClass): PythonClass | null { - // If the class is kept, keep the entire subtree if (this.canSkipClassUpdate() || this.shouldKeepClass(pythonClass)) { return pythonClass; } // Filter methods - const methods = pythonClass.methods - .map((it) => this.applyToFunction(it)) - .filter((it) => it !== null); + const methods = pythonClass.methods.map((it) => this.applyToFunction(it)).filter((it) => it !== null); // Return null if all methods are removed if (isEmptyList(methods)) { - return null + return null; } // Otherwise, create filtered class @@ -162,7 +150,6 @@ export default abstract class AbstractPythonFilter { * function should be removed. */ private applyToFunction(pythonFunction: PythonFunction): PythonFunction | null { - // If the function is kept, keep the entire subtree if (this.canSkipFunctionUpdate() || this.shouldKeepFunction(pythonFunction)) { return pythonFunction; @@ -175,7 +162,7 @@ export default abstract class AbstractPythonFilter { // Return null if all parameters are removed if (isEmptyList(parameters)) { - return null + return null; } // Otherwise, create filtered function From acf6eeaab52507707d82e23c6df2be92202a5ce2 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sat, 21 May 2022 20:14:00 +0200 Subject: [PATCH 12/45] fix: remove skip logic (no longer provides better performance) --- .../model/filters/AbstractPythonFilter.ts | 47 +++---------------- .../model/filters/ConjunctiveFilter.ts | 16 ------- .../model/filters/DeclarationTypeFilter.ts | 20 -------- .../packageData/model/filters/NullFilter.ts | 16 ------- 4 files changed, 6 insertions(+), 93 deletions(-) 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 0f65a2d87..f088357b5 100644 --- a/api-editor/gui/src/features/packageData/model/filters/AbstractPythonFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/AbstractPythonFilter.ts @@ -3,11 +3,11 @@ 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'; /** * An abstract base class for filters of Python declarations. To create a new filter create a new subclass and override - * the shouldKeepXXX methods. To speed up the filter, you can also override the canSkipXXXUpdate methods. + * the shouldKeepXXX methods. */ export default abstract class AbstractPythonFilter { /** @@ -30,46 +30,10 @@ export default abstract class AbstractPythonFilter { */ abstract shouldKeepParameter(pythonParameter: PythonParameter): boolean; - /** - * Returns whether the package can be returned unchanged after filtering. Use this to improve the performance of - * the filter but ensure correctness. - */ - canSkipPackageUpdate(): boolean { - return false; - } - - /** - * Returns whether the module can be returned unchanged after filtering. Use this to improve the performance of - * the filter but ensure correctness. - */ - canSkipModuleUpdate(): boolean { - return false; - } - - /** - * Returns whether the class can be returned unchanged after filtering. Use this to improve the performance of - * the filter but ensure correctness. - */ - canSkipClassUpdate(): boolean { - return false; - } - - /** - * Returns whether the function can be returned unchanged after filtering. Use this to improve the performance of - * the filter but ensure correctness. - */ - canSkipFunctionUpdate(): boolean { - return false; - } - /** * Applies this filter to the given package and creates a package with filtered modules. */ applyToPackage(pythonPackage: PythonPackage): PythonPackage { - // If the package is kept, keep the entire subtree - if (this.canSkipPackageUpdate()) { - return pythonPackage; - } // Filter modules const modules = pythonPackage.modules.map((it) => this.applyToModule(it)).filter((it) => it !== null); @@ -89,7 +53,7 @@ export default abstract class AbstractPythonFilter { */ private applyToModule(pythonModule: PythonModule): PythonModule | null { // If the module is kept, keep the entire subtree - if (this.canSkipModuleUpdate() || this.shouldKeepModule(pythonModule)) { + if (this.shouldKeepModule(pythonModule)) { return pythonModule; } @@ -119,8 +83,9 @@ export default abstract class AbstractPythonFilter { * should be removed. */ private applyToClass(pythonClass: PythonClass): PythonClass | null { + // If the class is kept, keep the entire subtree - if (this.canSkipClassUpdate() || this.shouldKeepClass(pythonClass)) { + if (this.shouldKeepClass(pythonClass)) { return pythonClass; } @@ -151,7 +116,7 @@ export default abstract class AbstractPythonFilter { */ private applyToFunction(pythonFunction: PythonFunction): PythonFunction | null { // If the function is kept, keep the entire subtree - if (this.canSkipFunctionUpdate() || this.shouldKeepFunction(pythonFunction)) { + if (this.shouldKeepFunction(pythonFunction)) { return pythonFunction; } 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 127975c0c..812309dc3 100644 --- a/api-editor/gui/src/features/packageData/model/filters/ConjunctiveFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/ConjunctiveFilter.ts @@ -27,20 +27,4 @@ export class ConjunctiveFilter extends AbstractPythonFilter { shouldKeepParameter(pythonParameter: PythonParameter): boolean { return this.filters.every((it) => it.shouldKeepParameter(pythonParameter)); } - - canSkipPackageUpdate(): boolean { - return this.filters.every((it) => it.canSkipPackageUpdate()); - } - - canSkipModuleUpdate(): boolean { - return this.filters.every((it) => it.canSkipModuleUpdate()); - } - - canSkipClassUpdate(): boolean { - return this.filters.every((it) => it.canSkipClassUpdate()); - } - - canSkipFunctionUpdate(): boolean { - return this.filters.every((it) => it.canSkipFunctionUpdate()); - } } 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 a4503c0d2..74303e01a 100644 --- a/api-editor/gui/src/features/packageData/model/filters/DeclarationTypeFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/DeclarationTypeFilter.ts @@ -24,26 +24,6 @@ export default class DeclarationTypeFilter extends AbstractPythonFilter { shouldKeepParameter(pythonParameter: PythonParameter): boolean { return this.type == DeclarationType.Parameter; } - - canSkipPackageUpdate(): boolean { - return this.type == DeclarationType.Module; - } - - canSkipModuleUpdate(): boolean { - return this.type == DeclarationType.Module; - } - - canSkipClassUpdate(): boolean { - return this.type == DeclarationType.Module || this.type == DeclarationType.Class; - } - - canSkipFunctionUpdate(): boolean { - return ( - this.type == DeclarationType.Module || - this.type == DeclarationType.Class || - this.type == DeclarationType.Function - ); - } } export enum DeclarationType { diff --git a/api-editor/gui/src/features/packageData/model/filters/NullFilter.ts b/api-editor/gui/src/features/packageData/model/filters/NullFilter.ts index 4172ec883..bded0f148 100644 --- a/api-editor/gui/src/features/packageData/model/filters/NullFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/NullFilter.ts @@ -23,20 +23,4 @@ export default class NullFilter extends AbstractPythonFilter { shouldKeepParameter(pythonParameter: PythonParameter): boolean { return true; } - - canSkipPackageUpdate(): boolean { - return false; - } - - canSkipModuleUpdate(): boolean { - return false; - } - - canSkipClassUpdate(): boolean { - return false; - } - - canSkipFunctionUpdate(): boolean { - return false; - } } From e0fafe542550110c5365ca9bc38411eb9aa6bf2b Mon Sep 17 00:00:00 2001 From: lars-reimann Date: Sat, 21 May 2022 18:18:02 +0000 Subject: [PATCH 13/45] style: apply automatic fixes of linters --- .../packageData/model/filters/AbstractPythonFilter.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) 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 f088357b5..e256c4818 100644 --- a/api-editor/gui/src/features/packageData/model/filters/AbstractPythonFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/AbstractPythonFilter.ts @@ -3,7 +3,7 @@ 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'; /** * An abstract base class for filters of Python declarations. To create a new filter create a new subclass and override @@ -34,7 +34,6 @@ export default abstract class AbstractPythonFilter { * Applies this filter to the given package and creates a package with filtered modules. */ applyToPackage(pythonPackage: PythonPackage): PythonPackage { - // Filter modules const modules = pythonPackage.modules.map((it) => this.applyToModule(it)).filter((it) => it !== null); @@ -83,7 +82,6 @@ export default abstract class AbstractPythonFilter { * should be removed. */ private applyToClass(pythonClass: PythonClass): PythonClass | null { - // If the class is kept, keep the entire subtree if (this.shouldKeepClass(pythonClass)) { return pythonClass; From 7ac3eba5bbd37e09c754dcdd62f0f5a919d6d5e6 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Mon, 23 May 2022 14:14:21 +0200 Subject: [PATCH 14/45] refactor: remove null filter (same as empty ConjunctiveFilter) --- .../packageData/model/filters/NullFilter.ts | 26 ------------------- 1 file changed, 26 deletions(-) delete mode 100644 api-editor/gui/src/features/packageData/model/filters/NullFilter.ts diff --git a/api-editor/gui/src/features/packageData/model/filters/NullFilter.ts b/api-editor/gui/src/features/packageData/model/filters/NullFilter.ts deleted file mode 100644 index bded0f148..000000000 --- a/api-editor/gui/src/features/packageData/model/filters/NullFilter.ts +++ /dev/null @@ -1,26 +0,0 @@ -import PythonClass from '../PythonClass'; -import PythonFunction from '../PythonFunction'; -import PythonModule from '../PythonModule'; -import PythonParameter from '../PythonParameter'; -import AbstractPythonFilter from './AbstractPythonFilter'; - -/** - * Keeps all declarations. - */ -export default class NullFilter extends AbstractPythonFilter { - shouldKeepModule(pythonModule: PythonModule): boolean { - return true; - } - - shouldKeepClass(pythonClass: PythonClass): boolean { - return true; - } - - shouldKeepFunction(pythonFunction: PythonFunction): boolean { - return true; - } - - shouldKeepParameter(pythonParameter: PythonParameter): boolean { - return true; - } -} From 75751dcbd6bf1b97e3bc27a8f5c215b5f406636f Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Mon, 23 May 2022 14:52:01 +0200 Subject: [PATCH 15/45] refactor: filter factory --- api-editor/gui/src/app/App.tsx | 52 ++++++++------- .../{HasNameFilter.ts => NameFilter.ts} | 12 ++-- .../model/filters/VisibilityFilter.ts | 37 +++++++++++ .../model/filters/filterFactory.ts | 63 ++++++++++--------- .../packageData/treeView/TreeView.tsx | 6 +- 5 files changed, 106 insertions(+), 64 deletions(-) rename api-editor/gui/src/features/packageData/model/filters/{HasNameFilter.ts => NameFilter.ts} (66%) create mode 100644 api-editor/gui/src/features/packageData/model/filters/VisibilityFilter.ts diff --git a/api-editor/gui/src/app/App.tsx b/api-editor/gui/src/app/App.tsx index da6748e72..f61d3bb1f 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,21 +33,18 @@ 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, - toggleIsExpandedInTreeView, -} from '../features/packageData/packageDataSlice'; +import {selectShowPackageDataImportDialog, toggleIsExpandedInTreeView,} 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(); @@ -137,18 +134,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 === 'none' && } + {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/HasNameFilter.ts b/api-editor/gui/src/features/packageData/model/filters/NameFilter.ts similarity index 66% rename from api-editor/gui/src/features/packageData/model/filters/HasNameFilter.ts rename to api-editor/gui/src/features/packageData/model/filters/NameFilter.ts index 8c7eef19b..3c9daa0ea 100644 --- a/api-editor/gui/src/features/packageData/model/filters/HasNameFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/NameFilter.ts @@ -5,28 +5,28 @@ import PythonParameter from '../PythonParameter'; import AbstractPythonFilter from './AbstractPythonFilter'; import PythonDeclaration from '../PythonDeclaration'; -export default class HasNameFilter extends AbstractPythonFilter { +export default class NameFilter extends AbstractPythonFilter { constructor(readonly name: string) { super(); } shouldKeepModule(pythonModule: PythonModule): boolean { - return this.filter(pythonModule); + return this.shouldKeepDeclaration(pythonModule); } shouldKeepClass(pythonClass: PythonClass): boolean { - return this.filter(pythonClass); + return this.shouldKeepDeclaration(pythonClass); } shouldKeepFunction(pythonFunction: PythonFunction): boolean { - return this.filter(pythonFunction); + return this.shouldKeepDeclaration(pythonFunction); } shouldKeepParameter(pythonParameter: PythonParameter): boolean { - return this.filter(pythonParameter); + return this.shouldKeepDeclaration(pythonParameter); } - private filter(declaration: PythonDeclaration): boolean { + private shouldKeepDeclaration(declaration: PythonDeclaration): boolean { return declaration.name.toLowerCase().includes(this.name.toLowerCase()); } } diff --git a/api-editor/gui/src/features/packageData/model/filters/VisibilityFilter.ts b/api-editor/gui/src/features/packageData/model/filters/VisibilityFilter.ts new file mode 100644 index 000000000..c05b6fb16 --- /dev/null +++ b/api-editor/gui/src/features/packageData/model/filters/VisibilityFilter.ts @@ -0,0 +1,37 @@ +import PythonClass from '../PythonClass'; +import PythonFunction from '../PythonFunction'; +import PythonModule from '../PythonModule'; +import PythonParameter from '../PythonParameter'; +import AbstractPythonFilter from './AbstractPythonFilter'; +import PythonDeclaration from '../PythonDeclaration'; + +export default class VisibilityFilter extends AbstractPythonFilter { + constructor(readonly visibility: Visibility) { + super(); + } + + shouldKeepModule(pythonModule: PythonModule): boolean { + return this.shouldKeepDeclaration(pythonModule); + } + + shouldKeepClass(pythonClass: PythonClass): boolean { + return this.shouldKeepDeclaration(pythonClass); + } + + shouldKeepFunction(pythonFunction: PythonFunction): boolean { + return this.shouldKeepDeclaration(pythonFunction); + } + + shouldKeepParameter(pythonParameter: PythonParameter): boolean { + return this.shouldKeepDeclaration(pythonParameter); + } + + private shouldKeepDeclaration(declaration: PythonDeclaration): boolean { + return declaration.isPublicDeclaration() == (this.visibility == Visibility.Public) + } +} + +export enum Visibility { + Public, + Internal +} 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 6df6ae8b2..26246ec11 100644 --- a/api-editor/gui/src/features/packageData/model/filters/filterFactory.ts +++ b/api-editor/gui/src/features/packageData/model/filters/filterFactory.ts @@ -1,39 +1,44 @@ -import { ConjunctiveFilter } from './ConjunctiveFilter'; -import HasNameFilter from './HasNameFilter'; +import {ConjunctiveFilter} from './ConjunctiveFilter'; +import NameFilter from './NameFilter'; import AbstractPythonFilter from './AbstractPythonFilter'; -import DeclarationTypeFilter, { DeclarationType } from './DeclarationTypeFilter'; +import DeclarationTypeFilter, {DeclarationType} from './DeclarationTypeFilter'; +import VisibilityFilter, {Visibility} from "./VisibilityFilter"; export function createFilterFromString(filterBoxInput: string): AbstractPythonFilter { const filters = []; - for (const match of filterBoxInput.matchAll(/(\w+):([^\s:]+)/gu)) { - if (match.length === 3) { - const [, scope, filterString] = match; + for (const token of filterBoxInput.split(/\s+/)) { + switch (token) { - switch (scope) { - case 'hasName': - filters.push(new HasNameFilter(filterString)); - break; - case 'is': - switch (filterString) { - case 'module': - filters.push(new DeclarationTypeFilter(DeclarationType.Module)); - break; - case 'class': - filters.push(new DeclarationTypeFilter(DeclarationType.Class)); - break; - case 'function': - filters.push(new DeclarationTypeFilter(DeclarationType.Function)); - break; - case 'parameter': - filters.push(new DeclarationTypeFilter(DeclarationType.Parameter)); - break; - // no default - } + // Declaration type + case 'is:module': + filters.push(new DeclarationTypeFilter(DeclarationType.Module)); + continue; + case 'is:class': + filters.push(new DeclarationTypeFilter(DeclarationType.Class)); + continue; + case 'is:function': + filters.push(new DeclarationTypeFilter(DeclarationType.Function)); + continue; + case 'is:parameter': + filters.push(new DeclarationTypeFilter(DeclarationType.Parameter)); + continue; - break; - // no default - } + // Visibility + case 'is:public': + filters.push(new VisibilityFilter(Visibility.Public)) + continue + case 'is:internal': + filters.push(new VisibilityFilter(Visibility.Internal)) + continue + } + + // Name + const nameMatch = /^name:(?\w+)$/.exec(token) + if (nameMatch) { + filters.push(new NameFilter(nameMatch?.groups?.name as string)) + // noinspection UnnecessaryContinueJS + continue } } diff --git a/api-editor/gui/src/features/packageData/treeView/TreeView.tsx b/api-editor/gui/src/features/packageData/treeView/TreeView.tsx index 237bb384b..97e718f64 100644 --- a/api-editor/gui/src/features/packageData/treeView/TreeView.tsx +++ b/api-editor/gui/src/features/packageData/treeView/TreeView.tsx @@ -19,6 +19,7 @@ import ClassNode from './ClassNode'; import FunctionNode from './FunctionNode'; import ModuleNode from './ModuleNode'; import ParameterNode from './ParameterNode'; +import AbstractPythonFilter from "../model/filters/AbstractPythonFilter"; interface ScrollOffset { scrollOffset: number; @@ -26,9 +27,10 @@ interface ScrollOffset { interface TreeViewProps { pythonPackage: PythonPackage; + filter: AbstractPythonFilter; } -const TreeView: React.FC = memo(({ pythonPackage }) => { +const TreeView: React.FC = ({ pythonPackage, filter }) => { const dispatch = useAppDispatch(); const allExpanded = useAppSelector(selectAllExpandedInTreeView); @@ -87,7 +89,7 @@ const TreeView: React.FC = memo(({ pythonPackage }) => { )} ); -}); +}; const walkChildrenInPreorder = function ( allExpandedItemsInTreeView: { [target: string]: true }, From f8cf2139c452391a68cfce3af253f14757f29a2e Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Mon, 23 May 2022 14:55:10 +0200 Subject: [PATCH 16/45] fix: popover warning --- api-editor/gui/src/common/MenuBar.tsx | 108 +++++++++++++------------- 1 file changed, 55 insertions(+), 53 deletions(-) diff --git a/api-editor/gui/src/common/MenuBar.tsx b/api-editor/gui/src/common/MenuBar.tsx index 3504dcac7..c8269a5e1 100644 --- a/api-editor/gui/src/common/MenuBar.tsx +++ b/api-editor/gui/src/common/MenuBar.tsx @@ -59,59 +59,61 @@ interface MenuBarProps { const HelpButton = function () { return ( - - - } aria-label="help" /> - - - - - Filter Options - - - - - is:xy - - - Displays only elements that are of the given type xy. Possible types are: module, class, - function, parameter. - - - - - hasName:xy - - Displays only elements with names that contain the given string xy. - - - - is:annotated - - Displays only elements that have been annotated. - - - - hasAnnotation:xy - - - Displays only elements that are annotated with the given type xy. Possible types: - unused, constant, required, optional, enum and boundary. - - - - - !filter - - - Displays only elements that do not match the given filter. Possible filters are any in - this list. - - - - - - + + + + } aria-label="help" /> + + + + + Filter Options + + + + + is:xy + + + Displays only elements that are of the given type xy. Possible types are: module, class, + function, parameter. + + + + + hasName:xy + + Displays only elements with names that contain the given string xy. + + + + is:annotated + + Displays only elements that have been annotated. + + + + hasAnnotation:xy + + + Displays only elements that are annotated with the given type xy. Possible types: + unused, constant, required, optional, enum and boundary. + + + + + !filter + + + Displays only elements that do not match the given filter. Possible filters are any in + this list. + + + + + + + ); }; From fb866835bfa375cdc46b7f2705644c6640ea543f Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Mon, 23 May 2022 15:18:33 +0200 Subject: [PATCH 17/45] feat: negated filters --- .../model/filters/NegatedFilter.ts | 30 +++++++ .../model/filters/filterFactory.ts | 83 +++++++++++-------- 2 files changed, 79 insertions(+), 34 deletions(-) create mode 100644 api-editor/gui/src/features/packageData/model/filters/NegatedFilter.ts diff --git a/api-editor/gui/src/features/packageData/model/filters/NegatedFilter.ts b/api-editor/gui/src/features/packageData/model/filters/NegatedFilter.ts new file mode 100644 index 000000000..628ed83de --- /dev/null +++ b/api-editor/gui/src/features/packageData/model/filters/NegatedFilter.ts @@ -0,0 +1,30 @@ +import AbstractPythonFilter from './AbstractPythonFilter'; +import PythonClass from '../PythonClass'; +import PythonFunction from '../PythonFunction'; +import PythonParameter from '../PythonParameter'; +import PythonModule from '../PythonModule'; + +/** + * Keeps declarations iff the contained filter wants to remove it. + */ +export class NegatedFilter extends AbstractPythonFilter { + constructor(readonly filter: AbstractPythonFilter) { + super(); + } + + shouldKeepModule(pythonModule: PythonModule): boolean { + return !this.filter.shouldKeepModule(pythonModule); + } + + shouldKeepClass(pythonClass: PythonClass): boolean { + return !this.filter.shouldKeepClass(pythonClass); + } + + shouldKeepFunction(pythonFunction: PythonFunction): boolean { + return !this.filter.shouldKeepFunction(pythonFunction); + } + + shouldKeepParameter(pythonParameter: PythonParameter): boolean { + return !this.filter.shouldKeepParameter(pythonParameter); + } +} 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 26246ec11..21f12c91c 100644 --- a/api-editor/gui/src/features/packageData/model/filters/filterFactory.ts +++ b/api-editor/gui/src/features/packageData/model/filters/filterFactory.ts @@ -3,44 +3,59 @@ 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"; -export function createFilterFromString(filterBoxInput: string): AbstractPythonFilter { - const filters = []; - - for (const token of filterBoxInput.split(/\s+/)) { - switch (token) { - - // Declaration type - case 'is:module': - filters.push(new DeclarationTypeFilter(DeclarationType.Module)); - continue; - case 'is:class': - filters.push(new DeclarationTypeFilter(DeclarationType.Class)); - continue; - case 'is:function': - filters.push(new DeclarationTypeFilter(DeclarationType.Function)); - continue; - case 'is:parameter': - filters.push(new DeclarationTypeFilter(DeclarationType.Parameter)); - continue; - - // Visibility - case 'is:public': - filters.push(new VisibilityFilter(Visibility.Public)) - continue - case 'is:internal': - filters.push(new VisibilityFilter(Visibility.Internal)) - continue - } +export function createFilterFromString(text: string): AbstractPythonFilter { + const filters: AbstractPythonFilter[] = []; - // Name - const nameMatch = /^name:(?\w+)$/.exec(token) - if (nameMatch) { - filters.push(new NameFilter(nameMatch?.groups?.name as string)) - // noinspection UnnecessaryContinueJS - continue + for (const token of text.split(/\s+/)) { + const newFilter = parsePotentiallyNegatedToken(token); + if (newFilter) { + filters.push(newFilter) } } return new ConjunctiveFilter(filters); } + +function parsePotentiallyNegatedToken(token: string): Optional { + const isNegated = token.startsWith('!') + const positiveToken = isNegated ? token.substring(1) : token + + const newPositiveFilter = parsePositiveToken(positiveToken) + if (!newPositiveFilter || !isNegated) { + return newPositiveFilter + } else { + return new NegatedFilter(newPositiveFilter) + } +} + +function parsePositiveToken(token: string): Optional { + + // Filters with fixed text + switch (token) { + + // Declaration type + case 'is:module': + return new DeclarationTypeFilter(DeclarationType.Module); + case 'is:class': + return new DeclarationTypeFilter(DeclarationType.Class); + case 'is:function': + return new DeclarationTypeFilter(DeclarationType.Function); + case 'is:parameter': + return new DeclarationTypeFilter(DeclarationType.Parameter); + + // Visibility + case 'is:public': + return new VisibilityFilter(Visibility.Public) + case 'is:internal': + return new VisibilityFilter(Visibility.Internal) + } + + // Name + const nameMatch = /^name:(?\w+)$/.exec(token) + if (nameMatch) { + return new NameFilter(nameMatch?.groups?.name as string) + } +} From c322fdd44bcde99c3ee3ddbd94cc9340c3a717e6 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Mon, 23 May 2022 15:24:43 +0200 Subject: [PATCH 18/45] refactor: remove setting "show private declarations" --- api-editor/gui/src/app/App.tsx | 2 +- api-editor/gui/src/common/MenuBar.tsx | 63 ++++++++----------- .../features/packageData/packageDataSlice.ts | 8 --- .../packageData/treeView/ClassNode.tsx | 12 +--- .../packageData/treeView/FunctionNode.tsx | 12 +--- .../packageData/treeView/ModuleNode.tsx | 18 ++---- .../packageData/treeView/TreeView.tsx | 18 ++---- 7 files changed, 42 insertions(+), 91 deletions(-) diff --git a/api-editor/gui/src/app/App.tsx b/api-editor/gui/src/app/App.tsx index f61d3bb1f..fbd0633d9 100644 --- a/api-editor/gui/src/app/App.tsx +++ b/api-editor/gui/src/app/App.tsx @@ -89,7 +89,7 @@ const App: React.FC = function () { // eslint-disable-next-line }, []); - const [filter, setFilter] = useState(''); + const [filter, setFilter] = useState('is:public'); const pythonFilter = createFilterFromString(filter); const filteredPythonPackage = pythonFilter.applyToPackage(pythonPackage); diff --git a/api-editor/gui/src/common/MenuBar.tsx b/api-editor/gui/src/common/MenuBar.tsx index c8269a5e1..e7cb4785a 100644 --- a/api-editor/gui/src/common/MenuBar.tsx +++ b/api-editor/gui/src/common/MenuBar.tsx @@ -36,19 +36,15 @@ import { 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 { - selectShowPrivateDeclarations, - togglePackageDataImportDialog, - toggleShowPrivateDeclarations, -} 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'; interface MenuBarProps { pythonPackage: PythonPackage; @@ -62,11 +58,11 @@ const HelpButton = function () { - } aria-label="help" /> + } aria-label="help"/> - - + + Filter Options @@ -75,7 +71,8 @@ const HelpButton = function () { is:xy - Displays only elements that are of the given type xy. Possible types are: module, class, + Displays only elements that are of the given type xy. Possible types are: module, + class, function, parameter. @@ -83,7 +80,8 @@ const HelpButton = function () { hasName:xy - Displays only elements with names that contain the given string xy. + Displays only elements with names that contain the given string + xy. @@ -105,7 +103,8 @@ const HelpButton = function () { !filter - Displays only elements that do not match the given filter. Possible filters are any in + Displays only elements that do not match the given filter. Possible filters are any + in this list. @@ -168,8 +167,8 @@ const DeleteAllAnnotations = function () { ); }; -const MenuBar: React.FC = function ({ pythonPackage, filter, setFilter, displayInferErrors }) { - const { colorMode, toggleColorMode } = useColorMode(); +const MenuBar: React.FC = function ({pythonPackage, filter, setFilter, displayInferErrors}) { + const {colorMode, toggleColorMode} = useColorMode(); const initialFocusRef = useRef(null); const dispatch = useAppDispatch(); @@ -191,7 +190,7 @@ const MenuBar: React.FC = function ({ pythonPackage, filter, setFi const requestOptions = { method: 'POST', - headers: { 'Content-Type': 'application/json' }, + headers: {'Content-Type': 'application/json'}, body: JSON.stringify(annotatedPythonPackage), }; fetch('/api-editor/infer', requestOptions).then(async (response) => { @@ -209,9 +208,6 @@ const MenuBar: React.FC = function ({ pythonPackage, filter, setFi }; const settings: string[] = []; - if (useAppSelector(selectShowPrivateDeclarations)) { - settings.push('showPrivateDeclarations'); - } if (colorMode == 'dark') { settings.push('darkMode'); } @@ -222,7 +218,7 @@ const MenuBar: React.FC = function ({ pythonPackage, filter, setFi {/* Box gets rid of popper.js warning "CSS margin styles cannot be used" */} - }> + }> File @@ -233,7 +229,7 @@ const MenuBar: React.FC = function ({ pythonPackage, filter, setFi Annotations - + Annotations @@ -242,22 +238,15 @@ const MenuBar: React.FC = function ({ pythonPackage, filter, setFi - + - }> + }> Settings - dispatch(toggleShowPrivateDeclarations())} - > - Show private declarations - - Dark mode @@ -267,7 +256,7 @@ const MenuBar: React.FC = function ({ pythonPackage, filter, setFi - + @@ -302,12 +291,12 @@ const MenuBar: React.FC = function ({ pythonPackage, filter, setFi - + Each scope must only be used once. - + ); diff --git a/api-editor/gui/src/features/packageData/packageDataSlice.ts b/api-editor/gui/src/features/packageData/packageDataSlice.ts index 008ecacd9..0c1684961 100644 --- a/api-editor/gui/src/features/packageData/packageDataSlice.ts +++ b/api-editor/gui/src/features/packageData/packageDataSlice.ts @@ -7,7 +7,6 @@ export interface PackageDataState { }; treeViewScrollOffset: number; showImportDialog: boolean; - showPrivateDeclarations: boolean; } // Initial state ------------------------------------------------------------------------------------------------------- @@ -16,7 +15,6 @@ const initialState: PackageDataState = { expandedInTreeView: {}, treeViewScrollOffset: 0, showImportDialog: false, - showPrivateDeclarations: false, }; // Slice --------------------------------------------------------------------------------------------------------------- @@ -38,9 +36,6 @@ const packageDataSlice = createSlice({ toggleImportDialog(state) { state.showImportDialog = !state.showImportDialog; }, - toggleShowPrivateDeclarations(state) { - state.showPrivateDeclarations = !state.showPrivateDeclarations; - }, }, }); @@ -49,7 +44,6 @@ export const { toggleIsExpanded: toggleIsExpandedInTreeView, setScrollOffset: setTreeViewScrollOffset, toggleImportDialog: togglePackageDataImportDialog, - toggleShowPrivateDeclarations, } = actions; export default reducer; @@ -65,5 +59,3 @@ export const selectTreeViewScrollOffset = (state: RootState): number => selectPackageData(state).treeViewScrollOffset; export const selectShowPackageDataImportDialog = (state: RootState): boolean => selectPackageData(state).showImportDialog; -export const selectShowPrivateDeclarations = (state: RootState): boolean => - selectPackageData(state).showPrivateDeclarations; diff --git a/api-editor/gui/src/features/packageData/treeView/ClassNode.tsx b/api-editor/gui/src/features/packageData/treeView/ClassNode.tsx index 9198ded17..9e7745099 100644 --- a/api-editor/gui/src/features/packageData/treeView/ClassNode.tsx +++ b/api-editor/gui/src/features/packageData/treeView/ClassNode.tsx @@ -1,21 +1,15 @@ import React from 'react'; -import { FaChalkboard } from 'react-icons/fa'; -import { isEmptyList } from '../../../common/util/listOperations'; +import {FaChalkboard} from 'react-icons/fa'; +import {isEmptyList} from '../../../common/util/listOperations'; import PythonClass from '../model/PythonClass'; import TreeNode from './TreeNode'; -import { useAppSelector } from '../../../app/hooks'; -import { selectShowPrivateDeclarations } from '../packageDataSlice'; interface ClassNodeProps { pythonClass: PythonClass; } const ClassNode: React.FC = function ({ pythonClass }) { - let methods = pythonClass.methods; - if (!useAppSelector(selectShowPrivateDeclarations)) { - methods = methods.filter((it) => it.isPublic); - } - const hasMethods = !isEmptyList(methods); + const hasMethods = !isEmptyList(pythonClass.methods); return ( = function ({ pythonFunction, }) { - let parameters = pythonFunction.parameters; - if (!useAppSelector(selectShowPrivateDeclarations)) { - parameters = parameters.filter((it) => it.isPublic); - } - const hasParameters = !isEmptyList(parameters); + const hasParameters = !isEmptyList(pythonFunction.parameters); return ( = function ({ pythonModule }) { - let classes = pythonModule.classes; - if (!useAppSelector(selectShowPrivateDeclarations)) { - classes = classes.filter((it) => it.isPublic); - } - const hasClasses = !isEmptyList(classes); - let functions = pythonModule.functions; - if (!useAppSelector(selectShowPrivateDeclarations)) { - functions = functions.filter((it) => it.isPublic); - } - const hasFunctions = !isEmptyList(functions); + const hasClasses = !isEmptyList(pythonModule.classes); + const hasFunctions = !isEmptyList(pythonModule.functions); const hasChildren = hasClasses || hasFunctions; return ( diff --git a/api-editor/gui/src/features/packageData/treeView/TreeView.tsx b/api-editor/gui/src/features/packageData/treeView/TreeView.tsx index 97e718f64..39605ffdd 100644 --- a/api-editor/gui/src/features/packageData/treeView/TreeView.tsx +++ b/api-editor/gui/src/features/packageData/treeView/TreeView.tsx @@ -1,20 +1,15 @@ -import { Box } from '@chakra-ui/react'; -import React, { memo, useCallback, useEffect, useRef } from 'react'; +import {Box} from '@chakra-ui/react'; +import React, {memo, useCallback, useEffect, useRef} from 'react'; import AutoSizer from 'react-virtualized-auto-sizer'; -import { FixedSizeList, ListChildComponentProps } from 'react-window'; -import { useAppDispatch, useAppSelector } from '../../../app/hooks'; +import {FixedSizeList, ListChildComponentProps} from 'react-window'; +import {useAppDispatch, useAppSelector} from '../../../app/hooks'; import PythonClass from '../model/PythonClass'; import PythonDeclaration from '../model/PythonDeclaration'; import PythonFunction from '../model/PythonFunction'; import PythonModule from '../model/PythonModule'; import PythonPackage from '../model/PythonPackage'; import PythonParameter from '../model/PythonParameter'; -import { - selectAllExpandedInTreeView, - selectShowPrivateDeclarations, - selectTreeViewScrollOffset, - setTreeViewScrollOffset, -} from '../packageDataSlice'; +import {selectAllExpandedInTreeView, selectTreeViewScrollOffset, setTreeViewScrollOffset,} from '../packageDataSlice'; import ClassNode from './ClassNode'; import FunctionNode from './FunctionNode'; import ModuleNode from './ModuleNode'; @@ -35,9 +30,6 @@ const TreeView: React.FC = ({ pythonPackage, filter }) => { const allExpanded = useAppSelector(selectAllExpandedInTreeView); let children = walkChildrenInPreorder(allExpanded, pythonPackage); - if (!useAppSelector(selectShowPrivateDeclarations)) { - children = children.filter((it) => it.isPublicDeclaration()); - } const previousScrollOffset = useAppSelector(selectTreeViewScrollOffset); // Keep a reference to the last FixedSizeList before everything is dismounted From a52be1b780171b19d56849b31515327cf03f89e5 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Mon, 23 May 2022 15:30:08 +0200 Subject: [PATCH 19/45] fix: help text for "name" filter --- api-editor/gui/src/common/MenuBar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api-editor/gui/src/common/MenuBar.tsx b/api-editor/gui/src/common/MenuBar.tsx index e7cb4785a..a29655e79 100644 --- a/api-editor/gui/src/common/MenuBar.tsx +++ b/api-editor/gui/src/common/MenuBar.tsx @@ -78,7 +78,7 @@ const HelpButton = function () { - hasName:xy + name:xy Displays only elements with names that contain the given string xy. From 6a47fc5c42302666500e99a1b71954f2e92c4314 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Mon, 23 May 2022 15:35:04 +0200 Subject: [PATCH 20/45] feat: improved help text --- api-editor/gui/src/common/MenuBar.tsx | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/api-editor/gui/src/common/MenuBar.tsx b/api-editor/gui/src/common/MenuBar.tsx index a29655e79..a354cd9e4 100644 --- a/api-editor/gui/src/common/MenuBar.tsx +++ b/api-editor/gui/src/common/MenuBar.tsx @@ -68,12 +68,20 @@ const HelpButton = function () { - is:xy + is:[type] - Displays only elements that are of the given type xy. Possible types are: module, - class, - function, parameter. + 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. From 59f5b97208a4305d2e2787117f1c81395b970875 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Mon, 23 May 2022 15:50:20 +0200 Subject: [PATCH 21/45] feat: boldface for filtered elements --- .../model/filters/AbstractPythonFilter.ts | 21 ++++++++++++++++++- .../packageData/treeView/ClassNode.tsx | 5 ++++- .../packageData/treeView/FunctionNode.tsx | 4 ++++ .../packageData/treeView/ModuleNode.tsx | 5 ++++- .../packageData/treeView/ParameterNode.tsx | 4 ++++ .../packageData/treeView/TreeNode.tsx | 8 ++++++- .../packageData/treeView/TreeView.tsx | 15 ++++++------- 7 files changed, 51 insertions(+), 11 deletions(-) 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 e256c4818..b4031ea94 100644 --- a/api-editor/gui/src/features/packageData/model/filters/AbstractPythonFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/AbstractPythonFilter.ts @@ -4,6 +4,7 @@ import PythonModule from '../PythonModule'; import PythonParameter from '../PythonParameter'; import PythonPackage from '../PythonPackage'; import { isEmptyList } from '../../../../common/util/listOperations'; +import PythonDeclaration from "../PythonDeclaration"; /** * An abstract base class for filters of Python declarations. To create a new filter create a new subclass and override @@ -31,7 +32,25 @@ export default abstract class AbstractPythonFilter { abstract shouldKeepParameter(pythonParameter: PythonParameter): boolean; /** - * Applies this filter to the given package and creates a package with filtered modules. + * Whether the given declaration should be kept after filtering. This function should not be overridden. + */ + shouldKeepDeclaration(pythonDeclaration: PythonDeclaration): boolean { + if (pythonDeclaration instanceof PythonModule) { + return this.shouldKeepModule(pythonDeclaration) + } else if (pythonDeclaration instanceof PythonClass) { + return this.shouldKeepClass(pythonDeclaration) + } else if (pythonDeclaration instanceof PythonFunction) { + return this.shouldKeepFunction(pythonDeclaration) + } else if (pythonDeclaration instanceof PythonParameter) { + return this.shouldKeepParameter(pythonDeclaration) + } else { + return true + } + } + + /** + * Applies this filter to the given package and creates a package with filtered modules. This function should not be + * overridden. */ applyToPackage(pythonPackage: PythonPackage): PythonPackage { // Filter modules diff --git a/api-editor/gui/src/features/packageData/treeView/ClassNode.tsx b/api-editor/gui/src/features/packageData/treeView/ClassNode.tsx index 9e7745099..387509d03 100644 --- a/api-editor/gui/src/features/packageData/treeView/ClassNode.tsx +++ b/api-editor/gui/src/features/packageData/treeView/ClassNode.tsx @@ -3,12 +3,14 @@ import {FaChalkboard} from 'react-icons/fa'; import {isEmptyList} from '../../../common/util/listOperations'; import PythonClass from '../model/PythonClass'; import TreeNode from './TreeNode'; +import AbstractPythonFilter from "../model/filters/AbstractPythonFilter"; interface ClassNodeProps { pythonClass: PythonClass; + filter: AbstractPythonFilter; } -const ClassNode: React.FC = function ({ pythonClass }) { +const ClassNode: React.FC = function ({ pythonClass, filter }) { const hasMethods = !isEmptyList(pythonClass.methods); return ( @@ -16,6 +18,7 @@ const ClassNode: React.FC = function ({ pythonClass }) { declaration={pythonClass} icon={FaChalkboard} isExpandable={hasMethods} + filter={filter} /> ); }; diff --git a/api-editor/gui/src/features/packageData/treeView/FunctionNode.tsx b/api-editor/gui/src/features/packageData/treeView/FunctionNode.tsx index 829edbba8..422bd77ad 100644 --- a/api-editor/gui/src/features/packageData/treeView/FunctionNode.tsx +++ b/api-editor/gui/src/features/packageData/treeView/FunctionNode.tsx @@ -3,13 +3,16 @@ import {FaCogs} from 'react-icons/fa'; import {isEmptyList} from '../../../common/util/listOperations'; import PythonFunction from '../model/PythonFunction'; import TreeNode from './TreeNode'; +import AbstractPythonFilter from "../model/filters/AbstractPythonFilter"; interface FunctionNodeProps { pythonFunction: PythonFunction; + filter: AbstractPythonFilter; } const FunctionNode: React.FC = function ({ pythonFunction, + filter }) { const hasParameters = !isEmptyList(pythonFunction.parameters); @@ -18,6 +21,7 @@ const FunctionNode: React.FC = function ({ declaration={pythonFunction} icon={FaCogs} isExpandable={hasParameters} + filter={filter} /> ); }; diff --git a/api-editor/gui/src/features/packageData/treeView/ModuleNode.tsx b/api-editor/gui/src/features/packageData/treeView/ModuleNode.tsx index 6e0a73102..45763d804 100644 --- a/api-editor/gui/src/features/packageData/treeView/ModuleNode.tsx +++ b/api-editor/gui/src/features/packageData/treeView/ModuleNode.tsx @@ -3,12 +3,14 @@ import {FaArchive} from 'react-icons/fa'; import {isEmptyList} from '../../../common/util/listOperations'; import PythonModule from '../model/PythonModule'; import TreeNode from './TreeNode'; +import AbstractPythonFilter from "../model/filters/AbstractPythonFilter"; interface ModuleNodeProps { pythonModule: PythonModule; + filter: AbstractPythonFilter; } -const ModuleNode: React.FC = function ({ pythonModule }) { +const ModuleNode: React.FC = function ({ pythonModule, filter }) { const hasClasses = !isEmptyList(pythonModule.classes); const hasFunctions = !isEmptyList(pythonModule.functions); const hasChildren = hasClasses || hasFunctions; @@ -18,6 +20,7 @@ const ModuleNode: React.FC = function ({ pythonModule }) { declaration={pythonModule} icon={FaArchive} isExpandable={hasChildren} + filter={filter} /> ); }; diff --git a/api-editor/gui/src/features/packageData/treeView/ParameterNode.tsx b/api-editor/gui/src/features/packageData/treeView/ParameterNode.tsx index 563ac94c1..398ec491f 100644 --- a/api-editor/gui/src/features/packageData/treeView/ParameterNode.tsx +++ b/api-editor/gui/src/features/packageData/treeView/ParameterNode.tsx @@ -2,19 +2,23 @@ import React from 'react'; import { FaKeyboard } from 'react-icons/fa'; import PythonParameter from '../model/PythonParameter'; import TreeNode from './TreeNode'; +import AbstractPythonFilter from "../model/filters/AbstractPythonFilter"; interface ParameterNodeProps { pythonParameter: PythonParameter; + filter: AbstractPythonFilter; } const ParameterNode: React.FC = function ({ pythonParameter, + filter }) { return ( ); }; diff --git a/api-editor/gui/src/features/packageData/treeView/TreeNode.tsx b/api-editor/gui/src/features/packageData/treeView/TreeNode.tsx index 53c8b981c..39491405e 100644 --- a/api-editor/gui/src/features/packageData/treeView/TreeNode.tsx +++ b/api-editor/gui/src/features/packageData/treeView/TreeNode.tsx @@ -10,17 +10,20 @@ import { toggleIsExpandedInTreeView, } from '../packageDataSlice'; import VisibilityIndicator from './VisibilityIndicator'; +import AbstractPythonFilter from "../model/filters/AbstractPythonFilter"; interface TreeNodeProps { declaration: PythonDeclaration; icon: IconType; isExpandable: boolean; + filter: AbstractPythonFilter; } const TreeNode: React.FC = function ({ declaration, icon, isExpandable, + filter }) { const currentPathname = useLocation().pathname; const navigate = useNavigate(); @@ -38,6 +41,9 @@ const TreeNode: React.FC = function ({ const color = isSelected(declaration, currentPathname) ? 'white' : undefined; + const fontWeight = filter.shouldKeepDeclaration(declaration) + ? 'bold' + : undefined; const handleClick = () => { dispatch(toggleIsExpandedInTreeView(declaration.pathAsString())); @@ -59,7 +65,7 @@ const TreeNode: React.FC = function ({ isSelected={isSelected(declaration, currentPathname)} /> - {declaration.getUniqueName()} + {declaration.getUniqueName()} ); }; diff --git a/api-editor/gui/src/features/packageData/treeView/TreeView.tsx b/api-editor/gui/src/features/packageData/treeView/TreeView.tsx index 39605ffdd..d081f402c 100644 --- a/api-editor/gui/src/features/packageData/treeView/TreeView.tsx +++ b/api-editor/gui/src/features/packageData/treeView/TreeView.tsx @@ -65,8 +65,8 @@ const TreeView: React.FC = ({ pythonPackage, filter }) => { data[index]?.pathAsString()} + itemData={{children, filter}} + itemKey={(index, data) => data.children[index]?.pathAsString()} width="100%" height={height} style={{ @@ -101,21 +101,22 @@ const walkChildrenInPreorder = function ( const TreeNodeGenerator: React.FC = memo( ({ data, index, style }) => { - const declaration = data[index]; + const declaration = data.children[index]; + const filter = data.filter; return ( {declaration instanceof PythonModule && ( - + )} {declaration instanceof PythonClass && ( - + )} {declaration instanceof PythonFunction && ( - + )} {declaration instanceof PythonParameter && ( - + )} ); From af102b9bf83fe2e0d3a1cb730bc14b063f428e17 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Mon, 23 May 2022 16:49:21 +0200 Subject: [PATCH 22/45] feat: ignore casing of filters --- .../features/packageData/model/filters/AbstractPythonFilter.ts | 3 ++- .../gui/src/features/packageData/model/filters/NameFilter.ts | 2 +- .../src/features/packageData/model/filters/VisibilityFilter.ts | 2 +- .../src/features/packageData/model/filters/filterFactory.ts | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) 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 b4031ea94..323c2ce40 100644 --- a/api-editor/gui/src/features/packageData/model/filters/AbstractPythonFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/AbstractPythonFilter.ts @@ -32,7 +32,8 @@ export default abstract class AbstractPythonFilter { abstract shouldKeepParameter(pythonParameter: PythonParameter): boolean; /** - * Whether the given declaration should be kept after filtering. This function should not be overridden. + * Whether the given declaration should be kept after filtering. This function generally does not need to be + * overridden. */ shouldKeepDeclaration(pythonDeclaration: PythonDeclaration): boolean { if (pythonDeclaration instanceof PythonModule) { 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 3c9daa0ea..2d3917a28 100644 --- a/api-editor/gui/src/features/packageData/model/filters/NameFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/NameFilter.ts @@ -26,7 +26,7 @@ export default class NameFilter extends AbstractPythonFilter { return this.shouldKeepDeclaration(pythonParameter); } - private shouldKeepDeclaration(declaration: PythonDeclaration): boolean { + shouldKeepDeclaration(declaration: PythonDeclaration): boolean { return declaration.name.toLowerCase().includes(this.name.toLowerCase()); } } 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 c05b6fb16..4c95b4c52 100644 --- a/api-editor/gui/src/features/packageData/model/filters/VisibilityFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/VisibilityFilter.ts @@ -26,7 +26,7 @@ export default class VisibilityFilter extends AbstractPythonFilter { return this.shouldKeepDeclaration(pythonParameter); } - private shouldKeepDeclaration(declaration: PythonDeclaration): boolean { + shouldKeepDeclaration(declaration: PythonDeclaration): boolean { return declaration.isPublicDeclaration() == (this.visibility == Visibility.Public) } } 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 21f12c91c..93c8c24f2 100644 --- a/api-editor/gui/src/features/packageData/model/filters/filterFactory.ts +++ b/api-editor/gui/src/features/packageData/model/filters/filterFactory.ts @@ -34,7 +34,7 @@ function parsePotentiallyNegatedToken(token: string): Optional { // Filters with fixed text - switch (token) { + switch (token.toLowerCase()) { // Declaration type case 'is:module': From 341a6e6e466364faa761463e82b9b3ff2877da5b Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Mon, 23 May 2022 16:51:41 +0200 Subject: [PATCH 23/45] perf: memoization of TreeView --- .../features/packageData/treeView/TreeView.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/api-editor/gui/src/features/packageData/treeView/TreeView.tsx b/api-editor/gui/src/features/packageData/treeView/TreeView.tsx index d081f402c..9c25e1d0e 100644 --- a/api-editor/gui/src/features/packageData/treeView/TreeView.tsx +++ b/api-editor/gui/src/features/packageData/treeView/TreeView.tsx @@ -25,7 +25,7 @@ interface TreeViewProps { filter: AbstractPythonFilter; } -const TreeView: React.FC = ({ pythonPackage, filter }) => { +const TreeView: React.FC = memo(({pythonPackage, filter}) => { const dispatch = useAppDispatch(); const allExpanded = useAppSelector(selectAllExpandedInTreeView); @@ -61,7 +61,7 @@ const TreeView: React.FC = ({ pythonPackage, filter }) => { return ( - {({ height }) => ( + {({height}) => ( = ({ pythonPackage, filter }) => { )} ); -}; +}); const walkChildrenInPreorder = function ( allExpandedItemsInTreeView: { [target: string]: true }, @@ -100,23 +100,23 @@ const walkChildrenInPreorder = function ( }; const TreeNodeGenerator: React.FC = memo( - ({ data, index, style }) => { + ({data, index, style}) => { const declaration = data.children[index]; const filter = data.filter; return ( {declaration instanceof PythonModule && ( - + )} {declaration instanceof PythonClass && ( - + )} {declaration instanceof PythonFunction && ( - + )} {declaration instanceof PythonParameter && ( - + )} ); From 3ade1b4007ceed27e7f26f13dc9e5bd1b3698242 Mon Sep 17 00:00:00 2001 From: lars-reimann Date: Mon, 23 May 2022 14:55:00 +0000 Subject: [PATCH 24/45] style: apply automatic fixes of linters --- api-editor/gui/src/app/App.tsx | 54 +++++++++-------- api-editor/gui/src/common/MenuBar.tsx | 48 +++++++-------- .../model/filters/AbstractPythonFilter.ts | 12 ++-- .../model/filters/VisibilityFilter.ts | 4 +- .../model/filters/filterFactory.ts | 32 +++++----- .../features/packageData/packageDataSlice.ts | 8 +-- .../packageData/treeView/ClassNode.tsx | 15 ++--- .../packageData/treeView/FunctionNode.tsx | 20 ++----- .../packageData/treeView/ModuleNode.tsx | 15 ++--- .../packageData/treeView/ParameterNode.tsx | 16 +---- .../packageData/treeView/TreeNode.tsx | 35 +++-------- .../packageData/treeView/TreeView.tsx | 60 +++++++------------ 12 files changed, 126 insertions(+), 193 deletions(-) diff --git a/api-editor/gui/src/app/App.tsx b/api-editor/gui/src/app/App.tsx index fbd0633d9..f20a7bf4b 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,18 +33,21 @@ 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, toggleIsExpandedInTreeView,} from '../features/packageData/packageDataSlice'; +import { + selectShowPackageDataImportDialog, + toggleIsExpandedInTreeView, +} 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(); @@ -134,18 +137,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 === 'none' && - } + {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/common/MenuBar.tsx b/api-editor/gui/src/common/MenuBar.tsx index a354cd9e4..bdacb9e89 100644 --- a/api-editor/gui/src/common/MenuBar.tsx +++ b/api-editor/gui/src/common/MenuBar.tsx @@ -36,15 +36,15 @@ import { 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'; interface MenuBarProps { pythonPackage: PythonPackage; @@ -58,11 +58,11 @@ const HelpButton = function () { - } aria-label="help"/> + } aria-label="help" /> - - + + Filter Options @@ -88,8 +88,9 @@ const HelpButton = function () { name:xy - Displays only elements with names that contain the given string - xy. + + Displays only elements with names that contain the given string xy. + @@ -112,8 +113,7 @@ const HelpButton = function () { Displays only elements that do not match the given filter. Possible filters are any - in - this list. + in this list. @@ -175,8 +175,8 @@ const DeleteAllAnnotations = function () { ); }; -const MenuBar: React.FC = function ({pythonPackage, filter, setFilter, displayInferErrors}) { - const {colorMode, toggleColorMode} = useColorMode(); +const MenuBar: React.FC = function ({ pythonPackage, filter, setFilter, displayInferErrors }) { + const { colorMode, toggleColorMode } = useColorMode(); const initialFocusRef = useRef(null); const dispatch = useAppDispatch(); @@ -198,7 +198,7 @@ const MenuBar: React.FC = function ({pythonPackage, filter, setFil const requestOptions = { method: 'POST', - headers: {'Content-Type': 'application/json'}, + headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(annotatedPythonPackage), }; fetch('/api-editor/infer', requestOptions).then(async (response) => { @@ -226,7 +226,7 @@ const MenuBar: React.FC = function ({pythonPackage, filter, setFil {/* Box gets rid of popper.js warning "CSS margin styles cannot be used" */} - }> + }> File @@ -237,7 +237,7 @@ const MenuBar: React.FC = function ({pythonPackage, filter, setFil Annotations - + Annotations @@ -246,11 +246,11 @@ const MenuBar: React.FC = function ({pythonPackage, filter, setFil - + - }> + }> Settings @@ -264,7 +264,7 @@ const MenuBar: React.FC = function ({pythonPackage, filter, setFil - + @@ -299,12 +299,12 @@ const MenuBar: React.FC = function ({pythonPackage, filter, setFil - + Each scope must only be used once. - + ); 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 323c2ce40..b5abc273c 100644 --- a/api-editor/gui/src/features/packageData/model/filters/AbstractPythonFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/AbstractPythonFilter.ts @@ -4,7 +4,7 @@ import PythonModule from '../PythonModule'; import PythonParameter from '../PythonParameter'; import PythonPackage from '../PythonPackage'; import { isEmptyList } from '../../../../common/util/listOperations'; -import PythonDeclaration from "../PythonDeclaration"; +import PythonDeclaration from '../PythonDeclaration'; /** * An abstract base class for filters of Python declarations. To create a new filter create a new subclass and override @@ -37,15 +37,15 @@ export default abstract class AbstractPythonFilter { */ shouldKeepDeclaration(pythonDeclaration: PythonDeclaration): boolean { if (pythonDeclaration instanceof PythonModule) { - return this.shouldKeepModule(pythonDeclaration) + return this.shouldKeepModule(pythonDeclaration); } else if (pythonDeclaration instanceof PythonClass) { - return this.shouldKeepClass(pythonDeclaration) + return this.shouldKeepClass(pythonDeclaration); } else if (pythonDeclaration instanceof PythonFunction) { - return this.shouldKeepFunction(pythonDeclaration) + return this.shouldKeepFunction(pythonDeclaration); } else if (pythonDeclaration instanceof PythonParameter) { - return this.shouldKeepParameter(pythonDeclaration) + return this.shouldKeepParameter(pythonDeclaration); } else { - return true + return true; } } 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 4c95b4c52..64a07e5ba 100644 --- a/api-editor/gui/src/features/packageData/model/filters/VisibilityFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/VisibilityFilter.ts @@ -27,11 +27,11 @@ export default class VisibilityFilter extends AbstractPythonFilter { } shouldKeepDeclaration(declaration: PythonDeclaration): boolean { - return declaration.isPublicDeclaration() == (this.visibility == Visibility.Public) + return declaration.isPublicDeclaration() == (this.visibility == Visibility.Public); } } export enum Visibility { Public, - Internal + Internal, } 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 93c8c24f2..bcb260f2a 100644 --- a/api-editor/gui/src/features/packageData/model/filters/filterFactory.ts +++ b/api-editor/gui/src/features/packageData/model/filters/filterFactory.ts @@ -1,10 +1,10 @@ -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 DeclarationTypeFilter, { DeclarationType } from './DeclarationTypeFilter'; +import VisibilityFilter, { Visibility } from './VisibilityFilter'; +import { NegatedFilter } from './NegatedFilter'; +import { Optional } from '../../../../common/util/types'; export function createFilterFromString(text: string): AbstractPythonFilter { const filters: AbstractPythonFilter[] = []; @@ -12,7 +12,7 @@ export function createFilterFromString(text: string): AbstractPythonFilter { for (const token of text.split(/\s+/)) { const newFilter = parsePotentiallyNegatedToken(token); if (newFilter) { - filters.push(newFilter) + filters.push(newFilter); } } @@ -20,22 +20,20 @@ export function createFilterFromString(text: string): AbstractPythonFilter { } function parsePotentiallyNegatedToken(token: string): Optional { - const isNegated = token.startsWith('!') - const positiveToken = isNegated ? token.substring(1) : token + const isNegated = token.startsWith('!'); + const positiveToken = isNegated ? token.substring(1) : token; - const newPositiveFilter = parsePositiveToken(positiveToken) + const newPositiveFilter = parsePositiveToken(positiveToken); if (!newPositiveFilter || !isNegated) { - return newPositiveFilter + return newPositiveFilter; } else { - return new NegatedFilter(newPositiveFilter) + return new NegatedFilter(newPositiveFilter); } } function parsePositiveToken(token: string): Optional { - // Filters with fixed text switch (token.toLowerCase()) { - // Declaration type case 'is:module': return new DeclarationTypeFilter(DeclarationType.Module); @@ -48,14 +46,14 @@ function parsePositiveToken(token: string): Optional { // Visibility case 'is:public': - return new VisibilityFilter(Visibility.Public) + return new VisibilityFilter(Visibility.Public); case 'is:internal': - return new VisibilityFilter(Visibility.Internal) + return new VisibilityFilter(Visibility.Internal); } // Name - const nameMatch = /^name:(?\w+)$/.exec(token) + const nameMatch = /^name:(?\w+)$/.exec(token); if (nameMatch) { - return new NameFilter(nameMatch?.groups?.name as string) + return new NameFilter(nameMatch?.groups?.name as string); } } diff --git a/api-editor/gui/src/features/packageData/packageDataSlice.ts b/api-editor/gui/src/features/packageData/packageDataSlice.ts index 0c1684961..e7acb6f6a 100644 --- a/api-editor/gui/src/features/packageData/packageDataSlice.ts +++ b/api-editor/gui/src/features/packageData/packageDataSlice.ts @@ -52,10 +52,8 @@ export const selectIsExpandedInTreeView = (target: string) => (state: RootState): boolean => Boolean(selectPackageData(state).expandedInTreeView[target]); -export const selectAllExpandedInTreeView = ( - state: RootState, -): { [target: string]: true } => selectPackageData(state).expandedInTreeView; -export const selectTreeViewScrollOffset = (state: RootState): number => - selectPackageData(state).treeViewScrollOffset; +export const selectAllExpandedInTreeView = (state: RootState): { [target: string]: true } => + selectPackageData(state).expandedInTreeView; +export const selectTreeViewScrollOffset = (state: RootState): number => selectPackageData(state).treeViewScrollOffset; export const selectShowPackageDataImportDialog = (state: RootState): boolean => selectPackageData(state).showImportDialog; diff --git a/api-editor/gui/src/features/packageData/treeView/ClassNode.tsx b/api-editor/gui/src/features/packageData/treeView/ClassNode.tsx index 387509d03..e2253fbe7 100644 --- a/api-editor/gui/src/features/packageData/treeView/ClassNode.tsx +++ b/api-editor/gui/src/features/packageData/treeView/ClassNode.tsx @@ -1,9 +1,9 @@ import React from 'react'; -import {FaChalkboard} from 'react-icons/fa'; -import {isEmptyList} from '../../../common/util/listOperations'; +import { FaChalkboard } from 'react-icons/fa'; +import { isEmptyList } from '../../../common/util/listOperations'; import PythonClass from '../model/PythonClass'; import TreeNode from './TreeNode'; -import AbstractPythonFilter from "../model/filters/AbstractPythonFilter"; +import AbstractPythonFilter from '../model/filters/AbstractPythonFilter'; interface ClassNodeProps { pythonClass: PythonClass; @@ -13,14 +13,7 @@ interface ClassNodeProps { const ClassNode: React.FC = function ({ pythonClass, filter }) { const hasMethods = !isEmptyList(pythonClass.methods); - return ( - - ); + return ; }; export default ClassNode; diff --git a/api-editor/gui/src/features/packageData/treeView/FunctionNode.tsx b/api-editor/gui/src/features/packageData/treeView/FunctionNode.tsx index 422bd77ad..c6b09bec7 100644 --- a/api-editor/gui/src/features/packageData/treeView/FunctionNode.tsx +++ b/api-editor/gui/src/features/packageData/treeView/FunctionNode.tsx @@ -1,29 +1,19 @@ import React from 'react'; -import {FaCogs} from 'react-icons/fa'; -import {isEmptyList} from '../../../common/util/listOperations'; +import { FaCogs } from 'react-icons/fa'; +import { isEmptyList } from '../../../common/util/listOperations'; import PythonFunction from '../model/PythonFunction'; import TreeNode from './TreeNode'; -import AbstractPythonFilter from "../model/filters/AbstractPythonFilter"; +import AbstractPythonFilter from '../model/filters/AbstractPythonFilter'; interface FunctionNodeProps { pythonFunction: PythonFunction; filter: AbstractPythonFilter; } -const FunctionNode: React.FC = function ({ - pythonFunction, - filter -}) { +const FunctionNode: React.FC = function ({ pythonFunction, filter }) { const hasParameters = !isEmptyList(pythonFunction.parameters); - return ( - - ); + return ; }; export default FunctionNode; diff --git a/api-editor/gui/src/features/packageData/treeView/ModuleNode.tsx b/api-editor/gui/src/features/packageData/treeView/ModuleNode.tsx index 45763d804..f9376cf13 100644 --- a/api-editor/gui/src/features/packageData/treeView/ModuleNode.tsx +++ b/api-editor/gui/src/features/packageData/treeView/ModuleNode.tsx @@ -1,9 +1,9 @@ import React from 'react'; -import {FaArchive} from 'react-icons/fa'; -import {isEmptyList} from '../../../common/util/listOperations'; +import { FaArchive } from 'react-icons/fa'; +import { isEmptyList } from '../../../common/util/listOperations'; import PythonModule from '../model/PythonModule'; import TreeNode from './TreeNode'; -import AbstractPythonFilter from "../model/filters/AbstractPythonFilter"; +import AbstractPythonFilter from '../model/filters/AbstractPythonFilter'; interface ModuleNodeProps { pythonModule: PythonModule; @@ -15,14 +15,7 @@ const ModuleNode: React.FC = function ({ pythonModule, filter } const hasFunctions = !isEmptyList(pythonModule.functions); const hasChildren = hasClasses || hasFunctions; - return ( - - ); + return ; }; export default ModuleNode; diff --git a/api-editor/gui/src/features/packageData/treeView/ParameterNode.tsx b/api-editor/gui/src/features/packageData/treeView/ParameterNode.tsx index 398ec491f..6caec12b8 100644 --- a/api-editor/gui/src/features/packageData/treeView/ParameterNode.tsx +++ b/api-editor/gui/src/features/packageData/treeView/ParameterNode.tsx @@ -2,25 +2,15 @@ import React from 'react'; import { FaKeyboard } from 'react-icons/fa'; import PythonParameter from '../model/PythonParameter'; import TreeNode from './TreeNode'; -import AbstractPythonFilter from "../model/filters/AbstractPythonFilter"; +import AbstractPythonFilter from '../model/filters/AbstractPythonFilter'; interface ParameterNodeProps { pythonParameter: PythonParameter; filter: AbstractPythonFilter; } -const ParameterNode: React.FC = function ({ - pythonParameter, - filter -}) { - return ( - - ); +const ParameterNode: React.FC = function ({ pythonParameter, filter }) { + return ; }; export default ParameterNode; diff --git a/api-editor/gui/src/features/packageData/treeView/TreeNode.tsx b/api-editor/gui/src/features/packageData/treeView/TreeNode.tsx index 39491405e..43daeb8cf 100644 --- a/api-editor/gui/src/features/packageData/treeView/TreeNode.tsx +++ b/api-editor/gui/src/features/packageData/treeView/TreeNode.tsx @@ -5,12 +5,9 @@ import { useLocation } from 'react-router'; import { useNavigate } from 'react-router-dom'; import { useAppDispatch, useAppSelector } from '../../../app/hooks'; import PythonDeclaration from '../model/PythonDeclaration'; -import { - selectIsExpandedInTreeView, - toggleIsExpandedInTreeView, -} from '../packageDataSlice'; +import { selectIsExpandedInTreeView, toggleIsExpandedInTreeView } from '../packageDataSlice'; import VisibilityIndicator from './VisibilityIndicator'; -import AbstractPythonFilter from "../model/filters/AbstractPythonFilter"; +import AbstractPythonFilter from '../model/filters/AbstractPythonFilter'; interface TreeNodeProps { declaration: PythonDeclaration; @@ -19,31 +16,18 @@ interface TreeNodeProps { filter: AbstractPythonFilter; } -const TreeNode: React.FC = function ({ - declaration, - icon, - isExpandable, - filter -}) { +const TreeNode: React.FC = function ({ declaration, icon, isExpandable, filter }) { const currentPathname = useLocation().pathname; const navigate = useNavigate(); const dispatch = useAppDispatch(); - const showChildren = useAppSelector( - selectIsExpandedInTreeView(declaration.pathAsString()), - ); + const showChildren = useAppSelector(selectIsExpandedInTreeView(declaration.pathAsString())); const level = levelOf(declaration); const paddingLeft = level === 0 ? '1rem' : `${1 + 0.75 * level}rem`; - const backgroundColor = isSelected(declaration, currentPathname) - ? 'cornflowerblue' - : undefined; - const color = isSelected(declaration, currentPathname) - ? 'white' - : undefined; - const fontWeight = filter.shouldKeepDeclaration(declaration) - ? 'bold' - : undefined; + const backgroundColor = isSelected(declaration, currentPathname) ? 'cornflowerblue' : undefined; + const color = isSelected(declaration, currentPathname) ? 'white' : undefined; + const fontWeight = filter.shouldKeepDeclaration(declaration) ? 'bold' : undefined; const handleClick = () => { dispatch(toggleIsExpandedInTreeView(declaration.pathAsString())); @@ -74,10 +58,7 @@ const levelOf = function (declaration: PythonDeclaration): number { return declaration.path().length - 2; }; -const isSelected = function ( - declaration: PythonDeclaration, - currentPathname: string, -): boolean { +const isSelected = function (declaration: PythonDeclaration, currentPathname: string): boolean { return `/${declaration.pathAsString()}` === currentPathname; }; diff --git a/api-editor/gui/src/features/packageData/treeView/TreeView.tsx b/api-editor/gui/src/features/packageData/treeView/TreeView.tsx index 9c25e1d0e..94c841887 100644 --- a/api-editor/gui/src/features/packageData/treeView/TreeView.tsx +++ b/api-editor/gui/src/features/packageData/treeView/TreeView.tsx @@ -1,20 +1,20 @@ -import {Box} from '@chakra-ui/react'; -import React, {memo, useCallback, useEffect, useRef} from 'react'; +import { Box } from '@chakra-ui/react'; +import React, { memo, useCallback, useEffect, useRef } from 'react'; import AutoSizer from 'react-virtualized-auto-sizer'; -import {FixedSizeList, ListChildComponentProps} from 'react-window'; -import {useAppDispatch, useAppSelector} from '../../../app/hooks'; +import { FixedSizeList, ListChildComponentProps } from 'react-window'; +import { useAppDispatch, useAppSelector } from '../../../app/hooks'; import PythonClass from '../model/PythonClass'; import PythonDeclaration from '../model/PythonDeclaration'; import PythonFunction from '../model/PythonFunction'; import PythonModule from '../model/PythonModule'; import PythonPackage from '../model/PythonPackage'; import PythonParameter from '../model/PythonParameter'; -import {selectAllExpandedInTreeView, selectTreeViewScrollOffset, setTreeViewScrollOffset,} from '../packageDataSlice'; +import { selectAllExpandedInTreeView, selectTreeViewScrollOffset, setTreeViewScrollOffset } from '../packageDataSlice'; import ClassNode from './ClassNode'; import FunctionNode from './FunctionNode'; import ModuleNode from './ModuleNode'; import ParameterNode from './ParameterNode'; -import AbstractPythonFilter from "../model/filters/AbstractPythonFilter"; +import AbstractPythonFilter from '../model/filters/AbstractPythonFilter'; interface ScrollOffset { scrollOffset: number; @@ -25,7 +25,7 @@ interface TreeViewProps { filter: AbstractPythonFilter; } -const TreeView: React.FC = memo(({pythonPackage, filter}) => { +const TreeView: React.FC = memo(({ pythonPackage, filter }) => { const dispatch = useAppDispatch(); const allExpanded = useAppSelector(selectAllExpandedInTreeView); @@ -46,8 +46,7 @@ const TreeView: React.FC = memo(({pythonPackage, filter}) => { const current = listRef.current; if (current) { try { - const newScrollOffset = (current.state as ScrollOffset) - .scrollOffset; + const newScrollOffset = (current.state as ScrollOffset).scrollOffset; dispatch(setTreeViewScrollOffset(newScrollOffset)); } catch { dispatch(setTreeViewScrollOffset(0)); @@ -61,11 +60,11 @@ const TreeView: React.FC = memo(({pythonPackage, filter}) => { return ( - {({height}) => ( + {({ height }) => ( data.children[index]?.pathAsString()} width="100%" height={height} @@ -89,38 +88,25 @@ const walkChildrenInPreorder = function ( ): PythonDeclaration[] { return declaration.children().flatMap((it) => { if (allExpandedItemsInTreeView[it.pathAsString()]) { - return [ - it, - ...walkChildrenInPreorder(allExpandedItemsInTreeView, it), - ]; + return [it, ...walkChildrenInPreorder(allExpandedItemsInTreeView, it)]; } else { return [it]; } }); }; -const TreeNodeGenerator: React.FC = memo( - ({data, index, style}) => { - const declaration = data.children[index]; - const filter = data.filter; +const TreeNodeGenerator: React.FC = memo(({ data, index, style }) => { + const declaration = data.children[index]; + const filter = data.filter; - return ( - - {declaration instanceof PythonModule && ( - - )} - {declaration instanceof PythonClass && ( - - )} - {declaration instanceof PythonFunction && ( - - )} - {declaration instanceof PythonParameter && ( - - )} - - ); - }, -); + return ( + + {declaration instanceof PythonModule && } + {declaration instanceof PythonClass && } + {declaration instanceof PythonFunction && } + {declaration instanceof PythonParameter && } + + ); +}); export default TreeView; From ec4d07c3245eab124f606a4b36150d452ba3f5c5 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Mon, 23 May 2022 18:49:35 +0200 Subject: [PATCH 25/45] feat: filter `annotation:any` --- api-editor/gui/src/app/App.tsx | 2 +- .../features/annotations/annotationSlice.ts | 60 ++++++++-------- .../model/filters/AbstractPythonFilter.ts | 49 ++++++------- .../model/filters/AnnotationFilter.ts | 69 +++++++++++++++++++ .../model/filters/ConjunctiveFilter.ts | 17 ++--- .../model/filters/DeclarationTypeFilter.ts | 9 +-- .../packageData/model/filters/NameFilter.ts | 19 ++--- .../model/filters/NegatedFilter.ts | 17 ++--- .../model/filters/VisibilityFilter.ts | 19 ++--- .../model/filters/filterFactory.ts | 16 +++-- .../packageData/treeView/TreeNode.tsx | 37 +++++++--- 11 files changed, 208 insertions(+), 106 deletions(-) create mode 100644 api-editor/gui/src/features/packageData/model/filters/AnnotationFilter.ts diff --git a/api-editor/gui/src/app/App.tsx b/api-editor/gui/src/app/App.tsx index f20a7bf4b..abdcd0747 100644 --- a/api-editor/gui/src/app/App.tsx +++ b/api-editor/gui/src/app/App.tsx @@ -94,7 +94,7 @@ const App: React.FC = function () { const [filter, setFilter] = useState('is:public'); const pythonFilter = createFilterFromString(filter); - const filteredPythonPackage = pythonFilter.applyToPackage(pythonPackage); + const filteredPythonPackage = pythonFilter.applyToPackage(pythonPackage, useAppSelector(selectAnnotations)); const userActionTarget = pythonPackage.getByRelativePathAsString(currentUserAction.target); diff --git a/api-editor/gui/src/features/annotations/annotationSlice.ts b/api-editor/gui/src/features/annotations/annotationSlice.ts index 523991924..9382a7ad3 100644 --- a/api-editor/gui/src/features/annotations/annotationSlice.ts +++ b/api-editor/gui/src/features/annotations/annotationSlice.ts @@ -1,6 +1,6 @@ -import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit'; +import {createAsyncThunk, createSlice, PayloadAction} from '@reduxjs/toolkit'; import * as idb from 'idb-keyval'; -import { RootState } from '../../app/store'; +import {RootState} from '../../app/store'; export interface AnnotationsState { attributes: { @@ -384,12 +384,12 @@ const annotationsSlice = createSlice({ } state.calledAfters[action.payload.target][ action.payload.calledAfterName - ] = action.payload; + ] = action.payload; }, removeCalledAfter(state, action: PayloadAction) { delete state.calledAfters[action.payload.target][ action.payload.calledAfterName - ]; + ]; if ( Object.keys(state.calledAfters[action.payload.target]) .length === 0 @@ -453,7 +453,7 @@ const annotationsSlice = createSlice({ removeGroup(state, action: PayloadAction) { delete state.groups[action.payload.target][ action.payload.groupName - ]; + ]; if (Object.keys(state.groups[action.payload.target]).length === 0) { delete state.groups[action.payload.target]; } @@ -571,7 +571,7 @@ const annotationsSlice = createSlice({ }, }); -const { actions, reducer } = annotationsSlice; +const {actions, reducer} = annotationsSlice; export const { set: setAnnotations, reset: resetAnnotations, @@ -620,53 +620,53 @@ export default reducer; export const selectAnnotations = (state: RootState) => state.annotations; export const selectAttribute = (target: string) => - (state: RootState): AttributeAnnotation | undefined => - selectAnnotations(state).attributes[target]; + (state: RootState): AttributeAnnotation | undefined => + selectAnnotations(state).attributes[target]; export const selectBoundary = (target: string) => - (state: RootState): BoundaryAnnotation | undefined => - selectAnnotations(state).boundaries[target]; + (state: RootState): BoundaryAnnotation | undefined => + selectAnnotations(state).boundaries[target]; export const selectCalledAfters = (target: string) => - (state: RootState): { [calledAfter: string]: CalledAfterAnnotation } => - selectAnnotations(state).calledAfters[target] ?? {}; + (state: RootState): { [calledAfter: string]: CalledAfterAnnotation } => + selectAnnotations(state).calledAfters[target] ?? {}; export const selectConstant = (target: string) => - (state: RootState): ConstantAnnotation | undefined => - selectAnnotations(state).constants[target]; + (state: RootState): ConstantAnnotation | undefined => + selectAnnotations(state).constants[target]; export const selectCurrentUserAction = (state: RootState): UserAction => selectAnnotations(state).currentUserAction; export const selectEnum = (target: string) => - (state: RootState): EnumAnnotation | undefined => - selectAnnotations(state).enums[target]; + (state: RootState): EnumAnnotation | undefined => + selectAnnotations(state).enums[target]; export const selectGroups = (target: string) => - (state: RootState): { [groupName: string]: GroupAnnotation } => - selectAnnotations(state).groups[target] ?? {}; + (state: RootState): { [groupName: string]: GroupAnnotation } => + selectAnnotations(state).groups[target] ?? {}; export const selectMove = (target: string) => - (state: RootState): MoveAnnotation | undefined => - selectAnnotations(state).moves[target]; + (state: RootState): MoveAnnotation | undefined => + selectAnnotations(state).moves[target]; export const selectOptional = (target: string) => - (state: RootState): OptionalAnnotation | undefined => - selectAnnotations(state).optionals[target]; + (state: RootState): OptionalAnnotation | undefined => + selectAnnotations(state).optionals[target]; export const selectPure = (target: string) => - (state: RootState): PureAnnotation | undefined => - selectAnnotations(state).pures[target]; + (state: RootState): PureAnnotation | undefined => + selectAnnotations(state).pures[target]; export const selectRenaming = (target: string) => - (state: RootState): RenameAnnotation | undefined => - selectAnnotations(state).renamings[target]; + (state: RootState): RenameAnnotation | undefined => + selectAnnotations(state).renamings[target]; export const selectRequired = (target: string) => - (state: RootState): RequiredAnnotation | undefined => - selectAnnotations(state).requireds[target]; + (state: RootState): RequiredAnnotation | undefined => + selectAnnotations(state).requireds[target]; export const selectShowAnnotationImportDialog = (state: RootState): boolean => selectAnnotations(state).showImportDialog; export const selectUnused = (target: string) => - (state: RootState): UnusedAnnotation | undefined => - selectAnnotations(state).unuseds[target]; + (state: RootState): UnusedAnnotation | undefined => + selectAnnotations(state).unuseds[target]; 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 b5abc273c..e2e460d8b 100644 --- a/api-editor/gui/src/features/packageData/model/filters/AbstractPythonFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/AbstractPythonFilter.ts @@ -3,8 +3,9 @@ import PythonFunction from '../PythonFunction'; import PythonModule from '../PythonModule'; import PythonParameter from '../PythonParameter'; import PythonPackage from '../PythonPackage'; -import { isEmptyList } from '../../../../common/util/listOperations'; -import PythonDeclaration from '../PythonDeclaration'; +import {isEmptyList} from '../../../../common/util/listOperations'; +import PythonDeclaration from "../PythonDeclaration"; +import {AnnotationsState} from "../../../annotations/annotationSlice"; /** * An abstract base class for filters of Python declarations. To create a new filter create a new subclass and override @@ -14,38 +15,38 @@ export default abstract class AbstractPythonFilter { /** * Whether the given module should be kept after filtering. */ - abstract shouldKeepModule(pythonModule: PythonModule): boolean; + abstract shouldKeepModule(pythonModule: PythonModule, annotations: AnnotationsState): boolean; /** * Whether the given class should be kept after filtering. */ - abstract shouldKeepClass(pythonClass: PythonClass): boolean; + abstract shouldKeepClass(pythonClass: PythonClass, annotations: AnnotationsState): boolean; /** * Whether the given function should be kept after filtering. */ - abstract shouldKeepFunction(pythonFunction: PythonFunction): boolean; + abstract shouldKeepFunction(pythonFunction: PythonFunction, annotations: AnnotationsState): boolean; /** * Whether the given parameter should be kept after filtering. */ - abstract shouldKeepParameter(pythonParameter: PythonParameter): boolean; + abstract shouldKeepParameter(pythonParameter: PythonParameter, annotations: AnnotationsState): boolean; /** * Whether the given declaration should be kept after filtering. This function generally does not need to be * overridden. */ - shouldKeepDeclaration(pythonDeclaration: PythonDeclaration): boolean { + shouldKeepDeclaration(pythonDeclaration: PythonDeclaration, annotations: AnnotationsState): boolean { if (pythonDeclaration instanceof PythonModule) { - return this.shouldKeepModule(pythonDeclaration); + return this.shouldKeepModule(pythonDeclaration, annotations) } else if (pythonDeclaration instanceof PythonClass) { - return this.shouldKeepClass(pythonDeclaration); + return this.shouldKeepClass(pythonDeclaration, annotations) } else if (pythonDeclaration instanceof PythonFunction) { - return this.shouldKeepFunction(pythonDeclaration); + return this.shouldKeepFunction(pythonDeclaration, annotations) } else if (pythonDeclaration instanceof PythonParameter) { - return this.shouldKeepParameter(pythonDeclaration); + return this.shouldKeepParameter(pythonDeclaration, annotations) } else { - return true; + return true } } @@ -53,9 +54,9 @@ 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): PythonPackage { + applyToPackage(pythonPackage: PythonPackage, annotations: AnnotationsState): PythonPackage { // Filter modules - const modules = pythonPackage.modules.map((it) => this.applyToModule(it)).filter((it) => it !== null); + const modules = pythonPackage.modules.map((it) => this.applyToModule(it, annotations)).filter((it) => it !== null); // Create filtered package return new PythonPackage( @@ -70,17 +71,17 @@ 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): PythonModule | null { + private applyToModule(pythonModule: PythonModule, annotations: AnnotationsState): PythonModule | null { // If the module is kept, keep the entire subtree - if (this.shouldKeepModule(pythonModule)) { + if (this.shouldKeepModule(pythonModule, annotations)) { return pythonModule; } // Filter classes - const classes = pythonModule.classes.map((it) => this.applyToClass(it)).filter((it) => it !== null); + const classes = pythonModule.classes.map((it) => this.applyToClass(it, annotations)).filter((it) => it !== null); // Filter functions - const functions = pythonModule.functions.map((it) => this.applyToFunction(it)).filter((it) => it !== null); + const functions = pythonModule.functions.map((it) => this.applyToFunction(it, annotations)).filter((it) => it !== null); // Return null if all classes and functions are removed if (isEmptyList(classes) && isEmptyList(functions)) { @@ -101,14 +102,14 @@ 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): PythonClass | null { + private applyToClass(pythonClass: PythonClass, annotations: AnnotationsState): PythonClass | null { // If the class is kept, keep the entire subtree - if (this.shouldKeepClass(pythonClass)) { + if (this.shouldKeepClass(pythonClass, annotations)) { return pythonClass; } // Filter methods - const methods = pythonClass.methods.map((it) => this.applyToFunction(it)).filter((it) => it !== null); + const methods = pythonClass.methods.map((it) => this.applyToFunction(it, annotations)).filter((it) => it !== null); // Return null if all methods are removed if (isEmptyList(methods)) { @@ -132,16 +133,16 @@ 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): PythonFunction | null { + private applyToFunction(pythonFunction: PythonFunction, annotations: AnnotationsState): PythonFunction | null { // If the function is kept, keep the entire subtree - if (this.shouldKeepFunction(pythonFunction)) { + if (this.shouldKeepFunction(pythonFunction, annotations)) { return pythonFunction; } // Filter parameters const parameters = pythonFunction.parameters .map((it) => it.clone()) - .filter((it) => this.shouldKeepParameter(it)); + .filter((it) => this.shouldKeepParameter(it, annotations)); // 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 new file mode 100644 index 000000000..9961f2fc1 --- /dev/null +++ b/api-editor/gui/src/features/packageData/model/filters/AnnotationFilter.ts @@ -0,0 +1,69 @@ +import PythonClass from '../PythonClass'; +import PythonFunction from '../PythonFunction'; +import PythonModule from '../PythonModule'; +import PythonParameter from '../PythonParameter'; +import AbstractPythonFilter from './AbstractPythonFilter'; +import PythonDeclaration from '../PythonDeclaration'; +import {AnnotationsState} from "../../../annotations/annotationSlice"; + +export default class AnnotationFilter extends AbstractPythonFilter { + constructor(readonly type: AnnotationType) { + super(); + } + + shouldKeepModule(pythonModule: PythonModule, annotations: AnnotationsState): boolean { + return this.shouldKeepDeclaration(pythonModule, annotations); + } + + shouldKeepClass(pythonClass: PythonClass, annotations: AnnotationsState): boolean { + return this.shouldKeepDeclaration(pythonClass, annotations); + } + + shouldKeepFunction(pythonFunction: PythonFunction, annotations: AnnotationsState): boolean { + return this.shouldKeepDeclaration(pythonFunction, annotations); + } + + shouldKeepParameter(pythonParameter: PythonParameter, annotations: AnnotationsState): boolean { + return this.shouldKeepDeclaration(pythonParameter, annotations); + } + + shouldKeepDeclaration(declaration: PythonDeclaration, annotations: AnnotationsState): boolean { + const id = declaration.pathAsString() + + switch (this.type) { + case AnnotationType.Any: + return id in annotations['attributes'] || + id in annotations['boundaries'] || + id in annotations['calledAfters'] || + id in annotations['constants'] || + id in annotations['enums'] || + id in annotations['groups'] || + id in annotations['moves'] || + id in annotations['optionals'] || + id in annotations['pures'] || + id in annotations['renamings'] || + id in annotations['requireds'] || + id in annotations['unuseds']; + + // TODO: check for specific annotations + default: + return true + } + } +} + +export enum AnnotationType { + Any, + Attributes, + Boundaries, + CalledAfters, + Constants, + Enums, + Groups, + Moves, + Optionals, + Pures, + Renamings, + Requireds, + Unuseds, +} 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 812309dc3..7ddecd5ef 100644 --- a/api-editor/gui/src/features/packageData/model/filters/ConjunctiveFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/ConjunctiveFilter.ts @@ -3,6 +3,7 @@ import PythonClass from '../PythonClass'; import PythonFunction from '../PythonFunction'; import PythonParameter from '../PythonParameter'; import PythonModule from '../PythonModule'; +import {AnnotationsState} from "../../../annotations/annotationSlice"; /** * Keeps declarations iff all contained filters want to keep it. @@ -12,19 +13,19 @@ export class ConjunctiveFilter extends AbstractPythonFilter { super(); } - shouldKeepModule(pythonModule: PythonModule): boolean { - return this.filters.every((it) => it.shouldKeepModule(pythonModule)); + shouldKeepModule(pythonModule: PythonModule, annotations: AnnotationsState): boolean { + return this.filters.every((it) => it.shouldKeepModule(pythonModule, annotations)); } - shouldKeepClass(pythonClass: PythonClass): boolean { - return this.filters.every((it) => it.shouldKeepClass(pythonClass)); + shouldKeepClass(pythonClass: PythonClass, annotations: AnnotationsState): boolean { + return this.filters.every((it) => it.shouldKeepClass(pythonClass, annotations)); } - shouldKeepFunction(pythonFunction: PythonFunction): boolean { - return this.filters.every((it) => it.shouldKeepFunction(pythonFunction)); + shouldKeepFunction(pythonFunction: PythonFunction, annotations: AnnotationsState): boolean { + return this.filters.every((it) => it.shouldKeepFunction(pythonFunction, annotations)); } - shouldKeepParameter(pythonParameter: PythonParameter): boolean { - return this.filters.every((it) => it.shouldKeepParameter(pythonParameter)); + shouldKeepParameter(pythonParameter: PythonParameter, annotations: AnnotationsState): boolean { + return this.filters.every((it) => it.shouldKeepParameter(pythonParameter, annotations)); } } 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 74303e01a..50e622738 100644 --- a/api-editor/gui/src/features/packageData/model/filters/DeclarationTypeFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/DeclarationTypeFilter.ts @@ -3,25 +3,26 @@ import PythonFunction from '../PythonFunction'; import PythonModule from '../PythonModule'; import PythonParameter from '../PythonParameter'; import AbstractPythonFilter from './AbstractPythonFilter'; +import {AnnotationsState} from "../../../annotations/annotationSlice"; export default class DeclarationTypeFilter extends AbstractPythonFilter { constructor(readonly type: DeclarationType) { super(); } - shouldKeepModule(pythonModule: PythonModule): boolean { + shouldKeepModule(pythonModule: PythonModule, annotations: AnnotationsState): boolean { return this.type == DeclarationType.Module; } - shouldKeepClass(pythonClass: PythonClass): boolean { + shouldKeepClass(pythonClass: PythonClass, annotations: AnnotationsState): boolean { return this.type == DeclarationType.Class; } - shouldKeepFunction(pythonFunction: PythonFunction): boolean { + shouldKeepFunction(pythonFunction: PythonFunction, annotations: AnnotationsState): boolean { return this.type == DeclarationType.Function; } - shouldKeepParameter(pythonParameter: PythonParameter): boolean { + shouldKeepParameter(pythonParameter: PythonParameter, annotations: AnnotationsState): 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 2d3917a28..6873cf808 100644 --- a/api-editor/gui/src/features/packageData/model/filters/NameFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/NameFilter.ts @@ -4,29 +4,30 @@ import PythonModule from '../PythonModule'; import PythonParameter from '../PythonParameter'; import AbstractPythonFilter from './AbstractPythonFilter'; import PythonDeclaration from '../PythonDeclaration'; +import {AnnotationsState} from "../../../annotations/annotationSlice"; export default class NameFilter extends AbstractPythonFilter { constructor(readonly name: string) { super(); } - shouldKeepModule(pythonModule: PythonModule): boolean { - return this.shouldKeepDeclaration(pythonModule); + shouldKeepModule(pythonModule: PythonModule, annotations: AnnotationsState): boolean { + return this.shouldKeepDeclaration(pythonModule, annotations); } - shouldKeepClass(pythonClass: PythonClass): boolean { - return this.shouldKeepDeclaration(pythonClass); + shouldKeepClass(pythonClass: PythonClass, annotations: AnnotationsState): boolean { + return this.shouldKeepDeclaration(pythonClass, annotations); } - shouldKeepFunction(pythonFunction: PythonFunction): boolean { - return this.shouldKeepDeclaration(pythonFunction); + shouldKeepFunction(pythonFunction: PythonFunction, annotations: AnnotationsState): boolean { + return this.shouldKeepDeclaration(pythonFunction, annotations); } - shouldKeepParameter(pythonParameter: PythonParameter): boolean { - return this.shouldKeepDeclaration(pythonParameter); + shouldKeepParameter(pythonParameter: PythonParameter, annotations: AnnotationsState): boolean { + return this.shouldKeepDeclaration(pythonParameter, annotations); } - shouldKeepDeclaration(declaration: PythonDeclaration): boolean { + shouldKeepDeclaration(declaration: PythonDeclaration, annotations: AnnotationsState): boolean { return declaration.name.toLowerCase().includes(this.name.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 628ed83de..6a14a25aa 100644 --- a/api-editor/gui/src/features/packageData/model/filters/NegatedFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/NegatedFilter.ts @@ -3,6 +3,7 @@ import PythonClass from '../PythonClass'; import PythonFunction from '../PythonFunction'; import PythonParameter from '../PythonParameter'; import PythonModule from '../PythonModule'; +import {AnnotationsState} from "../../../annotations/annotationSlice"; /** * Keeps declarations iff the contained filter wants to remove it. @@ -12,19 +13,19 @@ export class NegatedFilter extends AbstractPythonFilter { super(); } - shouldKeepModule(pythonModule: PythonModule): boolean { - return !this.filter.shouldKeepModule(pythonModule); + shouldKeepModule(pythonModule: PythonModule, annotations: AnnotationsState): boolean { + return !this.filter.shouldKeepModule(pythonModule, annotations); } - shouldKeepClass(pythonClass: PythonClass): boolean { - return !this.filter.shouldKeepClass(pythonClass); + shouldKeepClass(pythonClass: PythonClass, annotations: AnnotationsState): boolean { + return !this.filter.shouldKeepClass(pythonClass, annotations); } - shouldKeepFunction(pythonFunction: PythonFunction): boolean { - return !this.filter.shouldKeepFunction(pythonFunction); + shouldKeepFunction(pythonFunction: PythonFunction, annotations: AnnotationsState): boolean { + return !this.filter.shouldKeepFunction(pythonFunction, annotations); } - shouldKeepParameter(pythonParameter: PythonParameter): boolean { - return !this.filter.shouldKeepParameter(pythonParameter); + shouldKeepParameter(pythonParameter: PythonParameter, annotations: AnnotationsState): boolean { + return !this.filter.shouldKeepParameter(pythonParameter, annotations); } } 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 64a07e5ba..82a515a8c 100644 --- a/api-editor/gui/src/features/packageData/model/filters/VisibilityFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/VisibilityFilter.ts @@ -4,29 +4,30 @@ import PythonModule from '../PythonModule'; import PythonParameter from '../PythonParameter'; import AbstractPythonFilter from './AbstractPythonFilter'; import PythonDeclaration from '../PythonDeclaration'; +import {AnnotationsState} from "../../../annotations/annotationSlice"; export default class VisibilityFilter extends AbstractPythonFilter { constructor(readonly visibility: Visibility) { super(); } - shouldKeepModule(pythonModule: PythonModule): boolean { - return this.shouldKeepDeclaration(pythonModule); + shouldKeepModule(pythonModule: PythonModule, annotations: AnnotationsState): boolean { + return this.shouldKeepDeclaration(pythonModule, annotations); } - shouldKeepClass(pythonClass: PythonClass): boolean { - return this.shouldKeepDeclaration(pythonClass); + shouldKeepClass(pythonClass: PythonClass, annotations: AnnotationsState): boolean { + return this.shouldKeepDeclaration(pythonClass, annotations); } - shouldKeepFunction(pythonFunction: PythonFunction): boolean { - return this.shouldKeepDeclaration(pythonFunction); + shouldKeepFunction(pythonFunction: PythonFunction, annotations: AnnotationsState): boolean { + return this.shouldKeepDeclaration(pythonFunction, annotations); } - shouldKeepParameter(pythonParameter: PythonParameter): boolean { - return this.shouldKeepDeclaration(pythonParameter); + shouldKeepParameter(pythonParameter: PythonParameter, annotations: AnnotationsState): boolean { + return this.shouldKeepDeclaration(pythonParameter, annotations); } - shouldKeepDeclaration(declaration: PythonDeclaration): boolean { + shouldKeepDeclaration(declaration: PythonDeclaration, annotations: AnnotationsState): boolean { return declaration.isPublicDeclaration() == (this.visibility == Visibility.Public); } } 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 bcb260f2a..52a145eb3 100644 --- a/api-editor/gui/src/features/packageData/model/filters/filterFactory.ts +++ b/api-editor/gui/src/features/packageData/model/filters/filterFactory.ts @@ -1,10 +1,11 @@ -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 DeclarationTypeFilter, {DeclarationType} from './DeclarationTypeFilter'; +import VisibilityFilter, {Visibility} from './VisibilityFilter'; +import {NegatedFilter} from './NegatedFilter'; +import {Optional} from '../../../../common/util/types'; +import AnnotationFilter, {AnnotationType} from "./AnnotationFilter"; export function createFilterFromString(text: string): AbstractPythonFilter { const filters: AbstractPythonFilter[] = []; @@ -49,6 +50,11 @@ function parsePositiveToken(token: string): Optional { return new VisibilityFilter(Visibility.Public); case 'is:internal': return new VisibilityFilter(Visibility.Internal); + + // Annotations + case 'annotation:any': + return new AnnotationFilter(AnnotationType.Any); + // TODO other cases like 'annotation:@boundary' } // Name diff --git a/api-editor/gui/src/features/packageData/treeView/TreeNode.tsx b/api-editor/gui/src/features/packageData/treeView/TreeNode.tsx index 43daeb8cf..92abbd787 100644 --- a/api-editor/gui/src/features/packageData/treeView/TreeNode.tsx +++ b/api-editor/gui/src/features/packageData/treeView/TreeNode.tsx @@ -5,9 +5,13 @@ import { useLocation } from 'react-router'; import { useNavigate } from 'react-router-dom'; import { useAppDispatch, useAppSelector } from '../../../app/hooks'; import PythonDeclaration from '../model/PythonDeclaration'; -import { selectIsExpandedInTreeView, toggleIsExpandedInTreeView } from '../packageDataSlice'; +import { + selectIsExpandedInTreeView, + toggleIsExpandedInTreeView, +} from '../packageDataSlice'; import VisibilityIndicator from './VisibilityIndicator'; -import AbstractPythonFilter from '../model/filters/AbstractPythonFilter'; +import AbstractPythonFilter from "../model/filters/AbstractPythonFilter"; +import {selectAnnotations} from "../../annotations/annotationSlice"; interface TreeNodeProps { declaration: PythonDeclaration; @@ -16,18 +20,32 @@ interface TreeNodeProps { filter: AbstractPythonFilter; } -const TreeNode: React.FC = function ({ declaration, icon, isExpandable, filter }) { +const TreeNode: React.FC = function ({ + declaration, + icon, + isExpandable, + filter +}) { const currentPathname = useLocation().pathname; const navigate = useNavigate(); const dispatch = useAppDispatch(); - const showChildren = useAppSelector(selectIsExpandedInTreeView(declaration.pathAsString())); + const showChildren = useAppSelector( + selectIsExpandedInTreeView(declaration.pathAsString()), + ); + const annotations = useAppSelector(selectAnnotations) const level = levelOf(declaration); const paddingLeft = level === 0 ? '1rem' : `${1 + 0.75 * level}rem`; - const backgroundColor = isSelected(declaration, currentPathname) ? 'cornflowerblue' : undefined; - const color = isSelected(declaration, currentPathname) ? 'white' : undefined; - const fontWeight = filter.shouldKeepDeclaration(declaration) ? 'bold' : undefined; + const backgroundColor = isSelected(declaration, currentPathname) + ? 'cornflowerblue' + : undefined; + const color = isSelected(declaration, currentPathname) + ? 'white' + : undefined; + const fontWeight = filter.shouldKeepDeclaration(declaration, annotations) + ? 'bold' + : undefined; const handleClick = () => { dispatch(toggleIsExpandedInTreeView(declaration.pathAsString())); @@ -58,7 +76,10 @@ const levelOf = function (declaration: PythonDeclaration): number { return declaration.path().length - 2; }; -const isSelected = function (declaration: PythonDeclaration, currentPathname: string): boolean { +const isSelected = function ( + declaration: PythonDeclaration, + currentPathname: string, +): boolean { return `/${declaration.pathAsString()}` === currentPathname; }; From 155eb6cdc19ccc5b59732757c8c616b629d41c88 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Mon, 23 May 2022 19:05:38 +0200 Subject: [PATCH 26/45] fix: incorrect filtering of parameters --- .../features/packageData/model/filters/AbstractPythonFilter.ts | 1 - .../src/features/packageData/model/filters/AnnotationFilter.ts | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) 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 e2e460d8b..3c629c237 100644 --- a/api-editor/gui/src/features/packageData/model/filters/AbstractPythonFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/AbstractPythonFilter.ts @@ -141,7 +141,6 @@ export default abstract class AbstractPythonFilter { // Filter parameters const parameters = pythonFunction.parameters - .map((it) => it.clone()) .filter((it) => this.shouldKeepParameter(it, annotations)); // Return null if all parameters are removed 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 9961f2fc1..cb2e5624b 100644 --- a/api-editor/gui/src/features/packageData/model/filters/AnnotationFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/AnnotationFilter.ts @@ -30,6 +30,8 @@ export default class AnnotationFilter extends AbstractPythonFilter { shouldKeepDeclaration(declaration: PythonDeclaration, annotations: AnnotationsState): boolean { const id = declaration.pathAsString() + console.log(id); + switch (this.type) { case AnnotationType.Any: return id in annotations['attributes'] || From 3b70a666e349bac2e841ff5bc58b5d4b482daff9 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Mon, 23 May 2022 19:06:54 +0200 Subject: [PATCH 27/45] fix: remove console.log --- .../src/features/packageData/model/filters/AnnotationFilter.ts | 2 -- 1 file changed, 2 deletions(-) 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 cb2e5624b..9961f2fc1 100644 --- a/api-editor/gui/src/features/packageData/model/filters/AnnotationFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/AnnotationFilter.ts @@ -30,8 +30,6 @@ export default class AnnotationFilter extends AbstractPythonFilter { shouldKeepDeclaration(declaration: PythonDeclaration, annotations: AnnotationsState): boolean { const id = declaration.pathAsString() - console.log(id); - switch (this.type) { case AnnotationType.Any: return id in annotations['attributes'] || From 98f0214e502f14ef93052a579ee841da30811012 Mon Sep 17 00:00:00 2001 From: lars-reimann Date: Mon, 23 May 2022 17:10:05 +0000 Subject: [PATCH 28/45] style: apply automatic fixes of linters --- .../features/annotations/annotationSlice.ts | 121 +++++++----------- .../model/filters/AbstractPythonFilter.ts | 35 +++-- .../model/filters/AnnotationFilter.ts | 12 +- .../model/filters/ConjunctiveFilter.ts | 2 +- .../model/filters/DeclarationTypeFilter.ts | 2 +- .../packageData/model/filters/NameFilter.ts | 2 +- .../model/filters/NegatedFilter.ts | 2 +- .../model/filters/VisibilityFilter.ts | 2 +- .../model/filters/filterFactory.ts | 12 +- .../packageData/treeView/TreeNode.tsx | 39 ++---- 10 files changed, 97 insertions(+), 132 deletions(-) diff --git a/api-editor/gui/src/features/annotations/annotationSlice.ts b/api-editor/gui/src/features/annotations/annotationSlice.ts index 9382a7ad3..bca5d5f41 100644 --- a/api-editor/gui/src/features/annotations/annotationSlice.ts +++ b/api-editor/gui/src/features/annotations/annotationSlice.ts @@ -1,6 +1,6 @@ -import {createAsyncThunk, createSlice, PayloadAction} from '@reduxjs/toolkit'; +import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit'; import * as idb from 'idb-keyval'; -import {RootState} from '../../app/store'; +import { RootState } from '../../app/store'; export interface AnnotationsState { attributes: { @@ -337,22 +337,17 @@ const initialState: AnnotationsState = { // Thunks -------------------------------------------------------------------------------------------------------------- -export const initializeAnnotations = createAsyncThunk( - 'annotations/initialize', - async () => { - try { - const storedAnnotations = (await idb.get( - 'annotations', - )) as AnnotationsState; - return { - ...initialState, - ...storedAnnotations, - }; - } catch { - return initialState; - } - }, -); +export const initializeAnnotations = createAsyncThunk('annotations/initialize', async () => { + try { + const storedAnnotations = (await idb.get('annotations')) as AnnotationsState; + return { + ...initialState, + ...storedAnnotations, + }; + } catch { + return initialState; + } +}); // Slice --------------------------------------------------------------------------------------------------------------- @@ -382,18 +377,11 @@ const annotationsSlice = createSlice({ if (!state.calledAfters[action.payload.target]) { state.calledAfters[action.payload.target] = {}; } - state.calledAfters[action.payload.target][ - action.payload.calledAfterName - ] = action.payload; + state.calledAfters[action.payload.target][action.payload.calledAfterName] = action.payload; }, removeCalledAfter(state, action: PayloadAction) { - delete state.calledAfters[action.payload.target][ - action.payload.calledAfterName - ]; - if ( - Object.keys(state.calledAfters[action.payload.target]) - .length === 0 - ) { + delete state.calledAfters[action.payload.target][action.payload.calledAfterName]; + if (Object.keys(state.calledAfters[action.payload.target]).length === 0) { delete state.calledAfters[action.payload.target]; } }, @@ -415,16 +403,13 @@ const annotationsSlice = createSlice({ } else { const targetGroups = state.groups[action.payload.target]; const otherGroupNames = Object.values(targetGroups) - .filter( - (group) => group.groupName !== action.payload.groupName, - ) + .filter((group) => group.groupName !== action.payload.groupName) .map((group) => group.groupName); for (const nameOfGroup of otherGroupNames) { let needsChange = false; const group = targetGroups[nameOfGroup]; - const currentAnnotationParameter = - action.payload.parameters; + const currentAnnotationParameter = action.payload.parameters; const currentGroupParameter = [...group.parameters]; for (const parameter of currentAnnotationParameter) { const index = currentGroupParameter.indexOf(parameter); @@ -447,13 +432,10 @@ const annotationsSlice = createSlice({ } } } - state.groups[action.payload.target][action.payload.groupName] = - action.payload; + state.groups[action.payload.target][action.payload.groupName] = action.payload; }, removeGroup(state, action: PayloadAction) { - delete state.groups[action.payload.target][ - action.payload.groupName - ]; + delete state.groups[action.payload.target][action.payload.groupName]; if (Object.keys(state.groups[action.payload.target]).length === 0) { delete state.groups[action.payload.target]; } @@ -506,10 +488,7 @@ const annotationsSlice = createSlice({ target: action.payload, }; }, - showCalledAfterAnnotationForm( - state, - action: PayloadAction, - ) { + showCalledAfterAnnotationForm(state, action: PayloadAction) { state.currentUserAction = { type: 'calledAfter', target: action.payload.target, @@ -564,14 +543,11 @@ const annotationsSlice = createSlice({ }, }, extraReducers(builder) { - builder.addCase( - initializeAnnotations.fulfilled, - (state, action) => action.payload, - ); + builder.addCase(initializeAnnotations.fulfilled, (state, action) => action.payload); }, }); -const {actions, reducer} = annotationsSlice; +const { actions, reducer } = annotationsSlice; export const { set: setAnnotations, reset: resetAnnotations, @@ -620,53 +596,52 @@ export default reducer; export const selectAnnotations = (state: RootState) => state.annotations; export const selectAttribute = (target: string) => - (state: RootState): AttributeAnnotation | undefined => - selectAnnotations(state).attributes[target]; + (state: RootState): AttributeAnnotation | undefined => + selectAnnotations(state).attributes[target]; export const selectBoundary = (target: string) => - (state: RootState): BoundaryAnnotation | undefined => - selectAnnotations(state).boundaries[target]; + (state: RootState): BoundaryAnnotation | undefined => + selectAnnotations(state).boundaries[target]; export const selectCalledAfters = (target: string) => - (state: RootState): { [calledAfter: string]: CalledAfterAnnotation } => - selectAnnotations(state).calledAfters[target] ?? {}; + (state: RootState): { [calledAfter: string]: CalledAfterAnnotation } => + selectAnnotations(state).calledAfters[target] ?? {}; export const selectConstant = (target: string) => - (state: RootState): ConstantAnnotation | undefined => - selectAnnotations(state).constants[target]; -export const selectCurrentUserAction = (state: RootState): UserAction => - selectAnnotations(state).currentUserAction; + (state: RootState): ConstantAnnotation | undefined => + selectAnnotations(state).constants[target]; +export const selectCurrentUserAction = (state: RootState): UserAction => selectAnnotations(state).currentUserAction; export const selectEnum = (target: string) => - (state: RootState): EnumAnnotation | undefined => - selectAnnotations(state).enums[target]; + (state: RootState): EnumAnnotation | undefined => + selectAnnotations(state).enums[target]; export const selectGroups = (target: string) => - (state: RootState): { [groupName: string]: GroupAnnotation } => - selectAnnotations(state).groups[target] ?? {}; + (state: RootState): { [groupName: string]: GroupAnnotation } => + selectAnnotations(state).groups[target] ?? {}; export const selectMove = (target: string) => - (state: RootState): MoveAnnotation | undefined => - selectAnnotations(state).moves[target]; + (state: RootState): MoveAnnotation | undefined => + selectAnnotations(state).moves[target]; export const selectOptional = (target: string) => - (state: RootState): OptionalAnnotation | undefined => - selectAnnotations(state).optionals[target]; + (state: RootState): OptionalAnnotation | undefined => + selectAnnotations(state).optionals[target]; export const selectPure = (target: string) => - (state: RootState): PureAnnotation | undefined => - selectAnnotations(state).pures[target]; + (state: RootState): PureAnnotation | undefined => + selectAnnotations(state).pures[target]; export const selectRenaming = (target: string) => - (state: RootState): RenameAnnotation | undefined => - selectAnnotations(state).renamings[target]; + (state: RootState): RenameAnnotation | undefined => + selectAnnotations(state).renamings[target]; export const selectRequired = (target: string) => - (state: RootState): RequiredAnnotation | undefined => - selectAnnotations(state).requireds[target]; + (state: RootState): RequiredAnnotation | undefined => + selectAnnotations(state).requireds[target]; export const selectShowAnnotationImportDialog = (state: RootState): boolean => selectAnnotations(state).showImportDialog; export const selectUnused = (target: string) => - (state: RootState): UnusedAnnotation | undefined => - selectAnnotations(state).unuseds[target]; + (state: RootState): UnusedAnnotation | undefined => + selectAnnotations(state).unuseds[target]; 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 3c629c237..7553b5ac2 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,9 @@ import PythonFunction from '../PythonFunction'; import PythonModule from '../PythonModule'; import PythonParameter from '../PythonParameter'; import PythonPackage from '../PythonPackage'; -import {isEmptyList} from '../../../../common/util/listOperations'; -import PythonDeclaration from "../PythonDeclaration"; -import {AnnotationsState} from "../../../annotations/annotationSlice"; +import { isEmptyList } from '../../../../common/util/listOperations'; +import PythonDeclaration from '../PythonDeclaration'; +import { AnnotationsState } from '../../../annotations/annotationSlice'; /** * An abstract base class for filters of Python declarations. To create a new filter create a new subclass and override @@ -38,15 +38,15 @@ export default abstract class AbstractPythonFilter { */ shouldKeepDeclaration(pythonDeclaration: PythonDeclaration, annotations: AnnotationsState): boolean { if (pythonDeclaration instanceof PythonModule) { - return this.shouldKeepModule(pythonDeclaration, annotations) + return this.shouldKeepModule(pythonDeclaration, annotations); } else if (pythonDeclaration instanceof PythonClass) { - return this.shouldKeepClass(pythonDeclaration, annotations) + return this.shouldKeepClass(pythonDeclaration, annotations); } else if (pythonDeclaration instanceof PythonFunction) { - return this.shouldKeepFunction(pythonDeclaration, annotations) + return this.shouldKeepFunction(pythonDeclaration, annotations); } else if (pythonDeclaration instanceof PythonParameter) { - return this.shouldKeepParameter(pythonDeclaration, annotations) + return this.shouldKeepParameter(pythonDeclaration, annotations); } else { - return true + return true; } } @@ -56,7 +56,9 @@ export default abstract class AbstractPythonFilter { */ applyToPackage(pythonPackage: PythonPackage, annotations: AnnotationsState): PythonPackage { // Filter modules - const modules = pythonPackage.modules.map((it) => this.applyToModule(it, annotations)).filter((it) => it !== null); + const modules = pythonPackage.modules + .map((it) => this.applyToModule(it, annotations)) + .filter((it) => it !== null); // Create filtered package return new PythonPackage( @@ -78,10 +80,14 @@ export default abstract class AbstractPythonFilter { } // Filter classes - const classes = pythonModule.classes.map((it) => this.applyToClass(it, annotations)).filter((it) => it !== null); + const classes = pythonModule.classes + .map((it) => this.applyToClass(it, annotations)) + .filter((it) => it !== null); // Filter functions - const functions = pythonModule.functions.map((it) => this.applyToFunction(it, annotations)).filter((it) => it !== null); + const functions = pythonModule.functions + .map((it) => this.applyToFunction(it, annotations)) + .filter((it) => it !== null); // Return null if all classes and functions are removed if (isEmptyList(classes) && isEmptyList(functions)) { @@ -109,7 +115,9 @@ export default abstract class AbstractPythonFilter { } // Filter methods - const methods = pythonClass.methods.map((it) => this.applyToFunction(it, annotations)).filter((it) => it !== null); + const methods = pythonClass.methods + .map((it) => this.applyToFunction(it, annotations)) + .filter((it) => it !== null); // Return null if all methods are removed if (isEmptyList(methods)) { @@ -140,8 +148,7 @@ export default abstract class AbstractPythonFilter { } // Filter parameters - const parameters = pythonFunction.parameters - .filter((it) => this.shouldKeepParameter(it, annotations)); + const parameters = pythonFunction.parameters.filter((it) => this.shouldKeepParameter(it, annotations)); // 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 9961f2fc1..c1503b652 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,7 @@ 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'; export default class AnnotationFilter extends AbstractPythonFilter { constructor(readonly type: AnnotationType) { @@ -28,11 +28,12 @@ export default class AnnotationFilter extends AbstractPythonFilter { } shouldKeepDeclaration(declaration: PythonDeclaration, annotations: AnnotationsState): boolean { - const id = declaration.pathAsString() + const id = declaration.pathAsString(); switch (this.type) { case AnnotationType.Any: - return id in annotations['attributes'] || + return ( + id in annotations['attributes'] || id in annotations['boundaries'] || id in annotations['calledAfters'] || id in annotations['constants'] || @@ -43,11 +44,12 @@ export default class AnnotationFilter extends AbstractPythonFilter { id in annotations['pures'] || id in annotations['renamings'] || id in annotations['requireds'] || - id in annotations['unuseds']; + id in annotations['unuseds'] + ); // TODO: check for specific annotations default: - return true + return true; } } } 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 7ddecd5ef..c217b916c 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,7 @@ 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'; /** * Keeps declarations iff all contained filters want to keep it. 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 50e622738..a3b8a3ef0 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,7 @@ 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'; export default class DeclarationTypeFilter extends AbstractPythonFilter { constructor(readonly type: DeclarationType) { 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 6873cf808..38c1b9cde 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,7 @@ 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'; export default class NameFilter extends AbstractPythonFilter { constructor(readonly name: string) { 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 6a14a25aa..005efc368 100644 --- a/api-editor/gui/src/features/packageData/model/filters/NegatedFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/NegatedFilter.ts @@ -3,7 +3,7 @@ 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'; /** * Keeps declarations iff the contained filter wants to remove it. 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 82a515a8c..5e3112789 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,7 @@ 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'; export default class VisibilityFilter extends AbstractPythonFilter { constructor(readonly visibility: Visibility) { 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 52a145eb3..5840a1e7e 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,11 @@ -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'; export function createFilterFromString(text: string): AbstractPythonFilter { const filters: AbstractPythonFilter[] = []; diff --git a/api-editor/gui/src/features/packageData/treeView/TreeNode.tsx b/api-editor/gui/src/features/packageData/treeView/TreeNode.tsx index 92abbd787..fb4be7ad5 100644 --- a/api-editor/gui/src/features/packageData/treeView/TreeNode.tsx +++ b/api-editor/gui/src/features/packageData/treeView/TreeNode.tsx @@ -5,13 +5,10 @@ import { useLocation } from 'react-router'; import { useNavigate } from 'react-router-dom'; import { useAppDispatch, useAppSelector } from '../../../app/hooks'; import PythonDeclaration from '../model/PythonDeclaration'; -import { - selectIsExpandedInTreeView, - toggleIsExpandedInTreeView, -} from '../packageDataSlice'; +import { selectIsExpandedInTreeView, toggleIsExpandedInTreeView } from '../packageDataSlice'; import VisibilityIndicator from './VisibilityIndicator'; -import AbstractPythonFilter from "../model/filters/AbstractPythonFilter"; -import {selectAnnotations} from "../../annotations/annotationSlice"; +import AbstractPythonFilter from '../model/filters/AbstractPythonFilter'; +import { selectAnnotations } from '../../annotations/annotationSlice'; interface TreeNodeProps { declaration: PythonDeclaration; @@ -20,32 +17,19 @@ interface TreeNodeProps { filter: AbstractPythonFilter; } -const TreeNode: React.FC = function ({ - declaration, - icon, - isExpandable, - filter -}) { +const TreeNode: React.FC = function ({ declaration, icon, isExpandable, filter }) { const currentPathname = useLocation().pathname; const navigate = useNavigate(); const dispatch = useAppDispatch(); - const showChildren = useAppSelector( - selectIsExpandedInTreeView(declaration.pathAsString()), - ); - const annotations = useAppSelector(selectAnnotations) + const showChildren = useAppSelector(selectIsExpandedInTreeView(declaration.pathAsString())); + const annotations = useAppSelector(selectAnnotations); const level = levelOf(declaration); const paddingLeft = level === 0 ? '1rem' : `${1 + 0.75 * level}rem`; - const backgroundColor = isSelected(declaration, currentPathname) - ? 'cornflowerblue' - : undefined; - const color = isSelected(declaration, currentPathname) - ? 'white' - : undefined; - const fontWeight = filter.shouldKeepDeclaration(declaration, annotations) - ? 'bold' - : undefined; + const backgroundColor = isSelected(declaration, currentPathname) ? 'cornflowerblue' : undefined; + const color = isSelected(declaration, currentPathname) ? 'white' : undefined; + const fontWeight = filter.shouldKeepDeclaration(declaration, annotations) ? 'bold' : undefined; const handleClick = () => { dispatch(toggleIsExpandedInTreeView(declaration.pathAsString())); @@ -76,10 +60,7 @@ const levelOf = function (declaration: PythonDeclaration): number { return declaration.path().length - 2; }; -const isSelected = function ( - declaration: PythonDeclaration, - currentPathname: string, -): boolean { +const isSelected = function (declaration: PythonDeclaration, currentPathname: string): boolean { return `/${declaration.pathAsString()}` === currentPathname; }; From fbbdd5abf27641dbd15feab8187baaba26c87133 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Mon, 23 May 2022 19:13:01 +0200 Subject: [PATCH 29/45] feat: update help text --- api-editor/gui/src/common/MenuBar.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/api-editor/gui/src/common/MenuBar.tsx b/api-editor/gui/src/common/MenuBar.tsx index bdacb9e89..38429cf48 100644 --- a/api-editor/gui/src/common/MenuBar.tsx +++ b/api-editor/gui/src/common/MenuBar.tsx @@ -94,17 +94,18 @@ const HelpButton = function () { - is:annotated + annotation:any Displays only elements that have been annotated. - hasAnnotation:xy + annotation:[type] - Displays only elements that are annotated with the given type xy. Possible types: - unused, constant, required, optional, enum and boundary. + 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, @renaming, @required, @unused. From e41b286295ab6dc543398acf3a80d91d636f322c Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Tue, 24 May 2022 10:28:02 +0200 Subject: [PATCH 30/45] style: use === instead of == --- api-editor/gui/src/common/MenuBar.tsx | 2 +- .../packageData/model/filters/DeclarationTypeFilter.ts | 8 ++++---- .../packageData/model/filters/VisibilityFilter.ts | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/api-editor/gui/src/common/MenuBar.tsx b/api-editor/gui/src/common/MenuBar.tsx index 38429cf48..bebbc3e46 100644 --- a/api-editor/gui/src/common/MenuBar.tsx +++ b/api-editor/gui/src/common/MenuBar.tsx @@ -217,7 +217,7 @@ const MenuBar: React.FC = function ({ pythonPackage, filter, setFi }; const settings: string[] = []; - if (colorMode == 'dark') { + if (colorMode === 'dark') { settings.push('darkMode'); } 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 a3b8a3ef0..75413ff53 100644 --- a/api-editor/gui/src/features/packageData/model/filters/DeclarationTypeFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/DeclarationTypeFilter.ts @@ -11,19 +11,19 @@ export default class DeclarationTypeFilter extends AbstractPythonFilter { } shouldKeepModule(pythonModule: PythonModule, annotations: AnnotationsState): boolean { - return this.type == DeclarationType.Module; + return this.type === DeclarationType.Module; } shouldKeepClass(pythonClass: PythonClass, annotations: AnnotationsState): boolean { - return this.type == DeclarationType.Class; + return this.type === DeclarationType.Class; } shouldKeepFunction(pythonFunction: PythonFunction, annotations: AnnotationsState): boolean { - return this.type == DeclarationType.Function; + return this.type === DeclarationType.Function; } shouldKeepParameter(pythonParameter: PythonParameter, annotations: AnnotationsState): boolean { - return this.type == DeclarationType.Parameter; + return this.type === DeclarationType.Parameter; } } 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 5e3112789..f56eadd27 100644 --- a/api-editor/gui/src/features/packageData/model/filters/VisibilityFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/VisibilityFilter.ts @@ -28,7 +28,7 @@ export default class VisibilityFilter extends AbstractPythonFilter { } shouldKeepDeclaration(declaration: PythonDeclaration, annotations: AnnotationsState): boolean { - return declaration.isPublicDeclaration() == (this.visibility == Visibility.Public); + return declaration.isPublicDeclaration() === (this.visibility === Visibility.Public); } } From ae5dbbee9eb6d0cf61f94e78a6663972d99b3667 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Tue, 24 May 2022 10:36:32 +0200 Subject: [PATCH 31/45] feat: filters for specific annotations --- .../model/filters/AnnotationFilter.ts | 75 ++++++++++++------- .../model/filters/filterFactory.ts | 25 ++++++- 2 files changed, 73 insertions(+), 27 deletions(-) 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 c1503b652..936554928 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,7 @@ 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'; export default class AnnotationFilter extends AbstractPythonFilter { constructor(readonly type: AnnotationType) { @@ -33,21 +33,44 @@ export default class AnnotationFilter extends AbstractPythonFilter { switch (this.type) { case AnnotationType.Any: return ( - id in annotations['attributes'] || - id in annotations['boundaries'] || - id in annotations['calledAfters'] || - id in annotations['constants'] || - id in annotations['enums'] || - id in annotations['groups'] || - id in annotations['moves'] || - id in annotations['optionals'] || - id in annotations['pures'] || - id in annotations['renamings'] || - id in annotations['requireds'] || - id in annotations['unuseds'] + id in annotations.attributes || + id in annotations.boundaries || + id in annotations.calledAfters || + id in annotations.constants || + id in annotations.enums || + id in annotations.groups || + id in annotations.moves || + id in annotations.optionals || + id in annotations.pures || + id in annotations.renamings || + id in annotations.requireds || + id in annotations.unuseds ); + case AnnotationType.Attribute: + return id in annotations.attributes; + case AnnotationType.Boundary: + return id in annotations.boundaries; + case AnnotationType.CalledAfter: + return id in annotations.calledAfters; + case AnnotationType.Constant: + return id in annotations.constants; + case AnnotationType.Enum: + return id in annotations.enums; + case AnnotationType.Group: + return id in annotations.groups; + case AnnotationType.Move: + return id in annotations.moves; + case AnnotationType.Optional: + return id in annotations.optionals; + case AnnotationType.Pure: + return id in annotations.pures; + case AnnotationType.Renaming: + return id in annotations.renamings; + case AnnotationType.Required: + return id in annotations.requireds; + case AnnotationType.Unused: + return id in annotations.unuseds; - // TODO: check for specific annotations default: return true; } @@ -56,16 +79,16 @@ export default class AnnotationFilter extends AbstractPythonFilter { export enum AnnotationType { Any, - Attributes, - Boundaries, - CalledAfters, - Constants, - Enums, - Groups, - Moves, - Optionals, - Pures, - Renamings, - Requireds, - Unuseds, + Attribute, + Boundary, + CalledAfter, + Constant, + Enum, + Group, + Move, + Optional, + Pure, + Renaming, + Required, + Unused, } 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 5840a1e7e..97be1d3ee 100644 --- a/api-editor/gui/src/features/packageData/model/filters/filterFactory.ts +++ b/api-editor/gui/src/features/packageData/model/filters/filterFactory.ts @@ -54,7 +54,30 @@ function parsePositiveToken(token: string): Optional { // Annotations case 'annotation:any': return new AnnotationFilter(AnnotationType.Any); - // TODO other cases like 'annotation:@boundary' + case 'annotation:@attribute': + return new AnnotationFilter(AnnotationType.Attribute); + case 'annotation:@boundary': + return new AnnotationFilter(AnnotationType.Boundary); + case 'annotation:@calledAfter': + return new AnnotationFilter(AnnotationType.CalledAfter); + case 'annotation:@constant': + return new AnnotationFilter(AnnotationType.Constant); + case 'annotation:@enum': + return new AnnotationFilter(AnnotationType.Enum); + case 'annotation:@group': + return new AnnotationFilter(AnnotationType.Group); + case 'annotation:@move': + return new AnnotationFilter(AnnotationType.Move); + case 'annotation:@optional': + return new AnnotationFilter(AnnotationType.Optional); + case 'annotation:@pure': + return new AnnotationFilter(AnnotationType.Pure); + case 'annotation:@renaming': + return new AnnotationFilter(AnnotationType.Renaming); + case 'annotation:@required': + return new AnnotationFilter(AnnotationType.Required); + case 'annotation:@unused': + return new AnnotationFilter(AnnotationType.Unused); } // Name From ceb53b2e4f85f9036b23f5162ec320db56d9da09 Mon Sep 17 00:00:00 2001 From: lars-reimann Date: Tue, 24 May 2022 08:40:14 +0000 Subject: [PATCH 32/45] style: apply automatic fixes of linters --- .../src/features/packageData/model/filters/AnnotationFilter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 936554928..592d317f4 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,7 @@ 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'; export default class AnnotationFilter extends AbstractPythonFilter { constructor(readonly type: AnnotationType) { From 20289c82aa967d87897277180cbea0f1b44d8ee7 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Tue, 24 May 2022 10:49:08 +0200 Subject: [PATCH 33/45] docs: add code documentation for filters --- .../model/filters/AbstractPythonFilter.ts | 2 +- .../packageData/model/filters/AnnotationFilter.ts | 8 ++++++++ .../model/filters/ConjunctiveFilter.ts | 6 +++++- .../model/filters/DeclarationTypeFilter.ts | 7 +++++++ .../packageData/model/filters/NameFilter.ts | 11 +++++++++-- .../packageData/model/filters/NegatedFilter.ts | 6 +++++- .../packageData/model/filters/VisibilityFilter.ts | 7 +++++++ .../packageData/model/filters/filterFactory.ts | 15 +++++++++++++++ 8 files changed, 57 insertions(+), 5 deletions(-) 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 7553b5ac2..783a1d393 100644 --- a/api-editor/gui/src/features/packageData/model/filters/AbstractPythonFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/AbstractPythonFilter.ts @@ -9,7 +9,7 @@ import { AnnotationsState } from '../../../annotations/annotationSlice'; /** * An abstract base class for filters of Python declarations. To create a new filter create a new subclass and override - * the shouldKeepXXX methods. + * the abstract shouldKeepXXX methods. */ export default abstract class AbstractPythonFilter { /** 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 592d317f4..7975c8ed1 100644 --- a/api-editor/gui/src/features/packageData/model/filters/AnnotationFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/AnnotationFilter.ts @@ -6,7 +6,15 @@ import AbstractPythonFilter from './AbstractPythonFilter'; import PythonDeclaration from '../PythonDeclaration'; import { AnnotationsState } from '../../../annotations/annotationSlice'; +/** + * Keeps only declarations with either an arbitrary or a specific annotation. + */ export default class AnnotationFilter extends AbstractPythonFilter { + + /** + * @param type The annotations to look for. If this is set to `AnnotationType.Any` all annotated declarations are + * kept. For other values only declarations with the specified annotation are kept. + */ constructor(readonly type: AnnotationType) { super(); } 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 c217b916c..f0b7f4e50 100644 --- a/api-editor/gui/src/features/packageData/model/filters/ConjunctiveFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/ConjunctiveFilter.ts @@ -6,9 +6,13 @@ import PythonModule from '../PythonModule'; import { AnnotationsState } from '../../../annotations/annotationSlice'; /** - * Keeps declarations iff all contained filters want to keep it. + * Keeps declarations iff all contained filters keep it. */ export class ConjunctiveFilter extends AbstractPythonFilter { + + /** + * @param filters The filters that should all be applied. + */ constructor(readonly filters: AbstractPythonFilter[]) { super(); } 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 75413ff53..f4c85cb9b 100644 --- a/api-editor/gui/src/features/packageData/model/filters/DeclarationTypeFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/DeclarationTypeFilter.ts @@ -5,7 +5,14 @@ import PythonParameter from '../PythonParameter'; import AbstractPythonFilter from './AbstractPythonFilter'; import { AnnotationsState } from '../../../annotations/annotationSlice'; +/** + * Keeps only declarations of a specified type (module/class/function/parameter). + */ export default class DeclarationTypeFilter extends AbstractPythonFilter { + + /** + * @param type Which declarations to keep. + */ constructor(readonly type: DeclarationType) { super(); } 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 38c1b9cde..770f796da 100644 --- a/api-editor/gui/src/features/packageData/model/filters/NameFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/NameFilter.ts @@ -6,8 +6,15 @@ import AbstractPythonFilter from './AbstractPythonFilter'; import PythonDeclaration from '../PythonDeclaration'; import { AnnotationsState } from '../../../annotations/annotationSlice'; +/** + * Keeps only declarations that have a given string in their name. + */ export default class NameFilter extends AbstractPythonFilter { - constructor(readonly name: string) { + + /** + * @param substring The string that must be part of the name of the declaration. + */ + constructor(readonly substring: string) { super(); } @@ -28,6 +35,6 @@ export default class NameFilter extends AbstractPythonFilter { } shouldKeepDeclaration(declaration: PythonDeclaration, annotations: AnnotationsState): boolean { - return declaration.name.toLowerCase().includes(this.name.toLowerCase()); + return declaration.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 005efc368..9f44ca42f 100644 --- a/api-editor/gui/src/features/packageData/model/filters/NegatedFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/NegatedFilter.ts @@ -6,9 +6,13 @@ import PythonModule from '../PythonModule'; import { AnnotationsState } from '../../../annotations/annotationSlice'; /** - * Keeps declarations iff the contained filter wants to remove it. + * Keeps declarations iff the contained filter discards it. */ export class NegatedFilter extends AbstractPythonFilter { + + /** + * @param filter The filter to negate. + */ constructor(readonly filter: AbstractPythonFilter) { super(); } 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 f56eadd27..174b349f8 100644 --- a/api-editor/gui/src/features/packageData/model/filters/VisibilityFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/VisibilityFilter.ts @@ -6,7 +6,14 @@ import AbstractPythonFilter from './AbstractPythonFilter'; import PythonDeclaration from '../PythonDeclaration'; import { AnnotationsState } from '../../../annotations/annotationSlice'; +/** + * Keeps only declarations with a specified visibility (public/internal) + */ export default class VisibilityFilter extends AbstractPythonFilter { + + /** + * @param visibility The visibility of the declaration to keep. + */ constructor(readonly visibility: Visibility) { super(); } 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 97be1d3ee..c2e4e6eb1 100644 --- a/api-editor/gui/src/features/packageData/model/filters/filterFactory.ts +++ b/api-editor/gui/src/features/packageData/model/filters/filterFactory.ts @@ -7,6 +7,11 @@ import { NegatedFilter } from './NegatedFilter'; import { Optional } from '../../../../common/util/types'; import AnnotationFilter, { AnnotationType } from './AnnotationFilter'; +/** + * Creates a filter from the given string. This method handles conjunctions, negations, and non-negated tokens. + * + * @param text The text that describes the filter. + */ export function createFilterFromString(text: string): AbstractPythonFilter { const filters: AbstractPythonFilter[] = []; @@ -20,6 +25,11 @@ export function createFilterFromString(text: string): AbstractPythonFilter { return new ConjunctiveFilter(filters); } +/** + * Handles a single token that could be negated. + * + * @param token The text that describes the filter. + */ function parsePotentiallyNegatedToken(token: string): Optional { const isNegated = token.startsWith('!'); const positiveToken = isNegated ? token.substring(1) : token; @@ -32,6 +42,11 @@ function parsePotentiallyNegatedToken(token: string): Optional { // Filters with fixed text switch (token.toLowerCase()) { From 82583a3be15795b343c1eb4d15d892b9350634fc Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Tue, 24 May 2022 10:52:09 +0200 Subject: [PATCH 34/45] fix: filter text --- .../features/packageData/model/filters/AnnotationFilter.ts | 4 ++-- .../src/features/packageData/model/filters/filterFactory.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) 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 7975c8ed1..990960c22 100644 --- a/api-editor/gui/src/features/packageData/model/filters/AnnotationFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/AnnotationFilter.ts @@ -72,7 +72,7 @@ export default class AnnotationFilter extends AbstractPythonFilter { return id in annotations.optionals; case AnnotationType.Pure: return id in annotations.pures; - case AnnotationType.Renaming: + case AnnotationType.Rename: return id in annotations.renamings; case AnnotationType.Required: return id in annotations.requireds; @@ -96,7 +96,7 @@ export enum AnnotationType { Move, Optional, Pure, - Renaming, + Rename, Required, Unused, } 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 c2e4e6eb1..a28290069 100644 --- a/api-editor/gui/src/features/packageData/model/filters/filterFactory.ts +++ b/api-editor/gui/src/features/packageData/model/filters/filterFactory.ts @@ -87,8 +87,8 @@ function parsePositiveToken(token: string): Optional { return new AnnotationFilter(AnnotationType.Optional); case 'annotation:@pure': return new AnnotationFilter(AnnotationType.Pure); - case 'annotation:@renaming': - return new AnnotationFilter(AnnotationType.Renaming); + case 'annotation:@rename': + return new AnnotationFilter(AnnotationType.Rename); case 'annotation:@required': return new AnnotationFilter(AnnotationType.Required); case 'annotation:@unused': From 8abc2ea05420078fad4d4782aa3e2486c66cead3 Mon Sep 17 00:00:00 2001 From: lars-reimann Date: Tue, 24 May 2022 08:55:42 +0000 Subject: [PATCH 35/45] style: apply automatic fixes of linters --- .../src/features/packageData/model/filters/AnnotationFilter.ts | 1 - .../src/features/packageData/model/filters/ConjunctiveFilter.ts | 1 - .../features/packageData/model/filters/DeclarationTypeFilter.ts | 1 - .../gui/src/features/packageData/model/filters/NameFilter.ts | 1 - .../gui/src/features/packageData/model/filters/NegatedFilter.ts | 1 - .../src/features/packageData/model/filters/VisibilityFilter.ts | 1 - 6 files changed, 6 deletions(-) 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 990960c22..99bfcf2cd 100644 --- a/api-editor/gui/src/features/packageData/model/filters/AnnotationFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/AnnotationFilter.ts @@ -10,7 +10,6 @@ import { AnnotationsState } from '../../../annotations/annotationSlice'; * Keeps only declarations with either an arbitrary or a specific annotation. */ export default class AnnotationFilter extends AbstractPythonFilter { - /** * @param type The annotations to look for. If this is set to `AnnotationType.Any` all annotated declarations are * kept. For other values only declarations with the specified annotation are kept. 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 f0b7f4e50..ddbe181b1 100644 --- a/api-editor/gui/src/features/packageData/model/filters/ConjunctiveFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/ConjunctiveFilter.ts @@ -9,7 +9,6 @@ import { AnnotationsState } from '../../../annotations/annotationSlice'; * Keeps declarations iff all contained filters keep it. */ export class ConjunctiveFilter extends AbstractPythonFilter { - /** * @param filters The filters that should all be applied. */ 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 f4c85cb9b..b337216f2 100644 --- a/api-editor/gui/src/features/packageData/model/filters/DeclarationTypeFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/DeclarationTypeFilter.ts @@ -9,7 +9,6 @@ import { AnnotationsState } from '../../../annotations/annotationSlice'; * Keeps only declarations of a specified type (module/class/function/parameter). */ export default class DeclarationTypeFilter extends AbstractPythonFilter { - /** * @param type Which declarations to keep. */ 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 770f796da..6e1fb4350 100644 --- a/api-editor/gui/src/features/packageData/model/filters/NameFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/NameFilter.ts @@ -10,7 +10,6 @@ import { AnnotationsState } from '../../../annotations/annotationSlice'; * Keeps only declarations that have a given string in their name. */ export default class NameFilter extends AbstractPythonFilter { - /** * @param substring The string that must be part of the name of the declaration. */ 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 9f44ca42f..f4a2d4a57 100644 --- a/api-editor/gui/src/features/packageData/model/filters/NegatedFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/NegatedFilter.ts @@ -9,7 +9,6 @@ import { AnnotationsState } from '../../../annotations/annotationSlice'; * Keeps declarations iff the contained filter discards it. */ export class NegatedFilter extends AbstractPythonFilter { - /** * @param filter The filter to negate. */ 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 174b349f8..c8d01ce01 100644 --- a/api-editor/gui/src/features/packageData/model/filters/VisibilityFilter.ts +++ b/api-editor/gui/src/features/packageData/model/filters/VisibilityFilter.ts @@ -10,7 +10,6 @@ import { AnnotationsState } from '../../../annotations/annotationSlice'; * Keeps only declarations with a specified visibility (public/internal) */ export default class VisibilityFilter extends AbstractPythonFilter { - /** * @param visibility The visibility of the declaration to keep. */ From 2d81267c8d6623371d2619221a3f65db94bf5d3d Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Wed, 25 May 2022 10:51:26 +0200 Subject: [PATCH 36/45] feat: minor GUI improvements --- api-editor/gui/src/common/MenuBar.tsx | 8 ++++---- .../src/features/annotations/AnnotationImportDialog.tsx | 3 ++- .../src/features/packageData/PackageDataImportDialog.tsx | 3 ++- .../gui/src/features/packageData/treeView/TreeNode.tsx | 1 + api-editor/gui/src/features/usages/UsageImportDialog.tsx | 3 ++- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/api-editor/gui/src/common/MenuBar.tsx b/api-editor/gui/src/common/MenuBar.tsx index bebbc3e46..f39004511 100644 --- a/api-editor/gui/src/common/MenuBar.tsx +++ b/api-editor/gui/src/common/MenuBar.tsx @@ -232,15 +232,15 @@ const MenuBar: React.FC = function ({ pythonPackage, filter, setFi - dispatch(togglePackageDataImportDialog())}>API Data - dispatch(toggleUsageImportDialog())}>Usages - dispatch(toggleAnnotationImportDialog())}> + dispatch(togglePackageDataImportDialog())}>API Data + dispatch(toggleUsageImportDialog())}>Usages + dispatch(toggleAnnotationImportDialog())}> Annotations - Annotations + Annotations diff --git a/api-editor/gui/src/features/annotations/AnnotationImportDialog.tsx b/api-editor/gui/src/features/annotations/AnnotationImportDialog.tsx index 3f3ec890d..5c7760707 100644 --- a/api-editor/gui/src/features/annotations/AnnotationImportDialog.tsx +++ b/api-editor/gui/src/features/annotations/AnnotationImportDialog.tsx @@ -86,7 +86,8 @@ const AnnotationImportDialog: React.FC = function () { - Select an annotation file to import. + Select an annotation file to import. This data will be stored until another + annotation file is imported. diff --git a/api-editor/gui/src/features/packageData/PackageDataImportDialog.tsx b/api-editor/gui/src/features/packageData/PackageDataImportDialog.tsx index 565dc22d9..f1701bc96 100644 --- a/api-editor/gui/src/features/packageData/PackageDataImportDialog.tsx +++ b/api-editor/gui/src/features/packageData/PackageDataImportDialog.tsx @@ -83,7 +83,8 @@ const PackageDataImportDialog: React.FC = - Select an API data file to import. + Select an API data file to import. This data will be stored until another + API data file is imported. diff --git a/api-editor/gui/src/features/packageData/treeView/TreeNode.tsx b/api-editor/gui/src/features/packageData/treeView/TreeNode.tsx index fb4be7ad5..d42ddfd00 100644 --- a/api-editor/gui/src/features/packageData/treeView/TreeNode.tsx +++ b/api-editor/gui/src/features/packageData/treeView/TreeNode.tsx @@ -29,6 +29,7 @@ const TreeNode: React.FC = function ({ declaration, icon, isExpan const paddingLeft = level === 0 ? '1rem' : `${1 + 0.75 * level}rem`; const backgroundColor = isSelected(declaration, currentPathname) ? 'cornflowerblue' : undefined; const color = isSelected(declaration, currentPathname) ? 'white' : undefined; + const fontWeight = filter.shouldKeepDeclaration(declaration, annotations) ? 'bold' : undefined; const handleClick = () => { diff --git a/api-editor/gui/src/features/usages/UsageImportDialog.tsx b/api-editor/gui/src/features/usages/UsageImportDialog.tsx index ea1723bbd..11b8eda38 100644 --- a/api-editor/gui/src/features/usages/UsageImportDialog.tsx +++ b/api-editor/gui/src/features/usages/UsageImportDialog.tsx @@ -71,7 +71,8 @@ const UsageImportDialog: React.FC = function ({ - Select a usage file to import. + Select a usage file to import. This data will be stored until another + usage file is imported. Drag and drop a usage file here or click to select the file. (Only *.json will be accepted.) From 4ee8068c0ca5216f8ddbbbb1eab99400f7e87c4f Mon Sep 17 00:00:00 2001 From: lars-reimann Date: Wed, 25 May 2022 11:30:52 +0000 Subject: [PATCH 37/45] style: apply automatic fixes of linters --- api-editor/gui/src/common/MenuBar.tsx | 12 +- .../annotations/AnnotationImportDialog.tsx | 56 +++---- .../packageData/PackageDataImportDialog.tsx | 155 ++++++++---------- .../src/features/usages/UsageImportDialog.tsx | 6 +- 4 files changed, 109 insertions(+), 120 deletions(-) diff --git a/api-editor/gui/src/common/MenuBar.tsx b/api-editor/gui/src/common/MenuBar.tsx index f39004511..7d08387a4 100644 --- a/api-editor/gui/src/common/MenuBar.tsx +++ b/api-editor/gui/src/common/MenuBar.tsx @@ -232,15 +232,21 @@ const MenuBar: React.FC = function ({ pythonPackage, filter, setFi - dispatch(togglePackageDataImportDialog())}>API Data - dispatch(toggleUsageImportDialog())}>Usages + dispatch(togglePackageDataImportDialog())}> + API Data + + dispatch(toggleUsageImportDialog())}> + Usages + dispatch(toggleAnnotationImportDialog())}> Annotations - Annotations + + Annotations + diff --git a/api-editor/gui/src/features/annotations/AnnotationImportDialog.tsx b/api-editor/gui/src/features/annotations/AnnotationImportDialog.tsx index 5c7760707..a1c4369f2 100644 --- a/api-editor/gui/src/features/annotations/AnnotationImportDialog.tsx +++ b/api-editor/gui/src/features/annotations/AnnotationImportDialog.tsx @@ -26,26 +26,25 @@ import { const AnnotationImportDialog: React.FC = function () { const [fileName, setFileName] = useState(''); - const [newAnnotationStore, setNewAnnotationStore] = - useState({ - attributes: {}, - boundaries: {}, - constants: {}, - calledAfters: {}, - currentUserAction: { - target: '', - type: 'none', - }, - enums: {}, - groups: {}, - moves: {}, - optionals: {}, - pures: {}, - renamings: {}, - requireds: {}, - showImportDialog: false, - unuseds: {}, - }); + const [newAnnotationStore, setNewAnnotationStore] = useState({ + attributes: {}, + boundaries: {}, + constants: {}, + calledAfters: {}, + currentUserAction: { + target: '', + type: 'none', + }, + enums: {}, + groups: {}, + moves: {}, + optionals: {}, + pures: {}, + renamings: {}, + requireds: {}, + showImportDialog: false, + unuseds: {}, + }); const dispatch = useAppDispatch(); const submit = () => { @@ -66,9 +65,7 @@ const AnnotationImportDialog: React.FC = function () { const reader = new FileReader(); reader.onload = () => { if (typeof reader.result === 'string') { - const readAnnotationJson = JSON.parse( - reader.result, - ) as AnnotationsState; + const readAnnotationJson = JSON.parse(reader.result) as AnnotationsState; setNewAnnotationStore(readAnnotationJson); } }; @@ -86,17 +83,12 @@ const AnnotationImportDialog: React.FC = function () { - Select an annotation file to import. This data will be stored until another - annotation file is imported. + Select an annotation file to import. This data will be stored until another annotation file + is imported. - - Drag and drop an annotation file here or click - to select the file. - - - (only *.json will be accepted) - + Drag and drop an annotation file here or click to select the file. + (only *.json will be accepted) {fileName && ( diff --git a/api-editor/gui/src/features/packageData/PackageDataImportDialog.tsx b/api-editor/gui/src/features/packageData/PackageDataImportDialog.tsx index f1701bc96..32c13dd25 100644 --- a/api-editor/gui/src/features/packageData/PackageDataImportDialog.tsx +++ b/api-editor/gui/src/features/packageData/PackageDataImportDialog.tsx @@ -22,10 +22,7 @@ import { Setter } from '../../common/util/types'; import { isValidJsonFile } from '../../common/util/validation'; import { resetAnnotations } from '../annotations/annotationSlice'; import PythonPackage from './model/PythonPackage'; -import { - parsePythonPackageJson, - PythonPackageJson, -} from './model/PythonPackageBuilder'; +import { parsePythonPackageJson, PythonPackageJson } from './model/PythonPackageBuilder'; import { togglePackageDataImportDialog } from './packageDataSlice'; interface ImportPythonPackageDialogProps { @@ -33,90 +30,82 @@ interface ImportPythonPackageDialogProps { setFilter: Setter; } -const PackageDataImportDialog: React.FC = - function ({ setFilter, setPythonPackage }) { - const [fileName, setFileName] = useState(''); - const [newPythonPackage, setNewPythonPackage] = useState(); - const navigate = useNavigate(); - const dispatch = useAppDispatch(); +const PackageDataImportDialog: React.FC = function ({ setFilter, setPythonPackage }) { + const [fileName, setFileName] = useState(''); + const [newPythonPackage, setNewPythonPackage] = useState(); + const navigate = useNavigate(); + const dispatch = useAppDispatch(); - const submit = async () => { - if (newPythonPackage) { - const parsedPythonPackage = JSON.parse( - newPythonPackage, - ) as PythonPackageJson; - setPythonPackage(parsePythonPackageJson(parsedPythonPackage)); - setFilter(''); - navigate('/'); + const submit = async () => { + if (newPythonPackage) { + const parsedPythonPackage = JSON.parse(newPythonPackage) as PythonPackageJson; + setPythonPackage(parsePythonPackageJson(parsedPythonPackage)); + setFilter(''); + navigate('/'); - await idb.set('package', parsedPythonPackage); - } - close(); - }; - const close = () => dispatch(togglePackageDataImportDialog()); + await idb.set('package', parsedPythonPackage); + } + close(); + }; + const close = () => dispatch(togglePackageDataImportDialog()); - const slurpAndParse = (acceptedFiles: File[]) => { - if (isValidJsonFile(acceptedFiles[acceptedFiles.length - 1].name)) { - if (acceptedFiles.length > 1) { - // eslint-disable-next-line no-param-reassign - acceptedFiles = [acceptedFiles[acceptedFiles.length - 1]]; - } - setFileName(acceptedFiles[0].name); - const reader = new FileReader(); - reader.onload = () => { - if (typeof reader.result === 'string') { - setNewPythonPackage(reader.result); - dispatch(resetAnnotations()); - } - }; - reader.readAsText(acceptedFiles[0]); + const slurpAndParse = (acceptedFiles: File[]) => { + if (isValidJsonFile(acceptedFiles[acceptedFiles.length - 1].name)) { + if (acceptedFiles.length > 1) { + // eslint-disable-next-line no-param-reassign + acceptedFiles = [acceptedFiles[acceptedFiles.length - 1]]; } - }; + setFileName(acceptedFiles[0].name); + const reader = new FileReader(); + reader.onload = () => { + if (typeof reader.result === 'string') { + setNewPythonPackage(reader.result); + dispatch(resetAnnotations()); + } + }; + reader.readAsText(acceptedFiles[0]); + } + }; - return ( - - - - - Import API data - - - - - Select an API data file to import. This data will be stored until another - API data file is imported. - - - - Drag and drop an API data file here or click - to select the file. - - - (Only *.json will be accepted.) - - + return ( + + + + + Import API data + + + + + Select an API data file to import. This data will be stored until another API data file is + imported. + + + Drag and drop an API data file here or click to select the file. + (Only *.json will be accepted.) + - {fileName && ( - - Imported file: - {fileName} - - )} - - - - - - - - - - - ); - }; + {fileName && ( + + Imported file: + {fileName} + + )} + + + + + + + + + + + ); +}; export default PackageDataImportDialog; diff --git a/api-editor/gui/src/features/usages/UsageImportDialog.tsx b/api-editor/gui/src/features/usages/UsageImportDialog.tsx index 11b8eda38..8eadd1ba7 100644 --- a/api-editor/gui/src/features/usages/UsageImportDialog.tsx +++ b/api-editor/gui/src/features/usages/UsageImportDialog.tsx @@ -71,8 +71,10 @@ const UsageImportDialog: React.FC = function ({ - Select a usage file to import. This data will be stored until another - usage file is imported. + + Select a usage file to import. This data will be stored until another usage file is + imported. + Drag and drop a usage file here or click to select the file. (Only *.json will be accepted.) From 5481ae9e323fb284a10316469ed7d84db82f2bbf Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Wed, 25 May 2022 13:35:56 +0200 Subject: [PATCH 38/45] refactor: remove commented out code --- api-editor/gui/src/common/MenuBar.tsx | 99 +++++++++------------------ 1 file changed, 32 insertions(+), 67 deletions(-) diff --git a/api-editor/gui/src/common/MenuBar.tsx b/api-editor/gui/src/common/MenuBar.tsx index 7d08387a4..ad5dc5694 100644 --- a/api-editor/gui/src/common/MenuBar.tsx +++ b/api-editor/gui/src/common/MenuBar.tsx @@ -13,7 +13,6 @@ import { Icon, IconButton, Input, - InputGroup, ListItem, Menu, MenuButton, @@ -36,15 +35,15 @@ import { 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'; interface MenuBarProps { pythonPackage: PythonPackage; @@ -58,11 +57,11 @@ const HelpButton = function () { - } aria-label="help" /> + } aria-label="help"/> - - + + Filter Options @@ -176,9 +175,8 @@ const DeleteAllAnnotations = function () { ); }; -const MenuBar: React.FC = function ({ pythonPackage, filter, setFilter, displayInferErrors }) { - const { colorMode, toggleColorMode } = useColorMode(); - const initialFocusRef = useRef(null); +const MenuBar: React.FC = function ({pythonPackage, filter, setFilter, displayInferErrors}) { + const {colorMode, toggleColorMode} = useColorMode(); const dispatch = useAppDispatch(); const annotationStore = useAppSelector((state) => state.annotations); @@ -199,7 +197,7 @@ const MenuBar: React.FC = function ({ pythonPackage, filter, setFi const requestOptions = { method: 'POST', - headers: { 'Content-Type': 'application/json' }, + headers: {'Content-Type': 'application/json'}, body: JSON.stringify(annotatedPythonPackage), }; fetch('/api-editor/infer', requestOptions).then(async (response) => { @@ -227,37 +225,33 @@ const MenuBar: React.FC = function ({ pythonPackage, filter, setFi {/* Box gets rid of popper.js warning "CSS margin styles cannot be used" */} - }> + }> File - dispatch(togglePackageDataImportDialog())}> - API Data - - dispatch(toggleUsageImportDialog())}> - Usages - + dispatch(togglePackageDataImportDialog())}>API + Data + dispatch(toggleUsageImportDialog())}>Usages dispatch(toggleAnnotationImportDialog())}> Annotations - + - - Annotations - + Annotations - + - }> + }> Settings @@ -271,47 +265,18 @@ const MenuBar: React.FC = function ({ pythonPackage, filter, setFi - + - - - - - setFilter(event.target.value)} - // isInvalid={!PythonFilter.fromFilterBoxInput(filter)} - // borderColor={ - // PythonFilter.fromFilterBoxInput(filter)?.isFilteringModules() - // ? 'green' - // : 'inherit' - // } - spellCheck={false} - minWidth="400px" - /> - {/*{PythonFilter.fromFilterBoxInput(filter)?.isFilteringModules() && (*/} - {/* */} - {/* */} - {/* */} - {/*)}*/} - - - - - Each scope must only be used once. - - - - + setFilter(event.target.value)} + spellCheck={false} + minWidth="400px" + /> + ); From 82f33b7de1b2190551a20c2ff739e0a434a49f38 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Wed, 25 May 2022 13:36:22 +0200 Subject: [PATCH 39/45] feat: compute which modules are public --- api-editor/gui/src/features/packageData/model/PythonModule.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/api-editor/gui/src/features/packageData/model/PythonModule.ts b/api-editor/gui/src/features/packageData/model/PythonModule.ts index 59ebad0ff..45d8287d3 100644 --- a/api-editor/gui/src/features/packageData/model/PythonModule.ts +++ b/api-editor/gui/src/features/packageData/model/PythonModule.ts @@ -29,6 +29,10 @@ export default class PythonModule extends PythonDeclaration { }); } + isPublicDeclaration(): boolean { + return !this.name.split(".").some((it) => it.startsWith("_")) + } + parent(): Optional { return this.containingPackage; } From 92fe9b3857205624617efc7dab844af39488b493 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Wed, 25 May 2022 13:46:14 +0200 Subject: [PATCH 40/45] feat: emphasize important parts in help text --- api-editor/gui/src/common/MenuBar.tsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/api-editor/gui/src/common/MenuBar.tsx b/api-editor/gui/src/common/MenuBar.tsx index ad5dc5694..22c99ede5 100644 --- a/api-editor/gui/src/common/MenuBar.tsx +++ b/api-editor/gui/src/common/MenuBar.tsx @@ -70,8 +70,7 @@ const HelpButton = function () { is:[type] - Displays only elements that are of the given type. Replace [type] with one of - module, class, function, parameter. + Displays only elements that are of the given type. Replace [type] with one of module, class, function, parameter. @@ -80,7 +79,7 @@ const HelpButton = function () { Displays only elements that have the given visibility. Replace [visibility] with one - of public, internal. + of public, internal. @@ -103,8 +102,8 @@ const HelpButton = function () { 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, @renaming, @required, @unused. + with one of @attribute, @boundary, @calledAfter, @constant, @enum, @group, @move, + @optional, @pure, @renaming, @required, @unused. From a0dc4eee9223418e5e236f2e599b4733221c5e01 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Wed, 25 May 2022 15:38:09 +0200 Subject: [PATCH 41/45] fix: inability to add annotation to parameters of functions if they were at index 0 after filtering --- .../gui/src/features/packageData/model/PythonPackageBuilder.ts | 3 ++- .../gui/src/features/packageData/model/PythonParameter.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/api-editor/gui/src/features/packageData/model/PythonPackageBuilder.ts b/api-editor/gui/src/features/packageData/model/PythonPackageBuilder.ts index ed1aaf999..349907f13 100644 --- a/api-editor/gui/src/features/packageData/model/PythonPackageBuilder.ts +++ b/api-editor/gui/src/features/packageData/model/PythonPackageBuilder.ts @@ -199,7 +199,7 @@ interface PythonParameterJson { } const parsePythonParameterJson = function ( - parameterJson: PythonParameterJson, + parameterJson: PythonParameterJson, index: number ): PythonParameter { return new PythonParameter( parameterJson.name, @@ -208,6 +208,7 @@ const parsePythonParameterJson = function ( parameterJson.is_public, parameterJson.docstring.type ?? '', parameterJson.docstring.description ?? '', + index ); }; diff --git a/api-editor/gui/src/features/packageData/model/PythonParameter.ts b/api-editor/gui/src/features/packageData/model/PythonParameter.ts index 230496ba0..33facda6b 100644 --- a/api-editor/gui/src/features/packageData/model/PythonParameter.ts +++ b/api-editor/gui/src/features/packageData/model/PythonParameter.ts @@ -22,6 +22,7 @@ export default class PythonParameter extends PythonDeclaration { readonly isPublic: boolean = false, readonly typeInDocs: string = '', readonly description: string = '', + readonly index: number = 0, ) { super(); @@ -52,7 +53,7 @@ export default class PythonParameter extends PythonDeclaration { } // This is parameter of a method but not the first - return containingFunction.children()[0] !== this; + return this.index > 0 } toString(): string { From 7581a8570e987dfc6a32b3d2a371bd6269cdbfb8 Mon Sep 17 00:00:00 2001 From: lars-reimann Date: Wed, 25 May 2022 13:42:52 +0000 Subject: [PATCH 42/45] style: apply automatic fixes of linters --- api-editor/gui/src/common/MenuBar.tsx | 63 +++++++++------- .../packageData/model/PythonModule.ts | 2 +- .../packageData/model/PythonPackageBuilder.ts | 71 +++++-------------- .../packageData/model/PythonParameter.ts | 2 +- 4 files changed, 54 insertions(+), 84 deletions(-) diff --git a/api-editor/gui/src/common/MenuBar.tsx b/api-editor/gui/src/common/MenuBar.tsx index 22c99ede5..d53e1e380 100644 --- a/api-editor/gui/src/common/MenuBar.tsx +++ b/api-editor/gui/src/common/MenuBar.tsx @@ -35,15 +35,15 @@ import { 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'; interface MenuBarProps { pythonPackage: PythonPackage; @@ -57,11 +57,11 @@ const HelpButton = function () { - } aria-label="help"/> + } aria-label="help" /> - - + + Filter Options @@ -70,7 +70,8 @@ const HelpButton = function () { is:[type] - Displays only elements that are of the given type. Replace [type] with one of module, class, function, parameter. + Displays only elements that are of the given type. Replace [type] with one of{' '} + module, class, function, parameter. @@ -102,8 +103,12 @@ const HelpButton = function () { 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, @renaming, @required, @unused. + with one of{' '} + + @attribute, @boundary, @calledAfter, @constant, @enum, @group, @move, @optional, + @pure, @renaming, @required, @unused + + . @@ -174,8 +179,8 @@ const DeleteAllAnnotations = function () { ); }; -const MenuBar: React.FC = function ({pythonPackage, filter, setFilter, displayInferErrors}) { - const {colorMode, toggleColorMode} = useColorMode(); +const MenuBar: React.FC = function ({ pythonPackage, filter, setFilter, displayInferErrors }) { + const { colorMode, toggleColorMode } = useColorMode(); const dispatch = useAppDispatch(); const annotationStore = useAppSelector((state) => state.annotations); @@ -196,7 +201,7 @@ const MenuBar: React.FC = function ({pythonPackage, filter, setFil const requestOptions = { method: 'POST', - headers: {'Content-Type': 'application/json'}, + headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(annotatedPythonPackage), }; fetch('/api-editor/infer', requestOptions).then(async (response) => { @@ -224,33 +229,37 @@ const MenuBar: React.FC = function ({pythonPackage, filter, setFil {/* Box gets rid of popper.js warning "CSS margin styles cannot be used" */} - }> + }> File - dispatch(togglePackageDataImportDialog())}>API - Data - dispatch(toggleUsageImportDialog())}>Usages + dispatch(togglePackageDataImportDialog())}> + API Data + + dispatch(toggleUsageImportDialog())}> + Usages + dispatch(toggleAnnotationImportDialog())}> Annotations - + - Annotations + + Annotations + - + - }> + }> Settings @@ -264,7 +273,7 @@ const MenuBar: React.FC = function ({pythonPackage, filter, setFil - + = function ({pythonPackage, filter, setFil spellCheck={false} minWidth="400px" /> - + ); diff --git a/api-editor/gui/src/features/packageData/model/PythonModule.ts b/api-editor/gui/src/features/packageData/model/PythonModule.ts index 45d8287d3..b0f6cde47 100644 --- a/api-editor/gui/src/features/packageData/model/PythonModule.ts +++ b/api-editor/gui/src/features/packageData/model/PythonModule.ts @@ -30,7 +30,7 @@ export default class PythonModule extends PythonDeclaration { } isPublicDeclaration(): boolean { - return !this.name.split(".").some((it) => it.startsWith("_")) + return !this.name.split('.').some((it) => it.startsWith('_')); } parent(): Optional { diff --git a/api-editor/gui/src/features/packageData/model/PythonPackageBuilder.ts b/api-editor/gui/src/features/packageData/model/PythonPackageBuilder.ts index 349907f13..16a83714b 100644 --- a/api-editor/gui/src/features/packageData/model/PythonPackageBuilder.ts +++ b/api-editor/gui/src/features/packageData/model/PythonPackageBuilder.ts @@ -17,21 +17,15 @@ export interface PythonPackageJson { functions: PythonFunctionJson[]; } -export const parsePythonPackageJson = function ( - packageJson: PythonPackageJson, -): PythonPackage { +export const parsePythonPackageJson = function (packageJson: PythonPackageJson): PythonPackage { // Functions const functions = new Map( - packageJson.functions - .map(parsePythonFunctionJson) - .map((it) => [it.uniqueQualifiedName, it]), + packageJson.functions.map(parsePythonFunctionJson).map((it) => [it.uniqueQualifiedName, it]), ); // Classes const classes = new Map( - packageJson.classes - .map((it) => parsePythonClassJson(it, functions)) - .map((it) => [it.qualifiedName, it]), + packageJson.classes.map((it) => parsePythonClassJson(it, functions)).map((it) => [it.qualifiedName, it]), ); return new PythonPackage( @@ -59,9 +53,7 @@ const parsePythonModuleJson = function ( ): PythonModule { return new PythonModule( moduleJson.name, - moduleJson.imports - .map(parsePythonImportJson) - .sort((a, b) => a.module.localeCompare(b.module)), + moduleJson.imports.map(parsePythonImportJson).sort((a, b) => a.module.localeCompare(b.module)), moduleJson.from_imports.map(parsePythonFromImportJson).sort((a, b) => { const moduleComparison = a.module.localeCompare(b.module); if (moduleComparison === 0) { @@ -73,21 +65,11 @@ const parsePythonModuleJson = function ( moduleJson.classes .sort((a, b) => a.localeCompare(b)) .filter((classQualifiedName) => classes.has(classQualifiedName)) - .map( - (classQualifiedName) => - classes.get(classQualifiedName) as PythonClass, - ), + .map((classQualifiedName) => classes.get(classQualifiedName) as PythonClass), moduleJson.functions .sort((a, b) => a.localeCompare(b)) - .filter((functionUniqueQualifiedName) => - functions.has(functionUniqueQualifiedName), - ) - .map( - (functionUniqueQualifiedName) => - functions.get( - functionUniqueQualifiedName, - ) as PythonFunction, - ), + .filter((functionUniqueQualifiedName) => functions.has(functionUniqueQualifiedName)) + .map((functionUniqueQualifiedName) => functions.get(functionUniqueQualifiedName) as PythonFunction), ); }; @@ -96,9 +78,7 @@ interface PythonImportJson { alias: Optional; } -const parsePythonImportJson = function ( - importJson: PythonImportJson, -): PythonImport { +const parsePythonImportJson = function (importJson: PythonImportJson): PythonImport { return new PythonImport(importJson.module, importJson.alias); }; @@ -108,14 +88,8 @@ interface PythonFromImportJson { alias: Optional; } -const parsePythonFromImportJson = function ( - fromImportJson: PythonFromImportJson, -): PythonFromImport { - return new PythonFromImport( - fromImportJson.module, - fromImportJson.declaration, - fromImportJson.alias, - ); +const parsePythonFromImportJson = function (fromImportJson: PythonFromImportJson): PythonFromImport { + return new PythonFromImport(fromImportJson.module, fromImportJson.declaration, fromImportJson.alias); }; interface PythonClassJson { @@ -141,15 +115,8 @@ const parsePythonClassJson = function ( classJson.superclasses, classJson.methods .sort((a, b) => a.localeCompare(b)) - .filter((functionUniqueQualifiedName) => - functions.has(functionUniqueQualifiedName), - ) - .map( - (functionUniqueQualifiedName) => - functions.get( - functionUniqueQualifiedName, - ) as PythonFunction, - ), + .filter((functionUniqueQualifiedName) => functions.has(functionUniqueQualifiedName)) + .map((functionUniqueQualifiedName) => functions.get(functionUniqueQualifiedName) as PythonFunction), classJson.is_public, classJson.description ?? '', classJson.docstring ?? '', @@ -170,9 +137,7 @@ interface PythonFunctionJson { source_code: string; } -const parsePythonFunctionJson = function ( - functionJson: PythonFunctionJson, -): PythonFunction { +const parsePythonFunctionJson = function (functionJson: PythonFunctionJson): PythonFunction { return new PythonFunction( functionJson.name, functionJson.unique_name, @@ -198,9 +163,7 @@ interface PythonParameterJson { }; } -const parsePythonParameterJson = function ( - parameterJson: PythonParameterJson, index: number -): PythonParameter { +const parsePythonParameterJson = function (parameterJson: PythonParameterJson, index: number): PythonParameter { return new PythonParameter( parameterJson.name, parameterJson.default_value, @@ -208,7 +171,7 @@ const parsePythonParameterJson = function ( parameterJson.is_public, parameterJson.docstring.type ?? '', parameterJson.docstring.description ?? '', - index + index, ); }; @@ -232,9 +195,7 @@ interface PythonResultJson { description: Optional; } -const parsePythonResultJson = function ( - resultJson: PythonResultJson, -): PythonResult { +const parsePythonResultJson = function (resultJson: PythonResultJson): PythonResult { return new PythonResult( resultJson.name, resultJson.type, diff --git a/api-editor/gui/src/features/packageData/model/PythonParameter.ts b/api-editor/gui/src/features/packageData/model/PythonParameter.ts index 33facda6b..50bfb5a12 100644 --- a/api-editor/gui/src/features/packageData/model/PythonParameter.ts +++ b/api-editor/gui/src/features/packageData/model/PythonParameter.ts @@ -53,7 +53,7 @@ export default class PythonParameter extends PythonDeclaration { } // This is parameter of a method but not the first - return this.index > 0 + return this.index > 0; } toString(): string { From f58efaba5203ef0c1e108373061a72a7a984fd3a Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Wed, 25 May 2022 16:51:52 +0200 Subject: [PATCH 43/45] test: applyToPackage --- .../features/annotations/annotationSlice.ts | 2 +- .../filters/AbstractPythonFilter.test.ts | 193 ++++++++++++++++++ 2 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 api-editor/gui/src/features/packageData/model/filters/AbstractPythonFilter.test.ts diff --git a/api-editor/gui/src/features/annotations/annotationSlice.ts b/api-editor/gui/src/features/annotations/annotationSlice.ts index bca5d5f41..08684760e 100644 --- a/api-editor/gui/src/features/annotations/annotationSlice.ts +++ b/api-editor/gui/src/features/annotations/annotationSlice.ts @@ -318,7 +318,7 @@ interface RenameUserAction { // Initial state ------------------------------------------------------------------------------------------------------- -const initialState: AnnotationsState = { +export const initialState: AnnotationsState = { attributes: {}, boundaries: {}, calledAfters: {}, 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 new file mode 100644 index 000000000..fcd623cf9 --- /dev/null +++ b/api-editor/gui/src/features/packageData/model/filters/AbstractPythonFilter.test.ts @@ -0,0 +1,193 @@ +// noinspection UnnecessaryLocalVariableJS,DuplicatedCode + +import PythonPackage from "../PythonPackage"; +import PythonParameter from "../PythonParameter"; +import PythonModule from "../PythonModule"; +import PythonClass from "../PythonClass"; +import PythonFunction from "../PythonFunction"; +import NameFilter from "./NameFilter"; +import {initialState} from "../../../annotations/annotationSlice"; +import PythonDeclaration from "../PythonDeclaration"; + +let pythonPackage: PythonPackage; + +beforeEach(() => { + pythonPackage = new PythonPackage( + "test_package", + "test_package", + "1.0.0", + [ + new PythonModule( + "test_module_1", + [], + [], + [ + new PythonClass( + "test_class_1", + "test_module_1.test_class_1", + [], + [], + [ + new PythonFunction( + "test_method_1", + "test_method_1", + "test_module_1.test_class_1.test_method_1", + "test_module_1.test_class_1.test_method_1", + [], + [ + new PythonParameter("test_parameter_1"), + new PythonParameter("test_parameter_2") + ] + ), + new PythonFunction( + "test_method_2", + "test_method_2", + "test_module_1.test_class_1.test_method_2", + "test_module_1.test_class_1.test_method_2" + ) + ] + ), + new PythonClass( + "test_class_2", + "test_module_1.test_class_1", + ) + ], + [ + new PythonFunction( + "test_global_function_1", + "test_global_function_1", + "test_module_1.test_global_function_1", + "test_module_1.test_global_function_1", + [], + [ + new PythonParameter("test_parameter_1"), + new PythonParameter("test_parameter_2") + ] + ), + new PythonFunction( + "test_global_function_2", + "test_global_function_2", + "test_module_1.test_global_function_2", + "test_module_1.test_global_function_2" + ), + ] + ), + new PythonModule( + "test_module_2", + [], + [], + [], + [], + ) + ] + ) +}) + +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 modules = filteredPackage.modules + expect(names(modules)).toEqual(["test_module_1"]) + + const classes = modules[0].classes + expect(names(classes)).toEqual(["test_class_1", "test_class_2"]) + + const methods = classes[0].methods + expect(names(methods)).toEqual(["test_method_1", "test_method_2"]) + + const methodParameters = methods[0].parameters + expect(names(methodParameters)).toEqual(["test_parameter_1", "test_parameter_2"]) + + const globalFunctions = modules[0].functions + expect(names(globalFunctions)).toEqual(["test_global_function_1", "test_global_function_2"]) + + const globalFunctionParameters = globalFunctions[0].parameters + expect(names(globalFunctionParameters)).toEqual(["test_parameter_1", "test_parameter_2"]) + }) + + 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 modules = filteredPackage.modules + expect(names(modules)).toEqual(["test_module_1"]) + + const classes = modules[0].classes + expect(names(classes)).toEqual(["test_class_1"]) + + const methods = classes[0].methods + expect(names(methods)).toEqual(["test_method_1", "test_method_2"]) + + const methodParameters = methods[0].parameters + expect(names(methodParameters)).toEqual(["test_parameter_1", "test_parameter_2"]) + + const globalFunctions = modules[0].functions + expect(names(globalFunctions)).toEqual([]) + }) + + 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 modules = filteredPackage.modules + expect(names(modules)).toEqual(["test_module_1"]) + + const classes = modules[0].classes + expect(names(classes)).toEqual(["test_class_1"]) + + const methods = classes[0].methods + expect(names(methods)).toEqual(["test_method_1"]) + + const methodParameters = methods[0].parameters + expect(names(methodParameters)).toEqual(["test_parameter_1", "test_parameter_2"]) + + const globalFunctions = modules[0].functions + expect(names(globalFunctions)).toEqual([]) + }) + + 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 modules = filteredPackage.modules + expect(names(modules)).toEqual(["test_module_1"]) + + const classes = modules[0].classes + expect(names(classes)).toEqual([]) + + const globalFunctions = modules[0].functions + expect(names(globalFunctions)).toEqual(["test_global_function_1"]) + + const globalFunctionParameters = globalFunctions[0].parameters + expect(names(globalFunctionParameters)).toEqual(["test_parameter_1", "test_parameter_2"]) + }) + + 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 modules = filteredPackage.modules + expect(names(modules)).toEqual(["test_module_1"]) + + const classes = modules[0].classes + expect(names(classes)).toEqual(["test_class_1"]) + + const methods = classes[0].methods + expect(names(methods)).toEqual(["test_method_1"]) + + const methodParameters = methods[0].parameters + expect(names(methodParameters)).toEqual(["test_parameter_1"]) + + const globalFunctions = modules[0].functions + expect(names(globalFunctions)).toEqual(["test_global_function_1"]) + + const globalFunctionParameters = globalFunctions[0].parameters + expect(names(globalFunctionParameters)).toEqual(["test_parameter_1"]) + }) +}) + +function names(declarations: PythonDeclaration[]): string[] { + return declarations.map((it) => it.name) +} From 1720407b712becdb35932377b77d24f419665ddf Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Wed, 25 May 2022 17:04:00 +0200 Subject: [PATCH 44/45] test: createFilterFromString --- .../model/filters/filterFactory.test.ts | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 api-editor/gui/src/features/packageData/model/filters/filterFactory.test.ts 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 new file mode 100644 index 000000000..31471ff40 --- /dev/null +++ b/api-editor/gui/src/features/packageData/model/filters/filterFactory.test.ts @@ -0,0 +1,54 @@ +import {createFilterFromString} from "./filterFactory"; +import {ConjunctiveFilter} from "./ConjunctiveFilter"; +import VisibilityFilter, {Visibility} from "./VisibilityFilter"; +import {NegatedFilter} from "./NegatedFilter"; + +describe("createFilterFromString", () => { + test("handles an empty string", () => { + const completeFilter = createFilterFromString("") + expect(completeFilter).toBeInstanceOf(ConjunctiveFilter) + expect((completeFilter as ConjunctiveFilter).filters).toEqual([]) + }) + + test("handles a single positive token", () => { + const completeFilter = createFilterFromString("is:public") + expect(completeFilter).toBeInstanceOf(ConjunctiveFilter) + expect((completeFilter as ConjunctiveFilter).filters).toHaveLength(1) + + const positiveFilter = (completeFilter as ConjunctiveFilter).filters[0] + expect(positiveFilter).toBeInstanceOf(VisibilityFilter) + expect((positiveFilter as VisibilityFilter).visibility).toEqual(Visibility.Public) + }) + + test("handles a single negated token", () => { + const completeFilter = createFilterFromString("!is:public") + expect(completeFilter).toBeInstanceOf(ConjunctiveFilter) + expect((completeFilter as ConjunctiveFilter).filters).toHaveLength(1) + + const negatedFilter = (completeFilter as ConjunctiveFilter).filters[0] + expect(negatedFilter).toBeInstanceOf(NegatedFilter) + + const positiveFilter = (negatedFilter as NegatedFilter).filter + expect(positiveFilter).toBeInstanceOf(VisibilityFilter) + expect((positiveFilter as VisibilityFilter).visibility).toEqual(Visibility.Public) + }) + + test("handles multiple tokens", () => { + const completeFilter = createFilterFromString("is:public !is:public") + expect(completeFilter).toBeInstanceOf(ConjunctiveFilter) + expect((completeFilter as ConjunctiveFilter).filters).toHaveLength(2) + + // First token + const positiveFilter1 = (completeFilter as ConjunctiveFilter).filters[0] + expect(positiveFilter1).toBeInstanceOf(VisibilityFilter) + expect((positiveFilter1 as VisibilityFilter).visibility).toEqual(Visibility.Public) + + // Second token + const negatedFilter2 = (completeFilter as ConjunctiveFilter).filters[1] + expect(negatedFilter2).toBeInstanceOf(NegatedFilter) + + const positiveFilter2 = (negatedFilter2 as NegatedFilter).filter + expect(positiveFilter2).toBeInstanceOf(VisibilityFilter) + expect((positiveFilter2 as VisibilityFilter).visibility).toEqual(Visibility.Public) + }) +}) From 8b2865329e05c25420b299a8c31f5dbe952c7795 Mon Sep 17 00:00:00 2001 From: lars-reimann Date: Wed, 25 May 2022 15:07:49 +0000 Subject: [PATCH 45/45] style: apply automatic fixes of linters --- .../filters/AbstractPythonFilter.test.ts | 332 ++++++++---------- .../model/filters/filterFactory.test.ts | 100 +++--- 2 files changed, 206 insertions(+), 226 deletions(-) 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 fcd623cf9..0b5df105c 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 @@ -1,193 +1,173 @@ // noinspection UnnecessaryLocalVariableJS,DuplicatedCode -import PythonPackage from "../PythonPackage"; -import PythonParameter from "../PythonParameter"; -import PythonModule from "../PythonModule"; -import PythonClass from "../PythonClass"; -import PythonFunction from "../PythonFunction"; -import NameFilter from "./NameFilter"; -import {initialState} from "../../../annotations/annotationSlice"; -import PythonDeclaration from "../PythonDeclaration"; +import PythonPackage from '../PythonPackage'; +import PythonParameter from '../PythonParameter'; +import PythonModule from '../PythonModule'; +import PythonClass from '../PythonClass'; +import PythonFunction from '../PythonFunction'; +import NameFilter from './NameFilter'; +import { initialState } from '../../../annotations/annotationSlice'; +import PythonDeclaration from '../PythonDeclaration'; let pythonPackage: PythonPackage; beforeEach(() => { - pythonPackage = new PythonPackage( - "test_package", - "test_package", - "1.0.0", - [ - new PythonModule( - "test_module_1", - [], - [], - [ - new PythonClass( - "test_class_1", - "test_module_1.test_class_1", - [], - [], - [ - new PythonFunction( - "test_method_1", - "test_method_1", - "test_module_1.test_class_1.test_method_1", - "test_module_1.test_class_1.test_method_1", - [], - [ - new PythonParameter("test_parameter_1"), - new PythonParameter("test_parameter_2") - ] - ), - new PythonFunction( - "test_method_2", - "test_method_2", - "test_module_1.test_class_1.test_method_2", - "test_module_1.test_class_1.test_method_2" - ) - ] - ), - new PythonClass( - "test_class_2", - "test_module_1.test_class_1", - ) - ], - [ - new PythonFunction( - "test_global_function_1", - "test_global_function_1", - "test_module_1.test_global_function_1", - "test_module_1.test_global_function_1", - [], - [ - new PythonParameter("test_parameter_1"), - new PythonParameter("test_parameter_2") - ] - ), - new PythonFunction( - "test_global_function_2", - "test_global_function_2", - "test_module_1.test_global_function_2", - "test_module_1.test_global_function_2" - ), - ] - ), - new PythonModule( - "test_module_2", - [], - [], - [], - [], - ) - ] - ) -}) - -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 modules = filteredPackage.modules - expect(names(modules)).toEqual(["test_module_1"]) - - const classes = modules[0].classes - expect(names(classes)).toEqual(["test_class_1", "test_class_2"]) - - const methods = classes[0].methods - expect(names(methods)).toEqual(["test_method_1", "test_method_2"]) - - const methodParameters = methods[0].parameters - expect(names(methodParameters)).toEqual(["test_parameter_1", "test_parameter_2"]) - - const globalFunctions = modules[0].functions - expect(names(globalFunctions)).toEqual(["test_global_function_1", "test_global_function_2"]) - - const globalFunctionParameters = globalFunctions[0].parameters - expect(names(globalFunctionParameters)).toEqual(["test_parameter_1", "test_parameter_2"]) - }) - - 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 modules = filteredPackage.modules - expect(names(modules)).toEqual(["test_module_1"]) - - const classes = modules[0].classes - expect(names(classes)).toEqual(["test_class_1"]) - - const methods = classes[0].methods - expect(names(methods)).toEqual(["test_method_1", "test_method_2"]) - - const methodParameters = methods[0].parameters - expect(names(methodParameters)).toEqual(["test_parameter_1", "test_parameter_2"]) - - const globalFunctions = modules[0].functions - expect(names(globalFunctions)).toEqual([]) - }) - - 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 modules = filteredPackage.modules - expect(names(modules)).toEqual(["test_module_1"]) - - const classes = modules[0].classes - expect(names(classes)).toEqual(["test_class_1"]) - - const methods = classes[0].methods - expect(names(methods)).toEqual(["test_method_1"]) - - const methodParameters = methods[0].parameters - expect(names(methodParameters)).toEqual(["test_parameter_1", "test_parameter_2"]) - - const globalFunctions = modules[0].functions - expect(names(globalFunctions)).toEqual([]) - }) - - 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 modules = filteredPackage.modules - expect(names(modules)).toEqual(["test_module_1"]) - - const classes = modules[0].classes - expect(names(classes)).toEqual([]) - - const globalFunctions = modules[0].functions - expect(names(globalFunctions)).toEqual(["test_global_function_1"]) + pythonPackage = new PythonPackage('test_package', 'test_package', '1.0.0', [ + new PythonModule( + 'test_module_1', + [], + [], + [ + new PythonClass( + 'test_class_1', + 'test_module_1.test_class_1', + [], + [], + [ + new PythonFunction( + 'test_method_1', + 'test_method_1', + 'test_module_1.test_class_1.test_method_1', + 'test_module_1.test_class_1.test_method_1', + [], + [new PythonParameter('test_parameter_1'), new PythonParameter('test_parameter_2')], + ), + new PythonFunction( + 'test_method_2', + 'test_method_2', + 'test_module_1.test_class_1.test_method_2', + 'test_module_1.test_class_1.test_method_2', + ), + ], + ), + new PythonClass('test_class_2', 'test_module_1.test_class_1'), + ], + [ + new PythonFunction( + 'test_global_function_1', + 'test_global_function_1', + 'test_module_1.test_global_function_1', + 'test_module_1.test_global_function_1', + [], + [new PythonParameter('test_parameter_1'), new PythonParameter('test_parameter_2')], + ), + new PythonFunction( + 'test_global_function_2', + 'test_global_function_2', + 'test_module_1.test_global_function_2', + 'test_module_1.test_global_function_2', + ), + ], + ), + new PythonModule('test_module_2', [], [], [], []), + ]); +}); + +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 modules = filteredPackage.modules; + expect(names(modules)).toEqual(['test_module_1']); + + const classes = modules[0].classes; + expect(names(classes)).toEqual(['test_class_1', 'test_class_2']); + + const methods = classes[0].methods; + expect(names(methods)).toEqual(['test_method_1', 'test_method_2']); + + const methodParameters = methods[0].parameters; + expect(names(methodParameters)).toEqual(['test_parameter_1', 'test_parameter_2']); + + const globalFunctions = modules[0].functions; + expect(names(globalFunctions)).toEqual(['test_global_function_1', 'test_global_function_2']); + + const globalFunctionParameters = globalFunctions[0].parameters; + expect(names(globalFunctionParameters)).toEqual(['test_parameter_1', 'test_parameter_2']); + }); + + 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 modules = filteredPackage.modules; + expect(names(modules)).toEqual(['test_module_1']); + + const classes = modules[0].classes; + expect(names(classes)).toEqual(['test_class_1']); + + const methods = classes[0].methods; + expect(names(methods)).toEqual(['test_method_1', 'test_method_2']); + + const methodParameters = methods[0].parameters; + expect(names(methodParameters)).toEqual(['test_parameter_1', 'test_parameter_2']); + + const globalFunctions = modules[0].functions; + expect(names(globalFunctions)).toEqual([]); + }); + + 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 modules = filteredPackage.modules; + expect(names(modules)).toEqual(['test_module_1']); + + const classes = modules[0].classes; + expect(names(classes)).toEqual(['test_class_1']); + + const methods = classes[0].methods; + expect(names(methods)).toEqual(['test_method_1']); + + const methodParameters = methods[0].parameters; + expect(names(methodParameters)).toEqual(['test_parameter_1', 'test_parameter_2']); + + const globalFunctions = modules[0].functions; + expect(names(globalFunctions)).toEqual([]); + }); + + 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 modules = filteredPackage.modules; + expect(names(modules)).toEqual(['test_module_1']); + + const classes = modules[0].classes; + expect(names(classes)).toEqual([]); + + const globalFunctions = modules[0].functions; + expect(names(globalFunctions)).toEqual(['test_global_function_1']); - const globalFunctionParameters = globalFunctions[0].parameters - expect(names(globalFunctionParameters)).toEqual(["test_parameter_1", "test_parameter_2"]) - }) + const globalFunctionParameters = globalFunctions[0].parameters; + expect(names(globalFunctionParameters)).toEqual(['test_parameter_1', 'test_parameter_2']); + }); - 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) + 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 modules = filteredPackage.modules - expect(names(modules)).toEqual(["test_module_1"]) + const modules = filteredPackage.modules; + expect(names(modules)).toEqual(['test_module_1']); - const classes = modules[0].classes - expect(names(classes)).toEqual(["test_class_1"]) + const classes = modules[0].classes; + expect(names(classes)).toEqual(['test_class_1']); - const methods = classes[0].methods - expect(names(methods)).toEqual(["test_method_1"]) + const methods = classes[0].methods; + expect(names(methods)).toEqual(['test_method_1']); - const methodParameters = methods[0].parameters - expect(names(methodParameters)).toEqual(["test_parameter_1"]) + const methodParameters = methods[0].parameters; + expect(names(methodParameters)).toEqual(['test_parameter_1']); - const globalFunctions = modules[0].functions - expect(names(globalFunctions)).toEqual(["test_global_function_1"]) + const globalFunctions = modules[0].functions; + expect(names(globalFunctions)).toEqual(['test_global_function_1']); - const globalFunctionParameters = globalFunctions[0].parameters - expect(names(globalFunctionParameters)).toEqual(["test_parameter_1"]) - }) -}) + const globalFunctionParameters = globalFunctions[0].parameters; + expect(names(globalFunctionParameters)).toEqual(['test_parameter_1']); + }); +}); function names(declarations: PythonDeclaration[]): string[] { - return declarations.map((it) => it.name) + return declarations.map((it) => it.name); } 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 31471ff40..6359149e6 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 @@ -1,54 +1,54 @@ -import {createFilterFromString} from "./filterFactory"; -import {ConjunctiveFilter} from "./ConjunctiveFilter"; -import VisibilityFilter, {Visibility} from "./VisibilityFilter"; -import {NegatedFilter} from "./NegatedFilter"; - -describe("createFilterFromString", () => { - test("handles an empty string", () => { - const completeFilter = createFilterFromString("") - expect(completeFilter).toBeInstanceOf(ConjunctiveFilter) - expect((completeFilter as ConjunctiveFilter).filters).toEqual([]) - }) - - test("handles a single positive token", () => { - const completeFilter = createFilterFromString("is:public") - expect(completeFilter).toBeInstanceOf(ConjunctiveFilter) - expect((completeFilter as ConjunctiveFilter).filters).toHaveLength(1) - - const positiveFilter = (completeFilter as ConjunctiveFilter).filters[0] - expect(positiveFilter).toBeInstanceOf(VisibilityFilter) - expect((positiveFilter as VisibilityFilter).visibility).toEqual(Visibility.Public) - }) - - test("handles a single negated token", () => { - const completeFilter = createFilterFromString("!is:public") - expect(completeFilter).toBeInstanceOf(ConjunctiveFilter) - expect((completeFilter as ConjunctiveFilter).filters).toHaveLength(1) - - const negatedFilter = (completeFilter as ConjunctiveFilter).filters[0] - expect(negatedFilter).toBeInstanceOf(NegatedFilter) - - const positiveFilter = (negatedFilter as NegatedFilter).filter - expect(positiveFilter).toBeInstanceOf(VisibilityFilter) - expect((positiveFilter as VisibilityFilter).visibility).toEqual(Visibility.Public) - }) - - test("handles multiple tokens", () => { - const completeFilter = createFilterFromString("is:public !is:public") - expect(completeFilter).toBeInstanceOf(ConjunctiveFilter) - expect((completeFilter as ConjunctiveFilter).filters).toHaveLength(2) +import { createFilterFromString } from './filterFactory'; +import { ConjunctiveFilter } from './ConjunctiveFilter'; +import VisibilityFilter, { Visibility } from './VisibilityFilter'; +import { NegatedFilter } from './NegatedFilter'; + +describe('createFilterFromString', () => { + test('handles an empty string', () => { + const completeFilter = createFilterFromString(''); + expect(completeFilter).toBeInstanceOf(ConjunctiveFilter); + expect((completeFilter as ConjunctiveFilter).filters).toEqual([]); + }); + + test('handles a single positive token', () => { + const completeFilter = createFilterFromString('is:public'); + expect(completeFilter).toBeInstanceOf(ConjunctiveFilter); + expect((completeFilter as ConjunctiveFilter).filters).toHaveLength(1); + + const positiveFilter = (completeFilter as ConjunctiveFilter).filters[0]; + expect(positiveFilter).toBeInstanceOf(VisibilityFilter); + expect((positiveFilter as VisibilityFilter).visibility).toEqual(Visibility.Public); + }); + + test('handles a single negated token', () => { + const completeFilter = createFilterFromString('!is:public'); + expect(completeFilter).toBeInstanceOf(ConjunctiveFilter); + expect((completeFilter as ConjunctiveFilter).filters).toHaveLength(1); + + const negatedFilter = (completeFilter as ConjunctiveFilter).filters[0]; + expect(negatedFilter).toBeInstanceOf(NegatedFilter); + + const positiveFilter = (negatedFilter as NegatedFilter).filter; + expect(positiveFilter).toBeInstanceOf(VisibilityFilter); + expect((positiveFilter as VisibilityFilter).visibility).toEqual(Visibility.Public); + }); + + test('handles multiple tokens', () => { + const completeFilter = createFilterFromString('is:public !is:public'); + expect(completeFilter).toBeInstanceOf(ConjunctiveFilter); + expect((completeFilter as ConjunctiveFilter).filters).toHaveLength(2); // First token - const positiveFilter1 = (completeFilter as ConjunctiveFilter).filters[0] - expect(positiveFilter1).toBeInstanceOf(VisibilityFilter) - expect((positiveFilter1 as VisibilityFilter).visibility).toEqual(Visibility.Public) + const positiveFilter1 = (completeFilter as ConjunctiveFilter).filters[0]; + expect(positiveFilter1).toBeInstanceOf(VisibilityFilter); + expect((positiveFilter1 as VisibilityFilter).visibility).toEqual(Visibility.Public); // Second token - const negatedFilter2 = (completeFilter as ConjunctiveFilter).filters[1] - expect(negatedFilter2).toBeInstanceOf(NegatedFilter) - - const positiveFilter2 = (negatedFilter2 as NegatedFilter).filter - expect(positiveFilter2).toBeInstanceOf(VisibilityFilter) - expect((positiveFilter2 as VisibilityFilter).visibility).toEqual(Visibility.Public) - }) -}) + const negatedFilter2 = (completeFilter as ConjunctiveFilter).filters[1]; + expect(negatedFilter2).toBeInstanceOf(NegatedFilter); + + const positiveFilter2 = (negatedFilter2 as NegatedFilter).filter; + expect(positiveFilter2).toBeInstanceOf(VisibilityFilter); + expect((positiveFilter2 as VisibilityFilter).visibility).toEqual(Visibility.Public); + }); +});