diff --git a/plantuml/parser/README.md b/plantuml/parser/README.md index 17550284..7edee56a 100644 --- a/plantuml/parser/README.md +++ b/plantuml/parser/README.md @@ -98,4 +98,5 @@ For the detailed design and users Guide, see `docs/README.md` in the source tree Authoring guides: - [`docs/element-identifiers.md`](docs/element-identifiers.md) — how element identifiers are constructed and how to link component, class, and sequence diagrams +- [`docs/class-diagram.md`](docs/class-diagram.md) — class-diagram syntax and free-function conventions - [`docs/sequence-diagram.md`](docs/sequence-diagram.md) — supported sequence diagram syntax and semantics diff --git a/plantuml/parser/docs/class-diagram.md b/plantuml/parser/docs/class-diagram.md new file mode 100644 index 00000000..3eb8d496 --- /dev/null +++ b/plantuml/parser/docs/class-diagram.md @@ -0,0 +1,54 @@ + + +# Class Diagram Support Guide + +This guide describes class-diagram syntax that has specific semantic meaning in +the PlantUML parser and resolver. + +## Free functions + +Use an `entity` with the `<>` stereotype to group free functions. +The entity is a syntactic container only and is omitted from the resolved +class-diagram model. Its methods resolve as free functions in the enclosing +namespace. + +```plantuml +@startuml algorithms +namespace amp { + namespace algorithm { + entity Placeholder <> { + + clamp(value, min, max) + + max(a, b) + } + } +} +@enduml +``` + +This resolves to the free functions `amp.algorithm.clamp` and `amp.algorithm.max`. +Visibility markers such as `+` are accepted for PlantUML compatibility and do not +affect free-function semantics. + +The parser currently preserves only the entity's name, namespace, package, +stereotypes, and methods. Entity-level template parameters, `extends` and +`implements` clauses, attributes, and `using` type aliases are not represented +in the entity model. The grammar may accept these constructs, but the parser +silently drops them, so do not rely on them. Use method-level template +parameters for templated free functions. + +An `entity` without `<>` is not modeled as a class-diagram entity. +Use the corresponding PlantUML declaration (`class`, `interface`, or `struct`) +to model those element kinds. The `entity <>` handling is a focused +extension for grouping free functions, not a general substitute for those +declarations. diff --git a/plantuml/parser/integration_test/class_diagram/free_function_entity/free_function_entity.puml b/plantuml/parser/integration_test/class_diagram/free_function_entity/free_function_entity.puml new file mode 100644 index 00000000..9d26b58e --- /dev/null +++ b/plantuml/parser/integration_test/class_diagram/free_function_entity/free_function_entity.puml @@ -0,0 +1,29 @@ +' ******************************************************************************* +' Copyright (c) 2026 Contributors to the Eclipse Foundation +' +' See the NOTICE file(s) distributed with this work for additional +' information regarding copyright ownership. +' +' This program and the accompanying materials are made available under the +' terms of the Apache License Version 2.0 which is available at +' https://www.apache.org/licenses/LICENSE-2.0 +' +' SPDX-License-Identifier: Apache-2.0 +' ******************************************************************************* + +@startuml free_function_entity + +namespace amp { + namespace algorithm { + entity Placeholder <> { + + clamp(value, min, max) + + Submit(callable, arguments) + } + } +} + +entity amp.algorithm.Qualified <> { + + qualified() +} + +@enduml diff --git a/plantuml/parser/integration_test/class_diagram/free_function_entity/output.json b/plantuml/parser/integration_test/class_diagram/free_function_entity/output.json new file mode 100644 index 00000000..f0228a2e --- /dev/null +++ b/plantuml/parser/integration_test/class_diagram/free_function_entity/output.json @@ -0,0 +1,81 @@ +{ + "free_function_entity.puml": { + "name": "free_function_entity", + "entities": [], + "free_functions": [ + { + "name": "clamp", + "enclosing_namespace_id": "amp.algorithm", + "return_type": null, + "parameters": [ + { + "name": "value", + "param_type": null, + "is_variadic": false + }, + { + "name": "min", + "param_type": null, + "is_variadic": false + }, + { + "name": "max", + "param_type": null, + "is_variadic": false + } + ], + "template_parameters": null, + "source_location": { + "file": "", + "line": 19 + } + }, + { + "name": "Submit", + "enclosing_namespace_id": "amp.algorithm", + "return_type": null, + "parameters": [ + { + "name": "callable", + "param_type": null, + "is_variadic": false + }, + { + "name": "arguments", + "param_type": null, + "is_variadic": false + } + ], + "template_parameters": [ + { + "Type": { + "name": "CallableType", + "is_pack": false + } + }, + { + "Type": { + "name": "ArgumentTypes", + "is_pack": true + } + } + ], + "source_location": { + "file": "", + "line": 20 + } + }, + { + "name": "qualified", + "enclosing_namespace_id": "amp.algorithm", + "return_type": null, + "parameters": [], + "template_parameters": null, + "source_location": { + "file": "", + "line": 26 + } + } + ] + } +} diff --git a/plantuml/parser/puml_parser/src/class_diagram/src/class_ast.rs b/plantuml/parser/puml_parser/src/class_diagram/src/class_ast.rs index 2e2368d9..1f978fa1 100644 --- a/plantuml/parser/puml_parser/src/class_diagram/src/class_ast.rs +++ b/plantuml/parser/puml_parser/src/class_diagram/src/class_ast.rs @@ -29,24 +29,27 @@ pub enum Visibility { pub enum Element { ClassDef(ClassDef), StructDef(StructDef), - EnumDef(EnumDef), InterfaceDef(InterfaceDef), + EntityDef(EntityDef), + EnumDef(EnumDef), } impl Element { pub fn set_namespace(&mut self, ns: String) { match self { Element::ClassDef(def) => def.namespace = ns, Element::StructDef(def) => def.namespace = ns, - Element::EnumDef(def) => def.namespace = ns, Element::InterfaceDef(def) => def.namespace = ns, + Element::EntityDef(def) => def.namespace = ns, + Element::EnumDef(def) => def.namespace = ns, } } pub fn set_package(&mut self, ns: String) { match self { Element::ClassDef(def) => def.package = ns, Element::StructDef(def) => def.package = ns, - Element::EnumDef(def) => def.package = ns, Element::InterfaceDef(def) => def.package = ns, + Element::EntityDef(def) => def.package = ns, + Element::EnumDef(def) => def.package = ns, } } } @@ -268,6 +271,17 @@ impl TypeDef for InterfaceDef { } } +#[derive(Debug, Default, Serialize, Deserialize, PartialEq)] +pub struct EntityDef { + pub name: Name, + pub namespace: String, + pub package: String, + #[serde(default)] + pub stereotypes: Vec, + pub methods: Vec, + pub source_location: SourceLocation, +} + #[derive(Debug, Default, Serialize, Deserialize, PartialEq)] pub struct EnumDef { pub name: Name, @@ -481,6 +495,7 @@ mod tests { Element::StructDef(StructDef::default()), Element::EnumDef(EnumDef::default()), Element::InterfaceDef(InterfaceDef::default()), + Element::EntityDef(EntityDef::default()), ]; for el in elements.iter_mut() { @@ -493,6 +508,7 @@ mod tests { Element::StructDef(d) => assert_eq!(d.namespace, "test_ns"), Element::EnumDef(d) => assert_eq!(d.namespace, "test_ns"), Element::InterfaceDef(d) => assert_eq!(d.namespace, "test_ns"), + Element::EntityDef(d) => assert_eq!(d.namespace, "test_ns"), } } } diff --git a/plantuml/parser/puml_parser/src/class_diagram/src/class_parser.rs b/plantuml/parser/puml_parser/src/class_diagram/src/class_parser.rs index ac249613..3eead7c3 100644 --- a/plantuml/parser/puml_parser/src/class_diagram/src/class_parser.rs +++ b/plantuml/parser/puml_parser/src/class_diagram/src/class_parser.rs @@ -11,9 +11,9 @@ // SPDX-License-Identifier: Apache-2.0 // ******************************************************************************* use crate::class_ast::{ - Arrow, Attribute, ClassDef, ClassUmlFile, ClassUmlTopLevel, Element, EnumDef, EnumItem, - EnumValue, InterfaceDef, Method, Name, Namespace, Package, Param, Relationship, StructDef, - TypeAlias, Visibility, + Arrow, Attribute, ClassDef, ClassUmlFile, ClassUmlTopLevel, Element, EntityDef, EnumDef, + EnumItem, EnumValue, InterfaceDef, Method, Name, Namespace, Package, Param, Relationship, + StructDef, TypeAlias, Visibility, }; use crate::class_traits::{TypeDef, WritableName}; use crate::source_map::{ @@ -747,31 +747,30 @@ impl ClassParseSession<'_> { let implements_targets = collect_implements_targets(pair.clone()); match kind.as_str() { - "abstract class" => { + "abstract class" | "class" => { let mut def = self.parse_type_def_into::(pair)?; - def.source_location = source_location; - def.is_abstract = true; def.template_parameters = resolve_type_template_parameters( explicit_template_parameters, &def.name, &raw_type_def, ); - def.extends = extends_targets; - def.implements = implements_targets; - Ok(Element::ClassDef(def)) - } - "class" => { - let mut def = self.parse_type_def_into::(pair)?; def.source_location = source_location; - def.template_parameters = resolve_type_template_parameters( - explicit_template_parameters, - &def.name, - &raw_type_def, - ); + def.is_abstract = kind == "abstract class"; def.extends = extends_targets; def.implements = implements_targets; Ok(Element::ClassDef(def)) } + "entity" => { + let def = self.parse_type_def_into::(pair)?; + Ok(Element::EntityDef(EntityDef { + name: def.name, + namespace: def.namespace, + package: def.package, + stereotypes: def.stereotypes, + methods: def.methods, + source_location, + })) + } "struct" => { let mut def = self.parse_type_def_into::(pair)?; def.source_location = source_location; @@ -1567,6 +1566,7 @@ mod tests { Element::ClassDef(def) => &def.name, Element::StructDef(def) => &def.name, Element::InterfaceDef(def) => &def.name, + Element::EntityDef(def) => &def.name, Element::EnumDef(def) => &def.name, }; assert_eq!(name.internal, spelling, "`{keyword} {spelling}`"); diff --git a/plantuml/parser/puml_parser/src/class_diagram/src/lib.rs b/plantuml/parser/puml_parser/src/class_diagram/src/lib.rs index f2faeb0d..e8f1af48 100644 --- a/plantuml/parser/puml_parser/src/class_diagram/src/lib.rs +++ b/plantuml/parser/puml_parser/src/class_diagram/src/lib.rs @@ -16,8 +16,8 @@ mod class_traits; mod source_map; pub use class_ast::{ - Attribute, ClassDef, ClassUmlFile, ClassUmlTopLevel, Element, EnumDef, EnumItem, EnumValue, - InterfaceDef, Method, Name, Namespace, Package, Param, Relationship, StructDef, TypeAlias, - Visibility, + Attribute, ClassDef, ClassUmlFile, ClassUmlTopLevel, Element, EntityDef, EnumDef, EnumItem, + EnumValue, InterfaceDef, Method, Name, Namespace, Package, Param, Relationship, StructDef, + TypeAlias, Visibility, }; pub use class_parser::{ClassError, PumlClassParser}; diff --git a/plantuml/parser/puml_parser/src/class_diagram/test/integration_test.rs b/plantuml/parser/puml_parser/src/class_diagram/test/integration_test.rs index 331b0fd7..48a821a3 100644 --- a/plantuml/parser/puml_parser/src/class_diagram/test/integration_test.rs +++ b/plantuml/parser/puml_parser/src/class_diagram/test/integration_test.rs @@ -105,6 +105,16 @@ fn test_ignored_visibility_statements() { run_class_diagram_parser_case("ignored_visibility_statements"); } +#[test] +fn test_free_function_entity() { + run_class_diagram_parser_case("free_function_entity"); +} + +#[test] +fn test_keyword_prefixed_relationship() { + run_class_diagram_parser_case("keyword_prefixed_relationship"); +} + #[test] fn test_method_modifier_placement() { run_class_diagram_parser_case("method_modifier_placement"); diff --git a/plantuml/parser/puml_parser/src/grammar/activity.pest b/plantuml/parser/puml_parser/src/grammar/activity.pest index eef3a36f..e4589516 100644 --- a/plantuml/parser/puml_parser/src/grammar/activity.pest +++ b/plantuml/parser/puml_parser/src/grammar/activity.pest @@ -94,8 +94,6 @@ swimlane_text = @{ (!("|" | NEWLINE) ~ ANY)+ } -keyword_boundary = _{ !ASCII_ALPHANUMERIC } - control_stmt = { "break" | "kill" | "detach" } // Action diff --git a/plantuml/parser/puml_parser/src/grammar/class.pest b/plantuml/parser/puml_parser/src/grammar/class.pest index 1d342b7a..ef7ccc50 100644 --- a/plantuml/parser/puml_parser/src/grammar/class.pest +++ b/plantuml/parser/puml_parser/src/grammar/class.pest @@ -139,7 +139,8 @@ type_def = { type_def_with_body | type_def_decl } unsupported_object_def = { object_def_with_body | object_def_decl } type_def_decl = { type_kind ~ named ~ type_decl_suffix* } -object_def_decl = { "object" ~ named ~ type_decl_suffix* } +object_def_decl = { object_keyword ~ named ~ type_decl_suffix* } +object_keyword = @{ "object" ~ keyword_boundary } type_decl_suffix = _{ extends_clause | implements_clause | link_annotation | stereotype | color } template_param = @{ identifier ~ varargs? } template_param_list = _{ "<" ~ template_param ~ ("," ~ template_param)* ~ ">" } @@ -152,11 +153,15 @@ implements_target = { class_name_path } type_def_with_body = { type_def_decl ~ &(EOL* ~ "{") ~ EOL* ~ class_body ~ ";"? } object_def_with_body = { object_def_decl ~ &(EOL* ~ "{") ~ EOL* ~ class_body ~ ";"? } -type_kind = { - "abstract" ~ "class" - | "class" - | "struct" - | "interface" +type_kind = @{ + ( + "abstract" ~ WHITESPACE+ ~ "class" + | "class" + | "entity" + | "struct" + | "interface" + ) + ~ keyword_boundary } class_body = { "{" ~ EOL* ~ (ignored_mock_method_stmt | class_member | class_body_separator)* ~ EOL* ~ "}" } @@ -438,7 +443,8 @@ varargs = { "..." } /////////////////////////////////////////////////////////////// // type definition /////////////////////////////////////////////////////////////// -enum_def = { "enum" ~ named ~ stereotype? ~ enum_body? ~ ";"? } +enum_def = { enum_keyword ~ named ~ stereotype? ~ enum_body? ~ ";"? } +enum_keyword = @{ "enum" ~ keyword_boundary } enum_body = { "{" ~ EOL* ~ enum_item* ~ EOL* ~ "}" } diff --git a/plantuml/parser/puml_parser/src/grammar/common.pest b/plantuml/parser/puml_parser/src/grammar/common.pest index 6cca588a..0f07c5be 100644 --- a/plantuml/parser/puml_parser/src/grammar/common.pest +++ b/plantuml/parser/puml_parser/src/grammar/common.pest @@ -66,6 +66,7 @@ diagram_id = @{ (ASCII_ALPHANUMERIC | "_" | "-" | "." | "@" | " ")+ } puml_name = { STRING | diagram_id } identifier = @{ (ASCII_ALPHANUMERIC | "_")+ } +keyword_boundary = _{ !identifier } empty_line = { WHITESPACE* ~ EOL } //////////////////////////////////////////////////////////////////////////////// @@ -379,5 +380,5 @@ arrow_mid_decor = @{ "(0)" | "(0" | "0)" | "()" | "(" | ")" | "0" } // ---------- Direction ---------- arrow_direction = @{ ("up" | "u" | "right" | "ri" | "r" | "down" | "do" | "d" | "left" | "le" | "l") - ~ !(ASCII_ALPHANUMERIC | "_") + ~ keyword_boundary } diff --git a/plantuml/parser/puml_parser/tests/class_diagram/free_function_entity/free_function_entity.puml b/plantuml/parser/puml_parser/tests/class_diagram/free_function_entity/free_function_entity.puml new file mode 100644 index 00000000..71c26aa3 --- /dev/null +++ b/plantuml/parser/puml_parser/tests/class_diagram/free_function_entity/free_function_entity.puml @@ -0,0 +1,24 @@ +' ******************************************************************************* +' Copyright (c) 2026 Contributors to the Eclipse Foundation +' +' See the NOTICE file(s) distributed with this work for additional +' information regarding copyright ownership. +' +' This program and the accompanying materials are made available under the +' terms of the Apache License Version 2.0 which is available at +' https://www.apache.org/licenses/LICENSE-2.0 +' +' SPDX-License-Identifier: Apache-2.0 +' ******************************************************************************* + +@startuml free_function_entity + +namespace amp { + namespace algorithm { + entity clamp <> { + + clamp(value, min, max) + } + } +} + +@enduml diff --git a/plantuml/parser/puml_parser/tests/class_diagram/free_function_entity/output.json b/plantuml/parser/puml_parser/tests/class_diagram/free_function_entity/output.json new file mode 100644 index 00000000..8d5d0b70 --- /dev/null +++ b/plantuml/parser/puml_parser/tests/class_diagram/free_function_entity/output.json @@ -0,0 +1,82 @@ +{ + "free_function_entity.puml": { + "name": "free_function_entity", + "elements": [ + { + "Namespace": { + "name": { + "internal": "amp", + "display": null + }, + "source_location": { + "file": "", + "line": 16 + }, + "types": [], + "relationships": [], + "namespaces": [ + { + "name": { + "internal": "algorithm", + "display": null + }, + "source_location": { + "file": "", + "line": 17 + }, + "relationships": [], + "types": [ + { + "EntityDef": { + "name": { + "internal": "clamp", + "display": null + }, + "namespace": "algorithm", + "package": "", + "stereotypes": [ + "function" + ], + "methods": [ + { + "visibility": "Public", + "name": "clamp", + "template_parameters": null, + "params": [ + { + "name": "value", + "param_type": null + }, + { + "name": "min", + "param_type": null + }, + { + "name": "max", + "param_type": null + } + ], + "type": null, + "modifiers": [], + "source_location": { + "file": "", + "line": 19 + } + } + ], + "source_location": { + "file": "", + "line": 18 + } + } + } + ], + "namespaces": [] + } + ] + } + } + ], + "relationships": [] + } +} diff --git a/plantuml/parser/puml_parser/tests/class_diagram/keyword_prefixed_relationship/keyword_prefixed_relationship.puml b/plantuml/parser/puml_parser/tests/class_diagram/keyword_prefixed_relationship/keyword_prefixed_relationship.puml new file mode 100644 index 00000000..1fb10801 --- /dev/null +++ b/plantuml/parser/puml_parser/tests/class_diagram/keyword_prefixed_relationship/keyword_prefixed_relationship.puml @@ -0,0 +1,24 @@ +' ******************************************************************************* +' Copyright (c) 2026 Contributors to the Eclipse Foundation +' +' See the NOTICE file(s) distributed with this work for additional +' information regarding copyright ownership. +' +' This program and the accompanying materials are made available under the +' terms of the Apache License Version 2.0 which is available at +' https://www.apache.org/licenses/LICENSE-2.0 +' +' SPDX-License-Identifier: Apache-2.0 +' ******************************************************************************* + +@startuml keyword_prefixed_relationship + +class1 -> Foo +entity_manager -> Foo +struct1 -> Foo +interface1 -> Foo +object1 -> Foo +enum1 -> Foo +package1 -> Foo + +@enduml diff --git a/plantuml/parser/puml_parser/tests/class_diagram/keyword_prefixed_relationship/output.json b/plantuml/parser/puml_parser/tests/class_diagram/keyword_prefixed_relationship/output.json new file mode 100644 index 00000000..70c4c0e2 --- /dev/null +++ b/plantuml/parser/puml_parser/tests/class_diagram/keyword_prefixed_relationship/output.json @@ -0,0 +1,57 @@ +{ + "keyword_prefixed_relationship.puml": { + "name": "keyword_prefixed_relationship", + "elements": [], + "relationships": [ + { + "left": "class1", + "right": "Foo", + "arrow": { "left": null, "line": { "raw": "-" }, "middle": null, "right": { "raw": ">" } }, + "label": null, + "source_location": { "file": "", "line": 16 } + }, + { + "left": "entity_manager", + "right": "Foo", + "arrow": { "left": null, "line": { "raw": "-" }, "middle": null, "right": { "raw": ">" } }, + "label": null, + "source_location": { "file": "", "line": 17 } + }, + { + "left": "struct1", + "right": "Foo", + "arrow": { "left": null, "line": { "raw": "-" }, "middle": null, "right": { "raw": ">" } }, + "label": null, + "source_location": { "file": "", "line": 18 } + }, + { + "left": "interface1", + "right": "Foo", + "arrow": { "left": null, "line": { "raw": "-" }, "middle": null, "right": { "raw": ">" } }, + "label": null, + "source_location": { "file": "", "line": 19 } + }, + { + "left": "object1", + "right": "Foo", + "arrow": { "left": null, "line": { "raw": "-" }, "middle": null, "right": { "raw": ">" } }, + "label": null, + "source_location": { "file": "", "line": 20 } + }, + { + "left": "enum1", + "right": "Foo", + "arrow": { "left": null, "line": { "raw": "-" }, "middle": null, "right": { "raw": ">" } }, + "label": null, + "source_location": { "file": "", "line": 21 } + }, + { + "left": "package1", + "right": "Foo", + "arrow": { "left": null, "line": { "raw": "-" }, "middle": null, "right": { "raw": ">" } }, + "label": null, + "source_location": { "file": "", "line": 22 } + } + ] + } +} diff --git a/plantuml/parser/puml_resolver/src/class_diagram/src/class_resolver.rs b/plantuml/parser/puml_resolver/src/class_diagram/src/class_resolver.rs index 93aa9ed6..4e375fc2 100644 --- a/plantuml/parser/puml_resolver/src/class_diagram/src/class_resolver.rs +++ b/plantuml/parser/puml_resolver/src/class_diagram/src/class_resolver.rs @@ -582,6 +582,10 @@ impl ClassResolver { ) -> Result<(), ClassPumlResolverError> { match element { Element::EnumDef(def) => self.process_enum(def, scope), + Element::EntityDef(def) if self.is_function_entity(def) => { + self.process_free_function_entity(def, scope) + } + Element::EntityDef(_) => Ok(()), _ => { let entity_type = match element { Element::ClassDef(def) if def.is_abstract => EntityType::AbstractClass, @@ -595,6 +599,42 @@ impl ClassResolver { } } + fn is_function_entity(&self, def: &class_parser::EntityDef) -> bool { + def.stereotypes + .iter() + .any(|stereotype| stereotype.eq_ignore_ascii_case("function")) + } + + fn process_free_function_entity( + &mut self, + def: &class_parser::EntityDef, + scope: &DeclarationScope, + ) -> Result<(), ClassPumlResolverError> { + let (declared, _) = Self::declare( + &def.name, + IdentityKind::ClassLike, + scope, + Some(&def.source_location), + )?; + let enclosing_namespace_id = self.enclosing_namespace_id(&declared); + + self.logic + .free_functions + .extend(def.methods.iter().map(|method| FreeFunctionDecl { + name: method.name.clone(), + enclosing_namespace_id: enclosing_namespace_id.clone(), + return_type: method.r#type.clone(), + parameters: method.params.iter().map(Self::convert_param).collect(), + template_parameters: Self::convert_template_parameters_with_pack_expansions( + &method.template_parameters, + &method.params, + ), + source_location: method.source_location.clone(), + })); + + Ok(()) + } + fn process_class( &mut self, def: &Element, @@ -637,6 +677,9 @@ impl ClassResolver { &i.template_parameters, &i.source_location, ), + Element::EntityDef(_) => { + unreachable!("EntityDef should not be passed to process_class") + } Element::EnumDef(_) => { unreachable!("EnumDef should not be passed to process_class") } diff --git a/plantuml/parser/puml_resolver/src/class_diagram/test/class_resolver_test.rs b/plantuml/parser/puml_resolver/src/class_diagram/test/class_resolver_test.rs index e863d3bf..aeca2f2f 100644 --- a/plantuml/parser/puml_resolver/src/class_diagram/test/class_resolver_test.rs +++ b/plantuml/parser/puml_resolver/src/class_diagram/test/class_resolver_test.rs @@ -122,6 +122,11 @@ fn test_c_variadic_method() { run_class_resolver_case("c_variadic_method"); } +#[test] +fn test_free_function_entity() { + run_class_resolver_case("free_function_entity"); +} + #[test] fn test_class_template_pack() { run_class_resolver_case("class_template_pack"); diff --git a/plantuml/parser/puml_resolver/src/uid_normalization.rs b/plantuml/parser/puml_resolver/src/uid_normalization.rs index bf122917..bd32b350 100644 --- a/plantuml/parser/puml_resolver/src/uid_normalization.rs +++ b/plantuml/parser/puml_resolver/src/uid_normalization.rs @@ -20,8 +20,8 @@ pub use uid_utils::{is_identifier_path, join, normalize, normalized_segments}; /// Kind of element a name belongs to. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum IdentityKind { - /// class, abstract class, interface, enum, struct: a trailing template - /// argument list is not part of the name. + /// class, abstract class, interface, enum, struct, entity: + /// a trailing template argument list is not part of the name. ClassLike, Other, }