Skip to content
This repository has been archived by the owner on Nov 29, 2023. It is now read-only.

Commit

Permalink
feat: description annotation (#536)
Browse files Browse the repository at this point in the history
* feat: process description annotations in backend

* feat: update validation in backend

* feat(backend): actually trigger processing

* style: apply automatic fixes of linters

* feat: Adding description annotation (WIP)

* revert: changes to Kotlin files

* feat: Added description annotation, a filter for it and adjusted the heatmap to also count description annotations.

* style: apply automatic fixes of linters

* feat: don't show new description in annotation view (long)

* fix: build error

* feat: consistent label for new description text area

* refactor: sort stuff

* fix: description annotations not sent to server

* fix: remove log

* refactor: sort stuff

* refactor: sort stuff

* style: apply automatic fixes of linters

* fix: build error

Co-authored-by: lars-reimann <lars-reimann@users.noreply.github.com>
Co-authored-by: Arsam Islami <arsamislami@yahoo.de>
Co-authored-by: Masara <Masara@users.noreply.github.com>
  • Loading branch information
4 people authored Jun 13, 2022
1 parent cca834d commit 9f3ff94
Show file tree
Hide file tree
Showing 25 changed files with 361 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ data class ConstantAnnotation(val defaultValue: DefaultValue) : EditorAnnotation
override val validTargets = PARAMETERS
}

@Serializable
data class DescriptionAnnotation(val newDescription: String) : EditorAnnotation() {

@Transient
override val validTargets = ANY_DECLARATION
}

@Serializable
data class EnumAnnotation(val enumName: String, val pairs: List<EnumPair>) : EditorAnnotation() {

Expand Down Expand Up @@ -112,7 +119,7 @@ object RemoveAnnotation : EditorAnnotation() {
@Serializable
data class RenameAnnotation(val newName: String) : EditorAnnotation() {
@Transient
override val validTargets = CLASSES.union(FUNCTIONS).union(PARAMETERS)
override val validTargets = ANY_DECLARATION
}

@Serializable
Expand Down Expand Up @@ -164,6 +171,13 @@ enum class AnnotationTarget(private val target: String) {
}
}

val ANY_DECLARATION = setOf(
CLASS,
GLOBAL_FUNCTION,
METHOD,
CONSTRUCTOR_PARAMETER,
FUNCTION_PARAMETER
)
val GLOBAL_DECLARATIONS = setOf(CLASS, GLOBAL_FUNCTION)
val CLASSES = setOf(CLASS)
val FUNCTIONS = setOf(GLOBAL_FUNCTION, METHOD)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.larsreimann.api_editor.transformation

import com.larsreimann.api_editor.model.DescriptionAnnotation
import com.larsreimann.api_editor.mutable_model.PythonClass
import com.larsreimann.api_editor.mutable_model.PythonDeclaration
import com.larsreimann.api_editor.mutable_model.PythonFunction
import com.larsreimann.api_editor.mutable_model.PythonPackage
import com.larsreimann.api_editor.mutable_model.PythonParameter
import com.larsreimann.modeling.descendants

/**
* Processes and removes `@description` annotations.
*/
fun PythonPackage.processDescriptionAnnotations() {
this.descendants()
.filterIsInstance<PythonDeclaration>()
.forEach { it.processDescriptionAnnotations() }
}

private fun PythonDeclaration.processDescriptionAnnotations() {
this.annotations
.filterIsInstance<DescriptionAnnotation>()
.forEach {
when (this) {
is PythonClass -> this.description = it.newDescription
is PythonFunction -> this.description = it.newDescription
is PythonParameter -> this.description = it.newDescription
else -> {
// Do nothing
}
}
this.annotations.remove(it)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ private fun PythonPackage.preprocess(newPackageName: String) {
*/
private fun PythonPackage.processAnnotations() {
processRemoveAnnotations()
processDescriptionAnnotations()
processRenameAnnotations()
processMoveAnnotations()
processBoundaryAnnotations()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,15 +181,28 @@ class AnnotationValidator(private val annotatedPythonPackage: SerializablePython

companion object {
private var possibleCombinations = buildMap<String, Set<String>> {
this["Attribute"] = mutableSetOf("Rename")
this["Boundary"] = mutableSetOf("Group", "Optional", "Rename", "Required")
this["CalledAfter"] = mutableSetOf("CalledAfter", "Group", "Move", "Pure", "Rename")
this["Attribute"] = mutableSetOf("Description", "Rename")
this["Boundary"] = mutableSetOf("Description", "Group", "Optional", "Rename", "Required")
this["CalledAfter"] = mutableSetOf("CalledAfter", "Description", "Group", "Move", "Pure", "Rename")
this["Constant"] = mutableSetOf()
this["Enum"] = mutableSetOf("Group", "Rename", "Required")
this["Description"] = mutableSetOf(
"Attribute",
"Boundary",
"CalledAfter",
"Enum",
"Group",
"Move",
"Optional",
"Pure",
"Rename",
"Required"
)
this["Enum"] = mutableSetOf("Description", "Group", "Rename", "Required")
this["Group"] =
mutableSetOf(
"Boundary",
"CalledAfter",
"Description",
"Enum",
"Group",
"Move",
Expand All @@ -198,22 +211,23 @@ class AnnotationValidator(private val annotatedPythonPackage: SerializablePython
"Rename",
"Required"
)
this["Move"] = mutableSetOf("CalledAfter", "Group", "Pure", "Rename")
this["Optional"] = mutableSetOf("Boundary", "Group", "Rename")
this["Pure"] = mutableSetOf("CalledAfter", "Group", "Move", "Rename")
this["Move"] = mutableSetOf("CalledAfter", "Description", "Group", "Pure", "Rename")
this["Optional"] = mutableSetOf("Boundary", "Description", "Group", "Rename")
this["Pure"] = mutableSetOf("CalledAfter", "Description", "Group", "Move", "Rename")
this["Remove"] = mutableSetOf()
this["Rename"] = mutableSetOf(
"Attribute",
"Boundary",
"CalledAfter",
"Description",
"Enum",
"Group",
"Move",
"Optional",
"Pure",
"Required"
)
this["Required"] = mutableSetOf("Boundary", "Enum", "Group", "Rename")
this["Required"] = mutableSetOf("Boundary", "Description", "Enum", "Group", "Rename")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package com.larsreimann.api_editor.transformation

import com.larsreimann.api_editor.model.DescriptionAnnotation
import com.larsreimann.api_editor.mutable_model.PythonClass
import com.larsreimann.api_editor.mutable_model.PythonFunction
import com.larsreimann.api_editor.mutable_model.PythonModule
import com.larsreimann.api_editor.mutable_model.PythonPackage
import com.larsreimann.api_editor.mutable_model.PythonParameter
import io.kotest.matchers.collections.shouldBeEmpty
import io.kotest.matchers.shouldBe
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test

class DescriptionAnnotationProcessorTest {
private lateinit var testClass: PythonClass
private lateinit var testFunction: PythonFunction
private lateinit var testParameter: PythonParameter
private lateinit var testPackage: PythonPackage

@BeforeEach
fun reset() {
testClass = PythonClass(
name = "TestClass",
description = "Lorem ipsum",
annotations = mutableListOf(DescriptionAnnotation("Important class"))
)
testParameter = PythonParameter(
name = "testParameter",
description = "Lorem ipsum",
annotations = mutableListOf(DescriptionAnnotation("Important parameter"))
)
testFunction = PythonFunction(
name = "testFunction",
description = "Lorem ipsum",
annotations = mutableListOf(DescriptionAnnotation("Important function")),
parameters = mutableListOf(testParameter)
)
testPackage = PythonPackage(
distribution = "testPackage",
name = "testPackage",
version = "1.0.0",
modules = listOf(
PythonModule(
name = "testModule",
classes = listOf(testClass),
functions = listOf(testFunction)
)
)
)
}

@Test
fun `should process DescriptionAnnotation of classes`() {
testPackage.processDescriptionAnnotations()

testClass.description shouldBe "Important class"
}

@Test
fun `should remove DescriptionAnnotation of classes`() {
testPackage.processDescriptionAnnotations()

testClass.annotations
.filterIsInstance<DescriptionAnnotation>()
.shouldBeEmpty()
}

@Test
fun `should process DescriptionAnnotation of functions`() {
testPackage.processDescriptionAnnotations()

testFunction.description shouldBe "Important function"
}

@Test
fun `should remove DescriptionAnnotation of functions`() {
testPackage.processDescriptionAnnotations()

testFunction.annotations
.filterIsInstance<DescriptionAnnotation>()
.shouldBeEmpty()
}

@Test
fun `should process DescriptionAnnotation of parameters`() {
testPackage.processDescriptionAnnotations()

testParameter.description shouldBe "Important parameter"
}

@Test
fun `should remove DescriptionAnnotation of parameters`() {
testPackage.processDescriptionAnnotations()

testParameter.annotations
.filterIsInstance<DescriptionAnnotation>()
.shouldBeEmpty()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ fun AnnotationDropdown(
showBoundary: Boolean = false,
showCalledAfter: Boolean = false,
showConstant: Boolean = false,
showDescription: Boolean = false,
showEnum: Boolean = false,
showGroup: Boolean = false,
showMove: Boolean = false,
Expand Down Expand Up @@ -56,6 +57,11 @@ fun AnnotationDropdown(
Text(labels.getString("AnnotationDropdown.Option.Constant"))
}
}
if (showDescription) {
DropdownMenuItem(onClick = {}) {
Text(labels.getString("AnnotationDropdown.Option.Description"))
}
}
if (showEnum) {
DropdownMenuItem(onClick = {}) {
Text(labels.getString("AnnotationDropdown.Option.Enum"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ AnnotationDropdown.Option.Attribute=@attribute
AnnotationDropdown.Option.Boundary=@boundary
AnnotationDropdown.Option.CalledAfter=@calledAfter
AnnotationDropdown.Option.Constant=@constant
AnnotationDropdown.Option.Description=@description
AnnotationDropdown.Option.Enum=@enum
AnnotationDropdown.Option.Group=@group
AnnotationDropdown.Option.Move=@move
Expand Down
9 changes: 9 additions & 0 deletions api-editor/gui/src/app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { initializeAnnotations, persistAnnotations, selectAnnotations } from '..
import { BoundaryForm } from '../features/annotations/forms/BoundaryForm';
import { CalledAfterForm } from '../features/annotations/forms/CalledAfterForm';
import { ConstantForm } from '../features/annotations/forms/ConstantForm';
import { DescriptionForm } from '../features/annotations/forms/DescriptionForm';
import { EnumForm } from '../features/annotations/forms/EnumForm';
import { GroupForm } from '../features/annotations/forms/GroupForm';
import { MoveForm } from '../features/annotations/forms/MoveForm';
Expand Down Expand Up @@ -47,6 +48,8 @@ import {
selectFilteredPythonPackage,
selectPythonPackage,
} from '../features/packageData/apiSlice';
import { PythonClass } from '../features/packageData/model/PythonClass';
import { PythonParameter } from '../features/packageData/model/PythonParameter';

export const App: React.FC = function () {
useIndexedDB();
Expand Down Expand Up @@ -103,6 +106,12 @@ export const App: React.FC = function () {
{currentUserAction.type === 'constant' && (
<ConstantForm target={userActionTarget || pythonPackage} />
)}
{currentUserAction.type === 'description' &&
(userActionTarget instanceof PythonClass ||
userActionTarget instanceof PythonFunction ||
userActionTarget instanceof PythonParameter) && (
<DescriptionForm target={userActionTarget} />
)}
{currentUserAction.type === 'enum' && <EnumForm target={userActionTarget || pythonPackage} />}
{currentUserAction.type === 'group' && (
<GroupForm
Expand Down
4 changes: 2 additions & 2 deletions api-editor/gui/src/common/FilterHelpButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ export const FilterHelpButton = function () {
Displays only elements that are annotated with the given type xy. Replace [type]
with one of{' '}
<em>
@attribute, @boundary, @calledAfter, @constant, @enum, @group, @move, @optional,
@pure, @remove, @renaming, @required
@attribute, @boundary, @calledAfter, @constant, @description, @enum, @group,
@move, @optional, @pure, @remove, @renaming, @required
</em>
.
</ChakraText>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
InferableRemoveAnnotation,
InferableRenameAnnotation,
InferableRequiredAnnotation,
InferableDescriptionAnnotation,
} from './InferableAnnotation';

export class AnnotatedPythonPackageBuilder {
Expand Down Expand Up @@ -140,6 +141,7 @@ export class AnnotatedPythonPackageBuilder {
'Boundary',
'CalledAfters',
'Constant',
'Description',
'Enum',
'Groups',
'Move',
Expand Down Expand Up @@ -192,6 +194,12 @@ export class AnnotatedPythonPackageBuilder {
return new InferableConstantAnnotation(constantAnnotation);
}
break;
case 'Description':
const descriptionAnnotation = this.annotationStore.descriptions[target];
if (descriptionAnnotation) {
return new InferableDescriptionAnnotation(descriptionAnnotation);
}
break;
case 'Groups':
const groupAnnotations = this.annotationStore.groups[target];
if (!groupAnnotations) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
CalledAfterAnnotation,
ComparisonOperator,
ConstantAnnotation,
DescriptionAnnotation,
DefaultType,
DefaultValue,
EnumAnnotation,
Expand Down Expand Up @@ -92,6 +93,15 @@ export class InferableConstantAnnotation extends InferableAnnotation {
}
}

export class InferableDescriptionAnnotation extends InferableAnnotation {
readonly newDescription: string;

constructor(descriptionAnnotation: DescriptionAnnotation) {
super(dataPathPrefix + 'DescriptionAnnotation');
this.newDescription = descriptionAnnotation.newDescription;
}
}

export class InferableGroupAnnotation extends InferableAnnotation {
readonly groupName: string;
readonly parameters: string[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ import {
showMoveAnnotationForm,
showOptionalAnnotationForm,
showRenameAnnotationForm,
showDescriptionAnnotationForm,
} from '../ui/uiSlice';

interface AnnotationDropdownProps {
showAttribute?: boolean;
showBoundary?: boolean;
showCalledAfter?: boolean;
showConstant?: boolean;
showDescription?: boolean;
showEnum?: boolean;
showGroup?: boolean;
showMove?: boolean;
Expand All @@ -36,6 +38,7 @@ export const AnnotationDropdown: React.FC<AnnotationDropdownProps> = function ({
showBoundary = false,
showCalledAfter = false,
showConstant = false,
showDescription = false,
showGroup = false,
showEnum = false,
showMove = false,
Expand Down Expand Up @@ -79,6 +82,11 @@ export const AnnotationDropdown: React.FC<AnnotationDropdownProps> = function ({
{showConstant && (
<MenuItem onClick={() => dispatch(showConstantAnnotationForm(target))}>@constant</MenuItem>
)}
{showDescription && (
<MenuItem onClick={() => dispatch(showDescriptionAnnotationForm(target))}>
@description
</MenuItem>
)}
{showEnum && <MenuItem onClick={() => dispatch(showEnumAnnotationForm(target))}>@enum</MenuItem>}
{showGroup && (
<MenuItem
Expand Down
Loading

0 comments on commit 9f3ff94

Please sign in to comment.