Repository: ErykMroczek/mofmt (language: python)

Issue:
Wrong indent for annotation in otherwise empty model
Similar to #31, annotation indent is too high if the model only contains an annotation (like for documentation classes or extendable icons).

This code for example:
```
partial model Example
  "Icon for runnable examples"
  annotation (Icon(graphics = {Ellipse(extent = {{-100, -100}, {100, 100}})}));
end Example;
```

Produces:
```
partial model Example
  "Icon for runnable examples"
    annotation (Icon(graphics = {Ellipse(extent = {{-100, -100}, {100, 100}})}));

end Example;
```

The error does not occur when the model comment is deleted or if variable declarations are added.

Tested with mofmt 0.5.0 and 0.4.2.


Discussion / hints:
Eh, the issue is that Modelica syntax in the *composition* context is quite liberal:

```none
[ external [ language-specification ] [ external-function-call ] [ annotation-clause ] ";" ]

[ annotation-clause ";" ] 
```

Problem is to differentiate between first and second annotation, because it is syntactically valid to have `external` keyword followed by the `annotation-clause`, and nothing more. Would it annotation refer to the external element, or to the whole class?

It can be said that from the semantical standpoint it doesn't make sense to have an annotation to the external function without the `external-function-call` (prototype), but the thing is it can still happen., and needs to be dealt with somehow. I am not sure at this moment, what heuristic I should apply here to guard against such edge cases.

I will fix this specific issue though.

> Problem is to differentiate between first and second annotation, because it is syntactically valid to have `external` keyword followed by the `annotation-clause`, and nothing more. Would it annotation refer to the external element, or to the whole class?

I don't understand. You mean something like this?
```
function f
  external annotation(...);
end f;
```
Here the annotation is clearly part of the external keyword. The class annotation would be
```
function f
  external;
  annotation(...);
end f;
```

You are right, of course.
Sorry, looks like I was tired when I wrote that, and missed the semicolon :smile:.
BTW, as we mentioned the `external` part - currently `mofmt` formats the external calls like this:

```modelica
external "C"

  foo = bar(x, y, z)
    annotation (...);
```

I decided that it is more in line with how `equation` section etc. are formatted, and the code is more consistent visually. But previously it was something like:

```modelica
external "C" foo = bar(x, y, z)
  annotation (...);
```

Any issue with the new approach?

I ask as there are parts that I am not entirely sure about yet, and I wish to finally fix the output before the `0.6.0` version.
I don't think it's a bad idea, but it cannot be fully compared with `equation` sections, as `equation` does not end with an `;`
Moreover, Dymola immediately removes the newline between `external`  and `foo` as soon as you open the model.

How about this style?
```
external "C"
  foo = bar(x, y, z)
    annotation (...);
```

Still very readable and Dymola does not destroy it on first sight.
Turning off Dymola's autoformatting would be the most welcome feature.

I agree that it is not fully equivalent to `equation` syntax-wise, but still when go through files I "scan" them in a similar fashion.

Let's leave it for now. I will think about it.

Fix patch:
diff --git a/src/formatting.rs b/src/formatting.rs
index 15b17f9..a1c9d20 100644
--- a/src/formatting.rs
+++ b/src/formatting.rs
@@ -467,60 +467,66 @@ fn enumeration_literal(f: &mut Formatter, tree: Tree) {
 }
 
 fn composition(f: &mut Formatter, tree: Tree) {
+    let mut prev_rule = SyntaxKind::Error;
     for child in tree.children {
         match child {
-            Child::Tree(tree) => match tree.kind {
-                SyntaxKind::ElementList => {
-                    f.markers.push(Marker::Indent);
-                    f.handle_break(tree.start(), Blank::Required);
-                    element_list(f, tree);
-                    f.markers.push(Marker::Dedent);
-                }
-                SyntaxKind::EquationSection => {
-                    f.handle_break(tree.start(), Blank::Required);
-                    equation_section(f, tree);
-                }
-                SyntaxKind::AlgorithmSection => {
-                    f.handle_break(tree.start(), Blank::Required);
-                    algorithm_section(f, tree);
-                }
-                SyntaxKind::LanguageSpecification => {
-                    f.markers.push(Marker::Space);
-                    language_specification(f, tree);
-                }
-                SyntaxKind::ExternalFunctionCall => {
-                    f.markers.push(Marker::Indent);
-                    f.handle_break(tree.start(), Blank::Required);
-                    external_function_call(f, tree);
-                    f.markers.push(Marker::Dedent);
-                }
-                SyntaxKind::AnnotationClause => {
-                    f.markers.push(Marker::Indent);
-                    let extern_element_annotation = [
-                        ModelicaToken::RParen,
-                        ModelicaToken::External,
-                        ModelicaToken::String,
-                    ]
-                    .contains(&f.prev_tok);
-                    if extern_element_annotation {
+            Child::Tree(tree) => {
+                let kind = tree.kind;
+                match tree.kind {
+                    SyntaxKind::ElementList => {
                         f.markers.push(Marker::Indent);
+                        f.handle_break(tree.start(), Blank::Required);
+                        element_list(f, tree);
+                        f.markers.push(Marker::Dedent);
                     }
-                    f.handle_break(
-                        tree.start(),
-                        if !extern_element_annotation {
-                            Blank::Required
-                        } else {
-                            Blank::Illegal
-                        },
-                    );
-                    annotation_clause(f, tree);
-                    f.markers.push(Marker::Dedent);
-                    if extern_element_annotation {
+                    SyntaxKind::EquationSection => {
+                        f.handle_break(tree.start(), Blank::Required);
+                        equation_section(f, tree);
+                    }
+                    SyntaxKind::AlgorithmSection => {
+                        f.handle_break(tree.start(), Blank::Required);
+                        algorithm_section(f, tree);
+                    }
+                    SyntaxKind::LanguageSpecification => {
+                        f.markers.push(Marker::Space);
+                        language_specification(f, tree);
+                    }
+                    SyntaxKind::ExternalFunctionCall => {
+                        f.markers.push(Marker::Indent);
+                        f.handle_break(tree.start(), Blank::Required);
+                        external_function_call(f, tree);
                         f.markers.push(Marker::Dedent);
                     }
+                    SyntaxKind::AnnotationClause => {
+                        f.markers.push(Marker::Indent);
+                        let extern_element_annotation = f.prev_tok == ModelicaToken::External
+                            || ([
+                                SyntaxKind::LanguageSpecification,
+                                SyntaxKind::ExternalFunctionCall,
+                            ]
+                            .contains(&prev_rule)
+                                && f.prev_tok != ModelicaToken::Semicolon);
+                        if extern_element_annotation {
+                            f.markers.push(Marker::Indent);
+                        }
+                        f.handle_break(
+                            tree.start(),
+                            if !extern_element_annotation {
+                                Blank::Required
+                            } else {
+                                Blank::Illegal
+                            },
+                        );
+                        annotation_clause(f, tree);
+                        f.markers.push(Marker::Dedent);
+                        if extern_element_annotation {
+                            f.markers.push(Marker::Dedent);
+                        }
+                    }
+                    _ => unreachable!(),
                 }
-                _ => unreachable!(),
-            },
+                prev_rule = kind;
+            }
             Child::Token(tok) => {
                 let kind = tok.kind;
                 if [


Test patch:
diff --git a/tests/samples/code-input.mo b/tests/samples/code-input.mo
index 459df3a..96ed771 100644
--- a/tests/samples/code-input.mo
+++ b/tests/samples/code-input.mo
@@ -204,6 +204,14 @@ impure function Baz "To check annotations after empty sections"
 algorithm
 annotation ();
 end Baz;
+function Baz "To check annotations after decriptions"
+annotation ();
+end Baz;
+function Extern "To check annotations to external functions"
+external
+annotation ();
+annotation ();
+end Extern;
 partial function Bar "Just in case"
   initial algorithm
 
diff --git a/tests/samples/code-output.mo b/tests/samples/code-output.mo
index 4cac7c8..9481b8e 100644
--- a/tests/samples/code-output.mo
+++ b/tests/samples/code-output.mo
@@ -270,6 +270,21 @@ algorithm
   annotation ();
 
 end Baz;
+function Baz
+  "To check annotations after decriptions"
+
+  annotation ();
+
+end Baz;
+function Extern
+  "To check annotations to external functions"
+
+external
+    annotation ();
+
+  annotation ();
+
+end Extern;
 partial function Bar
   "Just in case"