Repository: ErykMroczek/mofmt (language: python)

Issue:
Line endings are not preserved on Windows
mofmt 0.4.1 stores all changed files with LF line endings, also for files using Windows-style CRLF.

To reproduce, perform the following steps (e.g. with Git Bash on Windows):

```
$ printf "model M\r\nextends X;\r\nend M;" > M.mo
$ file M.mo
M.mo: ASCII text, with CRLF line terminators
$ mofmt M.mo
$ file M.mo
M.mo: ASCII text
```

Moreover, line endings in HTML code are not changed, which results in mixed line endings on Windows.
A simple test is to store this code as `Doc.mo` with CRLF:
```
model Doc
    annotation (
      Documentation(
        info = "<html>
Foo
Bar
</html>"));
end Doc;
```

Then run it through mofmt.
`cat -e` shows us that the updated file uses LF for the lines with regular Modelica code and CRLF in the HTML section:

```
$ cat -e Doc.mo
model Doc$
    annotation ($
      Documentation($
        info = "<html>^M$
Foo^M$
Bar^M$
</html>"));$
$
end Doc;$
```




Discussion / hints:
Do you consider simply that the mixing is an issue, or that `mofmt` shouldn't change encoding at all?
At my job I am using Git on Windows with `core.autocrlf false`, and personally I am using Linux, so to be honest I kinda ignored the issue.
In my opinion the line endings should be preserved. Two reasons for Windows users:

1. Git for Windows uses `autocrlf=true` as default (set in `C:\ProgramData\Git\config`), so most Windows users will use this setting
2. I think Dymola on Windows always stores with CRLF. I am not aware of an option to change that.

Hence, a typical Dymola user working on Windows has .mo files with CRLF. Running mofmt on those files will show lots of changed files in `git status`. They disappear when you stage them, but this is annoying and confusing.
I guess you're right. It was just me being lazy.

Fix patch:
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..176a458
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,1 @@
+* text=auto
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 2d56f0a..85e1886 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,57 +1,57 @@
-name: CI
-
-on:
-  workflow_dispatch:
-  pull_request:
-    branches: [ "main" ]
-
-jobs:
-
-  format:
-    name: Rustfmt
-    runs-on: ubuntu-latest
-    timeout-minutes: 10
-    steps:
-      - uses: actions/checkout@v3
-      - uses: dtolnay/rust-toolchain@master
-        with:
-          toolchain: stable
-          components: rustfmt
-      - run: cargo fmt --all --check
-
-  clippy:
-    name: Clippy
-    runs-on: ubuntu-latest
-    timeout-minutes: 10
-    steps:
-      - uses: actions/checkout@v3
-      - uses: dtolnay/rust-toolchain@master
-        with:
-          toolchain: stable
-          components: clippy
-      - run: cargo clippy --workspace --all-features -- -D warnings
-
-  build_and_test_linux:
-    name: Build and Test (Linux)
-    runs-on: ubuntu-latest
-    timeout-minutes: 10
-    steps:
-      - uses: actions/checkout@v3
-      - uses: dtolnay/rust-toolchain@stable
-        with:
-          toolchain: stable
-      - run: cargo test --all-targets
-
-  build_and_test_win:
-    name: Build and Test (Windows)
-    runs-on: windows-latest
-    timeout-minutes: 10
-    steps:
-      - name: Prepare symlink configuration
-        run: git config --global core.symlinks true
-
-      - uses: actions/checkout@v3
-      - uses: dtolnay/rust-toolchain@stable
-        with:
-          toolchain: stable
-      - run: cargo test --all-targets
+name: CI
+
+on:
+  workflow_dispatch:
+  pull_request:
+    branches: [ "main" ]
+
+jobs:
+
+  format:
+    name: Rustfmt
+    runs-on: ubuntu-latest
+    timeout-minutes: 10
+    steps:
+      - uses: actions/checkout@v3
+      - uses: dtolnay/rust-toolchain@master
+        with:
+          toolchain: stable
+          components: rustfmt
+      - run: cargo fmt --all --check
+
+  clippy:
+    name: Clippy
+    runs-on: ubuntu-latest
+    timeout-minutes: 10
+    steps:
+      - uses: actions/checkout@v3
+      - uses: dtolnay/rust-toolchain@master
+        with:
+          toolchain: stable
+          components: clippy
+      - run: cargo clippy --workspace --all-features -- -D warnings
+
+  build_and_test_linux:
+    name: Build and Test (Linux)
+    runs-on: ubuntu-latest
+    timeout-minutes: 10
+    steps:
+      - uses: actions/checkout@v3
+      - uses: dtolnay/rust-toolchain@stable
+        with:
+          toolchain: stable
+      - run: cargo test --all-targets
+
+  build_and_test_win:
+    name: Build and Test (Windows)
+    runs-on: windows-latest
+    timeout-minutes: 10
+    steps:
+      - name: Prepare symlink configuration
+        run: git config --global core.symlinks true
+
+      - uses: actions/checkout@v3
+      - uses: dtolnay/rust-toolchain@stable
+        with:
+          toolchain: stable
+      - run: cargo test --all-targets
diff --git a/LICENSE b/LICENSE
index f7d9d73..687997e 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,375 +1,375 @@
-Mozilla Public License Version 2.0
-==================================
-
-Copyright (c) 2023 Eryk Mroczek
-
-1. Definitions
---------------
-
-1.1. "Contributor"
-    means each individual or legal entity that creates, contributes to
-    the creation of, or owns Covered Software.
-
-1.2. "Contributor Version"
-    means the combination of the Contributions of others (if any) used
-    by a Contributor and that particular Contributor's Contribution.
-
-1.3. "Contribution"
-    means Covered Software of a particular Contributor.
-
-1.4. "Covered Software"
-    means Source Code Form to which the initial Contributor has attached
-    the notice in Exhibit A, the Executable Form of such Source Code
-    Form, and Modifications of such Source Code Form, in each case
-    including portions thereof.
-
-1.5. "Incompatible With Secondary Licenses"
-    means
-
-    (a) that the initial Contributor has attached the notice described
-        in Exhibit B to the Covered Software; or
-
-    (b) that the Covered Software was made available under the terms of
-        version 1.1 or earlier of the License, but not also under the
-        terms of a Secondary License.
-
-1.6. "Executable Form"
-    means any form of the work other than Source Code Form.
-
-1.7. "Larger Work"
-    means a work that combines Covered Software with other material, in
-    a separate file or files, that is not Covered Software.
-
-1.8. "License"
-    means this document.
-
-1.9. "Licensable"
-    means having the right to grant, to the maximum extent possible,
-    whether at the time of the initial grant or subsequently, any and
-    all of the rights conveyed by this License.
-
-1.10. "Modifications"
-    means any of the following:
-
-    (a) any file in Source Code Form that results from an addition to,
-        deletion from, or modification of the contents of Covered
-        Software; or
-
-    (b) any new file in Source Code Form that contains any Covered
-        Software.
-
-1.11. "Patent Claims" of a Contributor
-    means any patent claim(s), including without limitation, method,
-    process, and apparatus claims, in any patent Licensable by such
-    Contributor that would be infringed, but for the grant of the
-    License, by the making, using, selling, offering for sale, having
-    made, import, or transfer of either its Contributions or its
-    Contributor Version.
-
-1.12. "Secondary License"
-    means either the GNU General Public License, Version 2.0, the GNU
-    Lesser General Public License, Version 2.1, the GNU Affero General
-    Public License, Version 3.0, or any later versions of those
-    licenses.
-
-1.13. "Source Code Form"
-    means the form of the work preferred for making modifications.
-
-1.14. "You" (or "Your")
-    means an individual or a legal entity exercising rights under this
-    License. For legal entities, "You" includes any entity that
-    controls, is controlled by, or is under common control with You. For
-    purposes of this definition, "control" means (a) the power, direct
-    or indirect, to cause the direction or management of such entity,
-    whether by contract or otherwise, or (b) ownership of more than
-    fifty percent (50%) of the outstanding shares or beneficial
-    ownership of such entity.
-
-2. License Grants and Conditions
---------------------------------
-
-2.1. Grants
-
-Each Contributor hereby grants You a world-wide, royalty-free,
-non-exclusive license:
-
-(a) under intellectual property rights (other than patent or trademark)
-    Licensable by such Contributor to use, reproduce, make available,
-    modify, display, perform, distribute, and otherwise exploit its
-    Contributions, either on an unmodified basis, with Modifications, or
-    as part of a Larger Work; and
-
-(b) under Patent Claims of such Contributor to make, use, sell, offer
-    for sale, have made, import, and otherwise transfer either its
-    Contributions or its Contributor Version.
-
-2.2. Effective Date
-
-The licenses granted in Section 2.1 with respect to any Contribution
-become effective for each Contribution on the date the Contributor first
-distributes such Contribution.
-
-2.3. Limitations on Grant Scope
-
-The licenses granted in this Section 2 are the only rights granted under
-this License. No additional rights or licenses will be implied from the
-distribution or licensing of Covered Software under this License.
-Notwithstanding Section 2.1(b) above, no patent license is granted by a
-Contributor:
-
-(a) for any code that a Contributor has removed from Covered Software;
-    or
-
-(b) for infringements caused by: (i) Your and any other third party's
-    modifications of Covered Software, or (ii) the combination of its
-    Contributions with other software (except as part of its Contributor
-    Version); or
-
-(c) under Patent Claims infringed by Covered Software in the absence of
-    its Contributions.
-
-This License does not grant any rights in the trademarks, service marks,
-or logos of any Contributor (except as may be necessary to comply with
-the notice requirements in Section 3.4).
-
-2.4. Subsequent Licenses
-
-No Contributor makes additional grants as a result of Your choice to
-distribute the Covered Software under a subsequent version of this
-License (see Section 10.2) or under the terms of a Secondary License (if
-permitted under the terms of Section 3.3).
-
-2.5. Representation
-
-Each Contributor represents that the Contributor believes its
-Contributions are its original creation(s) or it has sufficient rights
-to grant the rights to its Contributions conveyed by this License.
-
-2.6. Fair Use
-
-This License is not intended to limit any rights You have under
-applicable copyright doctrines of fair use, fair dealing, or other
-equivalents.
-
-2.7. Conditions
-
-Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
-in Section 2.1.
-
-3. Responsibilities
--------------------
-
-3.1. Distribution of Source Form
-
-All distribution of Covered Software in Source Code Form, including any
-Modifications that You create or to which You contribute, must be under
-the terms of this License. You must inform recipients that the Source
-Code Form of the Covered Software is governed by the terms of this
-License, and how they can obtain a copy of this License. You may not
-attempt to alter or restrict the recipients' rights in the Source Code
-Form.
-
-3.2. Distribution of Executable Form
-
-If You distribute Covered Software in Executable Form then:
-
-(a) such Covered Software must also be made available in Source Code
-    Form, as described in Section 3.1, and You must inform recipients of
-    the Executable Form how they can obtain a copy of such Source Code
-    Form by reasonable means in a timely manner, at a charge no more
-    than the cost of distribution to the recipient; and
-
-(b) You may distribute such Executable Form under the terms of this
-    License, or sublicense it under different terms, provided that the
-    license for the Executable Form does not attempt to limit or alter
-    the recipients' rights in the Source Code Form under this License.
-
-3.3. Distribution of a Larger Work
-
-You may create and distribute a Larger Work under terms of Your choice,
-provided that You also comply with the requirements of this License for
-the Covered Software. If the Larger Work is a combination of Covered
-Software with a work governed by one or more Secondary Licenses, and the
-Covered Software is not Incompatible With Secondary Licenses, this
-License permits You to additionally distribute such Covered Software
-under the terms of such Secondary License(s), so that the recipient of
-the Larger Work may, at their option, further distribute the Covered
-Software under the terms of either this License or such Secondary
-License(s).
-
-3.4. Notices
-
-You may not remove or alter the substance of any license notices
-(including copyright notices, patent notices, disclaimers of warranty,
-or limitations of liability) contained within the Source Code Form of
-the Covered Software, except that You may alter any license notices to
-the extent required to remedy known factual inaccuracies.
-
-3.5. Application of Additional Terms
-
-You may choose to offer, and to charge a fee for, warranty, support,
-indemnity or liability obligations to one or more recipients of Covered
-Software. However, You may do so only on Your own behalf, and not on
-behalf of any Contributor. You must make it absolutely clear that any
-such warranty, support, indemnity, or liability obligation is offered by
-You alone, and You hereby agree to indemnify every Contributor for any
-liability incurred by such Contributor as a result of warranty, support,
-indemnity or liability terms You offer. You may include additional
-disclaimers of warranty and limitations of liability specific to any
-jurisdiction.
-
-4. Inability to Comply Due to Statute or Regulation
----------------------------------------------------
-
-If it is impossible for You to comply with any of the terms of this
-License with respect to some or all of the Covered Software due to
-statute, judicial order, or regulation then You must: (a) comply with
-the terms of this License to the maximum extent possible; and (b)
-describe the limitations and the code they affect. Such description must
-be placed in a text file included with all distributions of the Covered
-Software under this License. Except to the extent prohibited by statute
-or regulation, such description must be sufficiently detailed for a
-recipient of ordinary skill to be able to understand it.
-
-5. Termination
---------------
-
-5.1. The rights granted under this License will terminate automatically
-if You fail to comply with any of its terms. However, if You become
-compliant, then the rights granted under this License from a particular
-Contributor are reinstated (a) provisionally, unless and until such
-Contributor explicitly and finally terminates Your grants, and (b) on an
-ongoing basis, if such Contributor fails to notify You of the
-non-compliance by some reasonable means prior to 60 days after You have
-come back into compliance. Moreover, Your grants from a particular
-Contributor are reinstated on an ongoing basis if such Contributor
-notifies You of the non-compliance by some reasonable means, this is the
-first time You have received notice of non-compliance with this License
-from such Contributor, and You become compliant prior to 30 days after
-Your receipt of the notice.
-
-5.2. If You initiate litigation against any entity by asserting a patent
-infringement claim (excluding declaratory judgment actions,
-counter-claims, and cross-claims) alleging that a Contributor Version
-directly or indirectly infringes any patent, then the rights granted to
-You by any and all Contributors for the Covered Software under Section
-2.1 of this License shall terminate.
-
-5.3. In the event of termination under Sections 5.1 or 5.2 above, all
-end user license agreements (excluding distributors and resellers) which
-have been validly granted by You or Your distributors under this License
-prior to termination shall survive termination.
-
-************************************************************************
-*                                                                      *
-*  6. Disclaimer of Warranty                                           *
-*  -------------------------                                           *
-*                                                                      *
-*  Covered Software is provided under this License on an "as is"       *
-*  basis, without warranty of any kind, either expressed, implied, or  *
-*  statutory, including, without limitation, warranties that the       *
-*  Covered Software is free of defects, merchantable, fit for a        *
-*  particular purpose or non-infringing. The entire risk as to the     *
-*  quality and performance of the Covered Software is with You.        *
-*  Should any Covered Software prove defective in any respect, You     *
-*  (not any Contributor) assume the cost of any necessary servicing,   *
-*  repair, or correction. This disclaimer of warranty constitutes an   *
-*  essential part of this License. No use of any Covered Software is   *
-*  authorized under this License except under this disclaimer.         *
-*                                                                      *
-************************************************************************
-
-************************************************************************
-*                                                                      *
-*  7. Limitation of Liability                                          *
-*  --------------------------                                          *
-*                                                                      *
-*  Under no circumstances and under no legal theory, whether tort      *
-*  (including negligence), contract, or otherwise, shall any           *
-*  Contributor, or anyone who distributes Covered Software as          *
-*  permitted above, be liable to You for any direct, indirect,         *
-*  special, incidental, or consequential damages of any character      *
-*  including, without limitation, damages for lost profits, loss of    *
-*  goodwill, work stoppage, computer failure or malfunction, or any    *
-*  and all other commercial damages or losses, even if such party      *
-*  shall have been informed of the possibility of such damages. This   *
-*  limitation of liability shall not apply to liability for death or   *
-*  personal injury resulting from such party's negligence to the       *
-*  extent applicable law prohibits such limitation. Some               *
-*  jurisdictions do not allow the exclusion or limitation of           *
-*  incidental or consequential damages, so this exclusion and          *
-*  limitation may not apply to You.                                    *
-*                                                                      *
-************************************************************************
-
-8. Litigation
--------------
-
-Any litigation relating to this License may be brought only in the
-courts of a jurisdiction where the defendant maintains its principal
-place of business and such litigation shall be governed by laws of that
-jurisdiction, without reference to its conflict-of-law provisions.
-Nothing in this Section shall prevent a party's ability to bring
-cross-claims or counter-claims.
-
-9. Miscellaneous
-----------------
-
-This License represents the complete agreement concerning the subject
-matter hereof. If any provision of this License is held to be
-unenforceable, such provision shall be reformed only to the extent
-necessary to make it enforceable. Any law or regulation which provides
-that the language of a contract shall be construed against the drafter
-shall not be used to construe this License against a Contributor.
-
-10. Versions of the License
----------------------------
-
-10.1. New Versions
-
-Mozilla Foundation is the license steward. Except as provided in Section
-10.3, no one other than the license steward has the right to modify or
-publish new versions of this License. Each version will be given a
-distinguishing version number.
-
-10.2. Effect of New Versions
-
-You may distribute the Covered Software under the terms of the version
-of the License under which You originally received the Covered Software,
-or under the terms of any subsequent version published by the license
-steward.
-
-10.3. Modified Versions
-
-If you create software not governed by this License, and you want to
-create a new license for such software, you may create and use a
-modified version of this License if you rename the license and remove
-any references to the name of the license steward (except to note that
-such modified license differs from this License).
-
-10.4. Distributing Source Code Form that is Incompatible With Secondary
-Licenses
-
-If You choose to distribute Source Code Form that is Incompatible With
-Secondary Licenses under the terms of this version of the License, the
-notice described in Exhibit B of this License must be attached.
-
-Exhibit A - Source Code Form License Notice
--------------------------------------------
-
-  This Source Code Form is subject to the terms of the Mozilla Public
-  License, v. 2.0. If a copy of the MPL was not distributed with this
-  file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-If it is not possible or desirable to put the notice in a particular
-file, then You may include the notice in a location (such as a LICENSE
-file in a relevant directory) where a recipient would be likely to look
-for such a notice.
-
-You may add additional accurate notices of copyright ownership.
-
-Exhibit B - "Incompatible With Secondary Licenses" Notice
----------------------------------------------------------
-
-  This Source Code Form is "Incompatible With Secondary Licenses", as
-  defined by the Mozilla Public License, v. 2.0.
+Mozilla Public License Version 2.0
+==================================
+
+Copyright (c) 2023 Eryk Mroczek
+
+1. Definitions
+--------------
+
+1.1. "Contributor"
+    means each individual or legal entity that creates, contributes to
+    the creation of, or owns Covered Software.
+
+1.2. "Contributor Version"
+    means the combination of the Contributions of others (if any) used
+    by a Contributor and that particular Contributor's Contribution.
+
+1.3. "Contribution"
+    means Covered Software of a particular Contributor.
+
+1.4. "Covered Software"
+    means Source Code Form to which the initial Contributor has attached
+    the notice in Exhibit A, the Executable Form of such Source Code
+    Form, and Modifications of such Source Code Form, in each case
+    including portions thereof.
+
+1.5. "Incompatible With Secondary Licenses"
+    means
+
+    (a) that the initial Contributor has attached the notice described
+        in Exhibit B to the Covered Software; or
+
+    (b) that the Covered Software was made available under the terms of
+        version 1.1 or earlier of the License, but not also under the
+        terms of a Secondary License.
+
+1.6. "Executable Form"
+    means any form of the work other than Source Code Form.
+
+1.7. "Larger Work"
+    means a work that combines Covered Software with other material, in
+    a separate file or files, that is not Covered Software.
+
+1.8. "License"
+    means this document.
+
+1.9. "Licensable"
+    means having the right to grant, to the maximum extent possible,
+    whether at the time of the initial grant or subsequently, any and
+    all of the rights conveyed by this License.
+
+1.10. "Modifications"
+    means any of the following:
+
+    (a) any file in Source Code Form that results from an addition to,
+        deletion from, or modification of the contents of Covered
+        Software; or
+
+    (b) any new file in Source Code Form that contains any Covered
+        Software.
+
+1.11. "Patent Claims" of a Contributor
+    means any patent claim(s), including without limitation, method,
+    process, and apparatus claims, in any patent Licensable by such
+    Contributor that would be infringed, but for the grant of the
+    License, by the making, using, selling, offering for sale, having
+    made, import, or transfer of either its Contributions or its
+    Contributor Version.
+
+1.12. "Secondary License"
+    means either the GNU General Public License, Version 2.0, the GNU
+    Lesser General Public License, Version 2.1, the GNU Affero General
+    Public License, Version 3.0, or any later versions of those
+    licenses.
+
+1.13. "Source Code Form"
+    means the form of the work preferred for making modifications.
+
+1.14. "You" (or "Your")
+    means an individual or a legal entity exercising rights under this
+    License. For legal entities, "You" includes any entity that
+    controls, is controlled by, or is under common control with You. For
+    purposes of this definition, "control" means (a) the power, direct
+    or indirect, to cause the direction or management of such entity,
+    whether by contract or otherwise, or (b) ownership of more than
+    fifty percent (50%) of the outstanding shares or beneficial
+    ownership of such entity.
+
+2. License Grants and Conditions
+--------------------------------
+
+2.1. Grants
+
+Each Contributor hereby grants You a world-wide, royalty-free,
+non-exclusive license:
+
+(a) under intellectual property rights (other than patent or trademark)
+    Licensable by such Contributor to use, reproduce, make available,
+    modify, display, perform, distribute, and otherwise exploit its
+    Contributions, either on an unmodified basis, with Modifications, or
+    as part of a Larger Work; and
+
+(b) under Patent Claims of such Contributor to make, use, sell, offer
+    for sale, have made, import, and otherwise transfer either its
+    Contributions or its Contributor Version.
+
+2.2. Effective Date
+
+The licenses granted in Section 2.1 with respect to any Contribution
+become effective for each Contribution on the date the Contributor first
+distributes such Contribution.
+
+2.3. Limitations on Grant Scope
+
+The licenses granted in this Section 2 are the only rights granted under
+this License. No additional rights or licenses will be implied from the
+distribution or licensing of Covered Software under this License.
+Notwithstanding Section 2.1(b) above, no patent license is granted by a
+Contributor:
+
+(a) for any code that a Contributor has removed from Covered Software;
+    or
+
+(b) for infringements caused by: (i) Your and any other third party's
+    modifications of Covered Software, or (ii) the combination of its
+    Contributions with other software (except as part of its Contributor
+    Version); or
+
+(c) under Patent Claims infringed by Covered Software in the absence of
+    its Contributions.
+
+This License does not grant any rights in the trademarks, service marks,
+or logos of any Contributor (except as may be necessary to comply with
+the notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+No Contributor makes additional grants as a result of Your choice to
+distribute the Covered Software under a subsequent version of this
+License (see Section 10.2) or under the terms of a Secondary License (if
+permitted under the terms of Section 3.3).
+
+2.5. Representation
+
+Each Contributor represents that the Contributor believes its
+Contributions are its original creation(s) or it has sufficient rights
+to grant the rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+This License is not intended to limit any rights You have under
+applicable copyright doctrines of fair use, fair dealing, or other
+equivalents.
+
+2.7. Conditions
+
+Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
+in Section 2.1.
+
+3. Responsibilities
+-------------------
+
+3.1. Distribution of Source Form
+
+All distribution of Covered Software in Source Code Form, including any
+Modifications that You create or to which You contribute, must be under
+the terms of this License. You must inform recipients that the Source
+Code Form of the Covered Software is governed by the terms of this
+License, and how they can obtain a copy of this License. You may not
+attempt to alter or restrict the recipients' rights in the Source Code
+Form.
+
+3.2. Distribution of Executable Form
+
+If You distribute Covered Software in Executable Form then:
+
+(a) such Covered Software must also be made available in Source Code
+    Form, as described in Section 3.1, and You must inform recipients of
+    the Executable Form how they can obtain a copy of such Source Code
+    Form by reasonable means in a timely manner, at a charge no more
+    than the cost of distribution to the recipient; and
+
+(b) You may distribute such Executable Form under the terms of this
+    License, or sublicense it under different terms, provided that the
+    license for the Executable Form does not attempt to limit or alter
+    the recipients' rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+You may create and distribute a Larger Work under terms of Your choice,
+provided that You also comply with the requirements of this License for
+the Covered Software. If the Larger Work is a combination of Covered
+Software with a work governed by one or more Secondary Licenses, and the
+Covered Software is not Incompatible With Secondary Licenses, this
+License permits You to additionally distribute such Covered Software
+under the terms of such Secondary License(s), so that the recipient of
+the Larger Work may, at their option, further distribute the Covered
+Software under the terms of either this License or such Secondary
+License(s).
+
+3.4. Notices
+
+You may not remove or alter the substance of any license notices
+(including copyright notices, patent notices, disclaimers of warranty,
+or limitations of liability) contained within the Source Code Form of
+the Covered Software, except that You may alter any license notices to
+the extent required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+You may choose to offer, and to charge a fee for, warranty, support,
+indemnity or liability obligations to one or more recipients of Covered
+Software. However, You may do so only on Your own behalf, and not on
+behalf of any Contributor. You must make it absolutely clear that any
+such warranty, support, indemnity, or liability obligation is offered by
+You alone, and You hereby agree to indemnify every Contributor for any
+liability incurred by such Contributor as a result of warranty, support,
+indemnity or liability terms You offer. You may include additional
+disclaimers of warranty and limitations of liability specific to any
+jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+---------------------------------------------------
+
+If it is impossible for You to comply with any of the terms of this
+License with respect to some or all of the Covered Software due to
+statute, judicial order, or regulation then You must: (a) comply with
+the terms of this License to the maximum extent possible; and (b)
+describe the limitations and the code they affect. Such description must
+be placed in a text file included with all distributions of the Covered
+Software under this License. Except to the extent prohibited by statute
+or regulation, such description must be sufficiently detailed for a
+recipient of ordinary skill to be able to understand it.
+
+5. Termination
+--------------
+
+5.1. The rights granted under this License will terminate automatically
+if You fail to comply with any of its terms. However, if You become
+compliant, then the rights granted under this License from a particular
+Contributor are reinstated (a) provisionally, unless and until such
+Contributor explicitly and finally terminates Your grants, and (b) on an
+ongoing basis, if such Contributor fails to notify You of the
+non-compliance by some reasonable means prior to 60 days after You have
+come back into compliance. Moreover, Your grants from a particular
+Contributor are reinstated on an ongoing basis if such Contributor
+notifies You of the non-compliance by some reasonable means, this is the
+first time You have received notice of non-compliance with this License
+from such Contributor, and You become compliant prior to 30 days after
+Your receipt of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+infringement claim (excluding declaratory judgment actions,
+counter-claims, and cross-claims) alleging that a Contributor Version
+directly or indirectly infringes any patent, then the rights granted to
+You by any and all Contributors for the Covered Software under Section
+2.1 of this License shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all
+end user license agreements (excluding distributors and resellers) which
+have been validly granted by You or Your distributors under this License
+prior to termination shall survive termination.
+
+************************************************************************
+*                                                                      *
+*  6. Disclaimer of Warranty                                           *
+*  -------------------------                                           *
+*                                                                      *
+*  Covered Software is provided under this License on an "as is"       *
+*  basis, without warranty of any kind, either expressed, implied, or  *
+*  statutory, including, without limitation, warranties that the       *
+*  Covered Software is free of defects, merchantable, fit for a        *
+*  particular purpose or non-infringing. The entire risk as to the     *
+*  quality and performance of the Covered Software is with You.        *
+*  Should any Covered Software prove defective in any respect, You     *
+*  (not any Contributor) assume the cost of any necessary servicing,   *
+*  repair, or correction. This disclaimer of warranty constitutes an   *
+*  essential part of this License. No use of any Covered Software is   *
+*  authorized under this License except under this disclaimer.         *
+*                                                                      *
+************************************************************************
+
+************************************************************************
+*                                                                      *
+*  7. Limitation of Liability                                          *
+*  --------------------------                                          *
+*                                                                      *
+*  Under no circumstances and under no legal theory, whether tort      *
+*  (including negligence), contract, or otherwise, shall any           *
+*  Contributor, or anyone who distributes Covered Software as          *
+*  permitted above, be liable to You for any direct, indirect,         *
+*  special, incidental, or consequential damages of any character      *
+*  including, without limitation, damages for lost profits, loss of    *
+*  goodwill, work stoppage, computer failure or malfunction, or any    *
+*  and all other commercial damages or losses, even if such party      *
+*  shall have been informed of the possibility of such damages. This   *
+*  limitation of liability shall not apply to liability for death or   *
+*  personal injury resulting from such party's negligence to the       *
+*  extent applicable law prohibits such limitation. Some               *
+*  jurisdictions do not allow the exclusion or limitation of           *
+*  incidental or consequential damages, so this exclusion and          *
+*  limitation may not apply to You.                                    *
+*                                                                      *
+************************************************************************
+
+8. Litigation
+-------------
+
+Any litigation relating to this License may be brought only in the
+courts of a jurisdiction where the defendant maintains its principal
+place of business and such litigation shall be governed by laws of that
+jurisdiction, without reference to its conflict-of-law provisions.
+Nothing in this Section shall prevent a party's ability to bring
+cross-claims or counter-claims.
+
+9. Miscellaneous
+----------------
+
+This License represents the complete agreement concerning the subject
+matter hereof. If any provision of this License is held to be
+unenforceable, such provision shall be reformed only to the extent
+necessary to make it enforceable. Any law or regulation which provides
+that the language of a contract shall be construed against the drafter
+shall not be used to construe this License against a Contributor.
+
+10. Versions of the License
+---------------------------
+
+10.1. New Versions
+
+Mozilla Foundation is the license steward. Except as provided in Section
+10.3, no one other than the license steward has the right to modify or
+publish new versions of this License. Each version will be given a
+distinguishing version number.
+
+10.2. Effect of New Versions
+
+You may distribute the Covered Software under the terms of the version
+of the License under which You originally received the Covered Software,
+or under the terms of any subsequent version published by the license
+steward.
+
+10.3. Modified Versions
+
+If you create software not governed by this License, and you want to
+create a new license for such software, you may create and use a
+modified version of this License if you rename the license and remove
+any references to the name of the license steward (except to note that
+such modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary
+Licenses
+
+If You choose to distribute Source Code Form that is Incompatible With
+Secondary Licenses under the terms of this version of the License, the
+notice described in Exhibit B of this License must be attached.
+
+Exhibit A - Source Code Form License Notice
+-------------------------------------------
+
+  This Source Code Form is subject to the terms of the Mozilla Public
+  License, v. 2.0. If a copy of the MPL was not distributed with this
+  file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+If it is not possible or desirable to put the notice in a particular
+file, then You may include the notice in a location (such as a LICENSE
+file in a relevant directory) where a recipient would be likely to look
+for such a notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - "Incompatible With Secondary Licenses" Notice
+---------------------------------------------------------
+
+  This Source Code Form is "Incompatible With Secondary Licenses", as
+  defined by the Mozilla Public License, v. 2.0.
diff --git a/README.md b/README.md
index fa88f89..f493c13 100644
--- a/README.md
+++ b/README.md
@@ -1,45 +1,45 @@
-# mofmt - Modelica code formatter
-
-*mofmt* is a code formatter for [Modelica](https://modelica.org/)
-language. It aims to enhance readability and provide style constistency
-across different Modelica projects.
-
-Code style applied by the *mofmt* is described in `code-style.md`.
-
-## Installation and usage
-
-### Installation
-
-*mofmt* can be installed with `cargo`:
-
-```shell
-cargo install mofmt
-```
-
-or you can just grab one of the released libraries.
-
-On top of that, repo contains a necessary `pre-commit-hooks.yaml` file,
-so if you are using Git, you can delegate running *mofmt* to
-[pre-commit](https://pre-commit.com/) framework.
-
-### Usage
-
-*mofmt* takes one kind of argument - path that points to Modelica source
-file or directory that is supposed to contain such files. *mofmt* can
-accept multiple such arguments.
-
-```shell
-mofmt PATH ...
-```
-
-## TODO
-
-[ ] include HTML pretty-printer
-
-## License
-
-MPL-2.0
-
-## Authors
-
-Eryk Mroczek: <mroczek.eryk@gmail.com>
+# mofmt - Modelica code formatter
+
+*mofmt* is a code formatter for [Modelica](https://modelica.org/)
+language. It aims to enhance readability and provide style constistency
+across different Modelica projects.
+
+Code style applied by the *mofmt* is described in `code-style.md`.
+
+## Installation and usage
+
+### Installation
+
+*mofmt* can be installed with `cargo`:
+
+```shell
+cargo install mofmt
+```
+
+or you can just grab one of the released libraries.
+
+On top of that, repo contains a necessary `pre-commit-hooks.yaml` file,
+so if you are using Git, you can delegate running *mofmt* to
+[pre-commit](https://pre-commit.com/) framework.
+
+### Usage
+
+*mofmt* takes one kind of argument - path that points to Modelica source
+file or directory that is supposed to contain such files. *mofmt* can
+accept multiple such arguments.
+
+```shell
+mofmt PATH ...
+```
+
+## TODO
+
+[ ] include HTML pretty-printer
+
+## License
+
+MPL-2.0
+
+## Authors
+
+Eryk Mroczek: <mroczek.eryk@gmail.com>
diff --git a/code-style.md b/code-style.md
index 7271834..0cb1808 100644
--- a/code-style.md
+++ b/code-style.md
@@ -1,239 +1,239 @@
-# Code style
-
-This document describes a code style as applied by the *mofmt*.
-
-## Maximum line length
-
-There is no line length limit. There are plenty of limits already.
-
-## Horizontal spacing
-
-Single space is used as a horizontal separator.
-
-```modelica
-parameter Real Foo(start = 0); // Comment
-```
-
-### Binary operators
-
-All binary operators are surrounded with spaces.
-
-```modelica
-2 * (3 - 1.76)
-a <= b or x
-1 : 10
-```
-
-### Unary operators
-
-`not` operator is followed by a space. Unary addition or subtraction,
-including element-wise, are not.
-
-```modelica
--4
-.-(2 + 3)
-not isOff
-```
-
-### Assignments and equalities
-
-`:=` and `=` tokens are surrounded with spaces.
-Exception occurs when equality or assignment is
-followed by a multiline if-expression. In such case the symbol is only
-preceeded with a space.
-
-```modelica
-foo = bar * 2
-Real foo(min = 0, max = 2) = 1 / 2
-foo := bar(x)
-foo := if x < 2 then bar else baz
-```
-
-But:
-
-```modelica
-foo :=
-  if x < 2 then
-    bar
-  else
-    baz
-```
-
-### Arrays, modifications, function calls, expression lists
-
-If there is no line wrap every comma and semicolon is followed by a
-space. This includes discarded slots in *output-expression-list*.
-
-```modelica
-{1.0, 2.0, 3.0} // Array
-enthalpy_pT(p, T) // Function call
-(foo, ,bar, baz) // Output expression list with a discared second element
-[x, y; a, b] // Matrix
-```
-
-## Indentation
-
-Indentation is 2 spaces per level.
-
-Modelica doesn't use indentation to define the scope, but it is
-nonetheless useful to use it to enhance readability. Descriptions and
-annotations are good examples. Overall, indentation is increased at:
-
-* descriptions and annotations
-* constraining clauses
-* elements, equations, statements and external function calls
-* enumeration items
-* inside loops and if blocks
-
-### Descriptions, annotations and constraining clauses
-
-Indentation is increased only one time per element.
-
-```modelica
-replaceable package Medium = Modelica.Media.R134a.R134a_ph
-  constrainedby Modelica.Media.Interfaces.PartialMedium
-  "Fluid medium"
-  annotation(Dialog(tab = "General", group = "Medium"));
-```
-
-### Element, statement and equation lists
-
-Indentation is increased once per section.
-
-```modelica
-model Model
-  "Some model"
-
-  import FooPackage;
-  import Modelica.Constants.inf;
-
-  // SNAP
-
-protected
-
-  parameter FooPackage foo
-    "foo parameter";
-
-equation
-
-  // SNAP
-  foo = bar;
-
-end Model;
-```
-
-### Enumeration items
-
-Indentation is increased inside `enumeration()` and at every
-description.
-
-```modelica
-type BoundaryType = enumeration(
-  Pressure
-    "Pressure boundary",
-  Flow
-    "Flow boundary")
-  "Enumeration of possible boundary types";
-```
-
-### Loops and ifs
-
-Indentation is increased once per block.
-
-```modelica
-if foo == bar then
-  baz := bark;
-  bam := bem;
-else
-  baz := 0;
-  bam := 1;
-end if;
-```
-
-## Vertical spacing
-
-Beside indented elements described in the previous section, a newline or
-two are inserted after every element, statement or equation.
-
-Additionally, a single blank is inserted:
-
-* before and after section keywords like `equation` or `protected`
-* before and after the *composition* production
-* before class-wide annotation in the *long-class-specifier*
-
-## Line wrapping
-
-### Function calls, arrays, matrices, modifications, lists
-
-The main rule here is: be consistent. The following approach is applied:
-
-1. If line is wrapped at any argument, then wrap at every argument.
-2. If line is wrapped inside a nested construct, then wrap at every
-   argument in every outer construct.
-
-Indentation is increased accordingly to help visually identify the
-scope.
-
-```Modelica
-// No wrap is fine
-h = enthalpy_pT(p, T)
-
-// Outer array is wrapped, but inner ones are kept intact
-parameter Real A[2, 3] = {
-  {1.0, 2.0, 3.0},
-  {5.0, 6.0, 7.0}}
-
-// In nested function call, if inner call is wrapped, outer call is
-// wrapped as well
-cp_a = specificHeat_pT(
-  p = p_a,
-  T = temperature_ph(
-    p = p_a,
-    h = h_a))
-
-// But it is fine to wrap only outer call, keeping inner one intact
-cp_b = specificHeat_pT(
-  p = p_b,
-  T = temperature_ph(p = p_b, h = h_b));
-
-// Wrapped comprehensions are little bit special
-{
-  a[i] * b[i]
-  for i in 1 : n}
-
-// Output expression lists
-(
-  foo,
-  bar) := baz(x, y)
-
-// Import lists
-import Modelica.Constants.{
-  pi,
-  inf};
-```
-
-### Expressions
-
-Expressions (arithmetic, logical etc.) are handled in a very same way as
-function calls and arrays. The difference is that instead of commas line
-is wrapped before binary operators. Wrapping is applied to all operators
-of the same precedence (as defined by the grammar). If wrap occured
-inside higher precedence (inner) part, lower precedence (outer) parts
-are wrapped as well.
-
-```Modelica
-// No wrap
-foo = bar + baz
-
-// Outer rule wrapped
-foo := 2
-  * (bar - baz)
-  / 5
-
-// Some inner rule wrapped
-foo := -2
-  + bam
-    / baz ^ 2
-    * bark
-  - 33
-```
+# Code style
+
+This document describes a code style as applied by the *mofmt*.
+
+## Maximum line length
+
+There is no line length limit. There are plenty of limits already.
+
+## Horizontal spacing
+
+Single space is used as a horizontal separator.
+
+```modelica
+parameter Real Foo(start = 0); // Comment
+```
+
+### Binary operators
+
+All binary operators are surrounded with spaces.
+
+```modelica
+2 * (3 - 1.76)
+a <= b or x
+1 : 10
+```
+
+### Unary operators
+
+`not` operator is followed by a space. Unary addition or subtraction,
+including element-wise, are not.
+
+```modelica
+-4
+.-(2 + 3)
+not isOff
+```
+
+### Assignments and equalities
+
+`:=` and `=` tokens are surrounded with spaces.
+Exception occurs when equality or assignment is
+followed by a multiline if-expression. In such case the symbol is only
+preceeded with a space.
+
+```modelica
+foo = bar * 2
+Real foo(min = 0, max = 2) = 1 / 2
+foo := bar(x)
+foo := if x < 2 then bar else baz
+```
+
+But:
+
+```modelica
+foo :=
+  if x < 2 then
+    bar
+  else
+    baz
+```
+
+### Arrays, modifications, function calls, expression lists
+
+If there is no line wrap every comma and semicolon is followed by a
+space. This includes discarded slots in *output-expression-list*.
+
+```modelica
+{1.0, 2.0, 3.0} // Array
+enthalpy_pT(p, T) // Function call
+(foo, ,bar, baz) // Output expression list with a discared second element
+[x, y; a, b] // Matrix
+```
+
+## Indentation
+
+Indentation is 2 spaces per level.
+
+Modelica doesn't use indentation to define the scope, but it is
+nonetheless useful to use it to enhance readability. Descriptions and
+annotations are good examples. Overall, indentation is increased at:
+
+* descriptions and annotations
+* constraining clauses
+* elements, equations, statements and external function calls
+* enumeration items
+* inside loops and if blocks
+
+### Descriptions, annotations and constraining clauses
+
+Indentation is increased only one time per element.
+
+```modelica
+replaceable package Medium = Modelica.Media.R134a.R134a_ph
+  constrainedby Modelica.Media.Interfaces.PartialMedium
+  "Fluid medium"
+  annotation(Dialog(tab = "General", group = "Medium"));
+```
+
+### Element, statement and equation lists
+
+Indentation is increased once per section.
+
+```modelica
+model Model
+  "Some model"
+
+  import FooPackage;
+  import Modelica.Constants.inf;
+
+  // SNAP
+
+protected
+
+  parameter FooPackage foo
+    "foo parameter";
+
+equation
+
+  // SNAP
+  foo = bar;
+
+end Model;
+```
+
+### Enumeration items
+
+Indentation is increased inside `enumeration()` and at every
+description.
+
+```modelica
+type BoundaryType = enumeration(
+  Pressure
+    "Pressure boundary",
+  Flow
+    "Flow boundary")
+  "Enumeration of possible boundary types";
+```
+
+### Loops and ifs
+
+Indentation is increased once per block.
+
+```modelica
+if foo == bar then
+  baz := bark;
+  bam := bem;
+else
+  baz := 0;
+  bam := 1;
+end if;
+```
+
+## Vertical spacing
+
+Beside indented elements described in the previous section, a newline or
+two are inserted after every element, statement or equation.
+
+Additionally, a single blank is inserted:
+
+* before and after section keywords like `equation` or `protected`
+* before and after the *composition* production
+* before class-wide annotation in the *long-class-specifier*
+
+## Line wrapping
+
+### Function calls, arrays, matrices, modifications, lists
+
+The main rule here is: be consistent. The following approach is applied:
+
+1. If line is wrapped at any argument, then wrap at every argument.
+2. If line is wrapped inside a nested construct, then wrap at every
+   argument in every outer construct.
+
+Indentation is increased accordingly to help visually identify the
+scope.
+
+```Modelica
+// No wrap is fine
+h = enthalpy_pT(p, T)
+
+// Outer array is wrapped, but inner ones are kept intact
+parameter Real A[2, 3] = {
+  {1.0, 2.0, 3.0},
+  {5.0, 6.0, 7.0}}
+
+// In nested function call, if inner call is wrapped, outer call is
+// wrapped as well
+cp_a = specificHeat_pT(
+  p = p_a,
+  T = temperature_ph(
+    p = p_a,
+    h = h_a))
+
+// But it is fine to wrap only outer call, keeping inner one intact
+cp_b = specificHeat_pT(
+  p = p_b,
+  T = temperature_ph(p = p_b, h = h_b));
+
+// Wrapped comprehensions are little bit special
+{
+  a[i] * b[i]
+  for i in 1 : n}
+
+// Output expression lists
+(
+  foo,
+  bar) := baz(x, y)
+
+// Import lists
+import Modelica.Constants.{
+  pi,
+  inf};
+```
+
+### Expressions
+
+Expressions (arithmetic, logical etc.) are handled in a very same way as
+function calls and arrays. The difference is that instead of commas line
+is wrapped before binary operators. Wrapping is applied to all operators
+of the same precedence (as defined by the grammar). If wrap occured
+inside higher precedence (inner) part, lower precedence (outer) parts
+are wrapped as well.
+
+```Modelica
+// No wrap
+foo = bar + baz
+
+// Outer rule wrapped
+foo := 2
+  * (bar - baz)
+  / 5
+
+// Some inner rule wrapped
+foo := -2
+  + bam
+    / baz ^ 2
+    * bark
+  - 33
+```
diff --git a/src/markers.rs b/src/markers.rs
index 8122840..d6bc171 100644
--- a/src/markers.rs
+++ b/src/markers.rs
@@ -1,9 +1,9 @@
-#[derive(PartialEq)]
-pub enum Marker {
-    Token(String),
-    Indent,
-    Dedent,
-    Space,
-    Blank,
-    Break,
-}
+#[derive(PartialEq)]
+pub enum Marker {
+    Token(String),
+    Indent,
+    Dedent,
+    Space,
+    Blank,
+    Break,
+}
diff --git a/src/printing.rs b/src/printing.rs
index 0c170ca..5d54c14 100644
--- a/src/printing.rs
+++ b/src/printing.rs
@@ -1,44 +1,45 @@
-use crate::markers::Marker;
-
-pub fn print(markers: Vec<Marker>) -> String {
-    let mut printer = Printer::new();
-    let formatted: Vec<String> = markers
-        .into_iter()
-        .filter_map(|m| printer.print_marker(m))
-        .collect();
-    formatted.join("")
-}
-
-struct Printer {
-    indent: usize,
-}
-
-impl Printer {
-    fn new() -> Self {
-        Printer { indent: 0 }
-    }
-
-    fn print_marker(&mut self, m: Marker) -> Option<String> {
-        const INDENT: &str = "  ";
-        match m {
-            Marker::Space => Some(String::from(" ")),
-            Marker::Indent => {
-                self.indent += 1;
-                None
-            }
-            Marker::Dedent => {
-                self.indent -= 1;
-                None
-            }
-            Marker::Token(txt) => Some(txt),
-            _ => {
-                let mut out = String::from("\n");
-                if m == Marker::Blank {
-                    out += "\n";
-                }
-                (0..self.indent).for_each(|_| out += INDENT);
-                Some(out)
-            }
-        }
-    }
-}
+use crate::markers::Marker;
+
+pub fn print(markers: Vec<Marker>) -> String {
+    let mut printer = Printer::new();
+    let formatted: Vec<String> = markers
+        .into_iter()
+        .filter_map(|m| printer.print_marker(m))
+        .collect();
+    formatted.join("")
+}
+
+struct Printer {
+    indent: usize,
+}
+
+impl Printer {
+    fn new() -> Self {
+        Printer { indent: 0 }
+    }
+
+    fn print_marker(&mut self, m: Marker) -> Option<String> {
+        const INDENT: &str = "  ";
+        const EOL: &str = if cfg!(windows) { "\r\n" } else { "\n" };
+        match m {
+            Marker::Space => Some(String::from(" ")),
+            Marker::Indent => {
+                self.indent += 1;
+                None
+            }
+            Marker::Dedent => {
+                self.indent -= 1;
+                None
+            }
+            Marker::Token(txt) => Some(txt),
+            _ => {
+                let mut out = String::from(EOL);
+                if m == Marker::Blank {
+                    out += EOL;
+                }
+                (0..self.indent).for_each(|_| out += INDENT);
+                Some(out)
+            }
+        }
+    }
+}
diff --git a/src/tree.rs b/src/tree.rs
index d2d3025..00553d5 100644
--- a/src/tree.rs
+++ b/src/tree.rs
@@ -1,131 +1,131 @@
-use moparse::{SyntaxEvent, SyntaxKind, Token};
-
-pub fn build_tree(tokens: Vec<Token>, events: Vec<SyntaxEvent>) -> Tree {
-    let mut stack = Vec::new();
-    let mut tokens = tokens.into_iter();
-    let mut events = events;
-
-    assert!(matches!(events.pop(), Some(SyntaxEvent::Exit)));
-
-    for event in events {
-        match event {
-            SyntaxEvent::Enter(kind) => stack.push(Tree::new(kind)),
-            SyntaxEvent::Exit => {
-                let tree = stack.pop().unwrap();
-                if tree.len() > 0 {
-                    stack.last_mut().unwrap().push(Child::Tree(tree));
-                }
-            }
-            SyntaxEvent::Advance => {
-                let token = tokens.next().unwrap();
-                stack.last_mut().unwrap().push(Child::Token(token));
-            }
-        }
-    }
-
-    assert!(tokens.next().is_none());
-    assert!(stack.len() == 1);
-
-    stack.pop().unwrap()
-}
-
-#[derive(Debug)]
-pub struct Tree {
-    pub kind: SyntaxKind,
-    pub children: Vec<Child>,
-}
-
-#[derive(Debug)]
-pub enum Child {
-    Token(Token),
-    Tree(Tree),
-}
-
-impl Tree {
-    pub fn new(kind: SyntaxKind) -> Self {
-        Tree {
-            kind,
-            children: Vec::new(),
-        }
-    }
-
-    pub fn push(&mut self, child: Child) {
-        self.children.push(child);
-    }
-
-    pub fn len(&self) -> usize {
-        self.children.len()
-    }
-
-    pub fn start(&self) -> &Token {
-        match self.children.first().unwrap() {
-            Child::Token(token) => token,
-            Child::Tree(tree) => tree.start(),
-        }
-    }
-
-    pub fn end(&self) -> &Token {
-        match self.children.last().unwrap() {
-            Child::Token(token) => token,
-            Child::Tree(tree) => tree.end(),
-        }
-    }
-
-    pub fn is_multiline(&self) -> bool {
-        let first = self.start();
-        let last = self.end();
-        first.start.line < last.end.line
-    }
-
-    pub fn contains(&self, kind: SyntaxKind) -> bool {
-        let mut contains = false;
-        for child in self.children.iter() {
-            if let Child::Tree(tree) = child {
-                if tree.kind == kind {
-                    contains = true;
-                } else {
-                    contains = tree.contains(kind);
-                }
-            }
-            if contains {
-                return contains;
-            }
-        }
-        contains
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use moparse::{parse, ModelicaToken};
-
-    use super::*;
-
-    fn tree(source: &str, start: SyntaxKind) -> Tree {
-        let parsed = parse(source, start);
-        build_tree(parsed.tokens, parsed.events)
-    }
-
-    #[test]
-    fn test_empty_rules() {
-        const SOURCE: &str = r#"annotation (choices(choice = 0 "Foo", choice = 1 "Bar"))"#;
-        let tree = tree(SOURCE, SyntaxKind::Description);
-        assert_eq!(tree.len(), 1);
-    }
-
-    #[test]
-    fn test_start_and_end() {
-        const SOURCE: &str = r#""descr" annotation ()"#;
-        let tree = tree(SOURCE, SyntaxKind::Description);
-        assert_eq!(tree.len(), 2);
-        if let Child::Tree(annotation) = tree.children.get(1).unwrap() {
-            assert_eq!(annotation.len(), 2);
-        } else {
-            panic!("annotation not found");
-        }
-        assert_eq!(tree.start().kind, ModelicaToken::String);
-        assert_eq!(tree.start().text, r#""descr""#);
-        assert_eq!(tree.end().kind, ModelicaToken::RParen);
-        assert_eq!(tree.end().end.pos, 21);
-    }
-}
+use moparse::{SyntaxEvent, SyntaxKind, Token};
+
+pub fn build_tree(tokens: Vec<Token>, events: Vec<SyntaxEvent>) -> Tree {
+    let mut stack = Vec::new();
+    let mut tokens = tokens.into_iter();
+    let mut events = events;
+
+    assert!(matches!(events.pop(), Some(SyntaxEvent::Exit)));
+
+    for event in events {
+        match event {
+            SyntaxEvent::Enter(kind) => stack.push(Tree::new(kind)),
+            SyntaxEvent::Exit => {
+                let tree = stack.pop().unwrap();
+                if tree.len() > 0 {
+                    stack.last_mut().unwrap().push(Child::Tree(tree));
+                }
+            }
+            SyntaxEvent::Advance => {
+                let token = tokens.next().unwrap();
+                stack.last_mut().unwrap().push(Child::Token(token));
+            }
+        }
+    }
+
+    assert!(tokens.next().is_none());
+    assert!(stack.len() == 1);
+
+    stack.pop().unwrap()
+}
+
+#[derive(Debug)]
+pub struct Tree {
+    pub kind: SyntaxKind,
+    pub children: Vec<Child>,
+}
+
+#[derive(Debug)]
+pub enum Child {
+    Token(Token),
+    Tree(Tree),
+}
+
+impl Tree {
+    pub fn new(kind: SyntaxKind) -> Self {
+        Tree {
+            kind,
+            children: Vec::new(),
+        }
+    }
+
+    pub fn push(&mut self, child: Child) {
+        self.children.push(child);
+    }
+
+    pub fn len(&self) -> usize {
+        self.children.len()
+    }
+
+    pub fn start(&self) -> &Token {
+        match self.children.first().unwrap() {
+            Child::Token(token) => token,
+            Child::Tree(tree) => tree.start(),
+        }
+    }
+
+    pub fn end(&self) -> &Token {
+        match self.children.last().unwrap() {
+            Child::Token(token) => token,
+            Child::Tree(tree) => tree.end(),
+        }
+    }
+
+    pub fn is_multiline(&self) -> bool {
+        let first = self.start();
+        let last = self.end();
+        first.start.line < last.end.line
+    }
+
+    pub fn contains(&self, kind: SyntaxKind) -> bool {
+        let mut contains = false;
+        for child in self.children.iter() {
+            if let Child::Tree(tree) = child {
+                if tree.kind == kind {
+                    contains = true;
+                } else {
+                    contains = tree.contains(kind);
+                }
+            }
+            if contains {
+                return contains;
+            }
+        }
+        contains
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use moparse::{parse, ModelicaToken};
+
+    use super::*;
+
+    fn tree(source: &str, start: SyntaxKind) -> Tree {
+        let parsed = parse(source, start);
+        build_tree(parsed.tokens, parsed.events)
+    }
+
+    #[test]
+    fn test_empty_rules() {
+        const SOURCE: &str = r#"annotation (choices(choice = 0 "Foo", choice = 1 "Bar"))"#;
+        let tree = tree(SOURCE, SyntaxKind::Description);
+        assert_eq!(tree.len(), 1);
+    }
+
+    #[test]
+    fn test_start_and_end() {
+        const SOURCE: &str = r#""descr" annotation ()"#;
+        let tree = tree(SOURCE, SyntaxKind::Description);
+        assert_eq!(tree.len(), 2);
+        if let Child::Tree(annotation) = tree.children.get(1).unwrap() {
+            assert_eq!(annotation.len(), 2);
+        } else {
+            panic!("annotation not found");
+        }
+        assert_eq!(tree.start().kind, ModelicaToken::String);
+        assert_eq!(tree.start().text, r#""descr""#);
+        assert_eq!(tree.end().kind, ModelicaToken::RParen);
+        assert_eq!(tree.end().end.pos, 21);
+    }
+}


Test patch:
diff --git a/tests/samples/code-input.mo b/tests/samples/code-input.mo
index f84fa13..72fcc37 100644
--- a/tests/samples/code-input.mo
+++ b/tests/samples/code-input.mo
@@ -51,7 +51,7 @@ Integer[1, 3, 4] bar =if true then
 Real smallest=(Modelica.Math.Vectors.sort({4,2,5,1}))[1];
 
 protected
-  // Here we have to comments
+  // Here we have two comments
 
   /*
   And they are separated
diff --git a/tests/samples/code-output.mo b/tests/samples/code-output.mo
index 2691176..6ea8551 100644
--- a/tests/samples/code-output.mo
+++ b/tests/samples/code-output.mo
@@ -63,7 +63,7 @@ final partial model FooModel
 
 protected
 
-  // Here we have to comments
+  // Here we have two comments
 
   /*
   And they are separated
diff --git a/tests/style_tests.rs b/tests/style_tests.rs
index e5657b4..73c4dc2 100644
--- a/tests/style_tests.rs
+++ b/tests/style_tests.rs
@@ -1,17 +1,17 @@
-use mofmt;
-use moparse;
-use std::fs;
-
-// Helper functions
-fn format_file(path: &str) -> String {
-    let input = fs::read_to_string(path).expect("error");
-    let parsed = moparse::parse(&input, moparse::SyntaxKind::StoredDefinition);
-    mofmt::pretty_print(parsed.tokens, parsed.comments, parsed.events)
-}
-
-#[test]
-fn test_formatting() {
-    let formatted = format_file("tests/samples/code-input.mo");
-    let expected = fs::read_to_string("tests/samples/code-output.mo").expect("error");
-    assert_eq!(expected, formatted);
-}
+use mofmt;
+use moparse;
+use std::fs;
+
+// Helper functions
+fn format_file(path: &str) -> String {
+    let input = fs::read_to_string(path).expect("error");
+    let parsed = moparse::parse(&input, moparse::SyntaxKind::StoredDefinition);
+    mofmt::pretty_print(parsed.tokens, parsed.comments, parsed.events)
+}
+
+#[test]
+fn test_formatting() {
+    let formatted = format_file("tests/samples/code-input.mo");
+    let expected = fs::read_to_string("tests/samples/code-output.mo").expect("error");
+    assert_eq!(expected, formatted);
+}