Repository: fortran-lang/fortls (language: python)

Issue:
Add Python 3.11 in CI

Mixing command line and json interface arguments
When a mix of the command line interface and the json interface are used the following options are not being properly set and are instead set to empty sets. We should be able to mix the two interfaces, although I would not recommend it.

 - `source_dirs`
 - `incl_suffixes`
 - `excl_suffixes`
 - `include_dirs`
Feature request:  documentHighlightProvider support
Currently `fortls` does not implement the documentHighlightProvider language server feature, which makes emacs `lsp-mode` do not have symbol highlighting feature. Can you implement this feature ?
Keyword modifiers in module procedures/functions cause incorrect parsing
**Describe the bug**
Module Procedures/Functions + keyword modifiers like pure cause incorrect parsing of AST scopes in submodules

**To Reproduce**

```f08
module m
end module m

submodule (m) n

    use, intrinsic :: iso_fortran_env, only: int8, int16, int32, int64
    implicit none

    integer, parameter :: sp = selected_real_kind(6)
    integer, parameter :: dp = selected_real_kind(15)

contains

    pure module function foo_sp(x) result(fi)
        real(sp), intent(in) :: x
        real(sp) :: fi
    end function foo_sp

    pure module function foo_dp(x) result(fi)
        real(dp), intent(in) :: x
        real(dp) :: fi
    end function foo_dp
end submodule n
```


**Setup information (please complete the following information):**
 - fortls Version 2.5.0

LSP error messages for config files are not issues correctly
The way error messages post in the langserver.py module is wrong in some places. The post_messages list should either be appended or the function post_message() should be called directly.
Implement workspace/configuration
Add https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_didChangeConfiguration
I think I should be able to pass the settings dictionary that gets converted into attributes. I might have to do an update after reading the in the configuration file to make sure everything is synchronized.


Discussion / hints:


Could you please elaborate a bit about what you mean by "symbol highlighting feature"? From what I understand `documentHighlightProvider` should be nearly identical to `textDocument/references` which is already implemented.
If by any chance you mean syntax highlighting, that is usually provided by the extension and not the language server since it is a big task of its own.

>Document Highlights Request (:leftwards_arrow_with_hook:)
The document highlight request is sent from the client to the server to resolve a document highlights for a given text document position. For programming languages this usually highlights all references to the symbol scoped to this file. However we kept ‘textDocument/documentHighlight’ and ‘textDocument/references’ separate requests since the first one is allowed to be more fuzzy. Symbol matches usually have a [DocumentHighlightKind](https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#documentHighlightKind) of Read or Write whereas fuzzy or textual matches use Textas the kind.

see: https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#textDocument_documentHighlight


Further elaborating on my previous comments, this is the current status of the reference highlighting, which I suspect is what you are asking

![image](https://user-images.githubusercontent.com/16143716/152478855-3c561175-f6c7-471a-a7cb-e0016552f1b8.png)

Could you please post the settings you use for `fortls`?

Yes, what I mean by "symbol highlighting" is *reference highlighting*, see the example code in Emacs using `fortls` with `lsp-mode`:
![image](https://user-images.githubusercontent.com/9455367/152490011-9cd12482-4a51-483d-8041-7eaf537c8952.png)
The `dist` variables under the curser should have been highlighted, but I can not get it working in Emacs with `lsp-mode`. However, reference highlighting using`fortls` works just fine in Vscode. 
The settings for `fortls` I'm using are `"--variable_hover" "--hover_signature" "--lowercase_intrinsics" `
So I would guess then that `lsp-mode` does not support `textDocument/references`, which is weird. Has that always been the case? Your client should be able to handle both `textDocument/references` and `documentHighlightProvider`.

Well, it seems that `lsp-mode` needs `documentHighlightProvider` to do the reference highlighting, as discussed in the [issue](https://github.com/emacs-lsp/lsp-mode/issues/3338#issue-1122602616). Maybe I just need to wait for `lsp-mode` to support it. Thanks anyway for your help! `fortls` is very useful.




Fix patch:
diff --git a/.coveragerc b/.coveragerc
index b3c2823f..26deb54b 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -1,6 +1,5 @@
 [run]
-dynamic_context = test_function
-omit = 
+omit =
     fortls/__init__.py
     fortls/version.py
 
@@ -9,4 +8,7 @@ exclude_lines =
     if debug:
     log.debug
     except:
-    if not PY3K:
\ No newline at end of file
+    if not PY3K:
+
+[html]
+show_contexts = True
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 701e1a77..57523ab0 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -10,13 +10,13 @@ jobs:
     strategy:
       matrix:
         os: [ubuntu-latest, windows-latest]
-        python-version: ["3.7", "3.8", "3.9", "3.10"]
+        python-version: ["3.7", "3.8", "3.9", "3.10", "3.11-dev"]
       fail-fast: false
     runs-on: ${{ matrix.os }}
 
     steps:
       - uses: actions/checkout@v3
-      - uses: actions/setup-python@v3
+      - uses: actions/setup-python@v4
         with:
           python-version: ${{ matrix.python-version }}
           architecture: x64
@@ -25,7 +25,7 @@ jobs:
         run: pip install .[dev]
 
       - name: Unittests
-        run: pytest -v
+        run: pytest --doctest-modules
 
       - name: Lint
         run: black --diff --check --verbose .
@@ -38,14 +38,15 @@ jobs:
 
     steps:
       - uses: actions/checkout@v3
-      - uses: actions/setup-python@v3
+      - uses: actions/setup-python@v4
         with:
           python-version: "3.10"
 
       - name: Coverage report
         run: |
           pip install .[dev]
-          pytest --cov=fortls --cov-report=xml
+          pytest --doctest-modules
+        shell: bash
 
       - name: Upload coverage to Codecov
         uses: codecov/codecov-action@v3
diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml
index 6465872f..aaea18aa 100644
--- a/.github/workflows/python-publish.yml
+++ b/.github/workflows/python-publish.yml
@@ -19,7 +19,7 @@ jobs:
     steps:
       - uses: actions/checkout@v3
       - name: Set up Python
-        uses: actions/setup-python@v3
+        uses: actions/setup-python@v4
         with:
           python-version: "3.x"
 
diff --git a/.gitignore b/.gitignore
index 241dc003..eab690a8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,6 @@
 *.pyc
 .vscode
-*.egg-info 
+*.egg-info
 dist/
 build/
 docs/_build/
@@ -14,4 +14,5 @@ fortls/_version.py
 *.smod
 *.log
 
-.coverage
\ No newline at end of file
+.coverage
+coverage.xml
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 00000000..1d881010
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,28 @@
+# See https://pre-commit.com for more information
+# See https://pre-commit.com/hooks.html for more hooks
+repos:
+-   repo: https://github.com/pre-commit/pre-commit-hooks
+    rev: v4.2.0
+    hooks:
+    -   id: trailing-whitespace
+    -   id: end-of-file-fixer
+    -   id: check-yaml
+    -   id: check-added-large-files
+        args: ['--maxkb=2000']
+-   repo: https://gitlab.com/pycqa/flake8
+    rev: 3.9.2
+    hooks:
+    - id: flake8
+-   repo: https://github.com/asottile/pyupgrade
+    rev: v2.32.1
+    hooks:
+    -   id: pyupgrade
+-   repo: https://github.com/pycqa/isort
+    rev: 5.10.1
+    hooks:
+    -   id: isort
+        name: isort (python)
+-   repo: https://github.com/psf/black
+    rev: 22.3.0
+    hooks:
+    -   id: black
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c11edcb1..6811cf24 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,11 +2,101 @@
 
 ## Unreleased
 
+## 2.10.0
+
+### Fixed
+
+- Fixed `workspace/didChangeConfiguration` requests that caused errors in VS Code logs
+  ([#114](https://github.com/gnikit/fortls/issues/114))
+
+## 2.9.0
+
+### Fixed
+
+- Fixed glob pattern resolution for command line arguments
+  ([#142](https://github.com/gnikit/fortls/issues/142))
+
+### Changed
+
+- Changed the default value of the following options when a mix the command line
+  interface and the `json` interface are used. Instead of having the `json` interface
+  default the values to an empty set it now defaults to the values of the
+  command line: `excl_paths`, `source_dirs`, `incl_suffixes`, `excl_suffixes`, `include_dirs`
+  ([#143](https://github.com/gnikit/fortls/issues/143))
+
+## 2.8.0
+
+### Added
+
+- Added support for comments in the `json` configuration files
+  ([#137](https://github.com/gnikit/fortls/issues/137))
+- Added `sitemap.xml` to documentation webpage
+  ([#134](https://github.com/gnikit/fortls/pull/134))
+
+### Fixed
+
+- Fixed bug where error messages did not post correctly
+  ([#135](https://github.com/gnikit/fortls/issues/135))
+
+## 2.7.0
+
+### Added
+
+- Added doctests in the pytest test suite
+  ([#131](https://github.com/gnikit/fortls/issues/131))
+
+### Changed
+
+- Renamed variables to simplify parser
+  ([#133](https://github.com/gnikit/fortls/pull/133))
+- Redesigned parsing functions for short-hand declarations of array dimensions,
+  character length and parsing of kind
+  ([#130](https://github.com/gnikit/fortls/pull/130))
+
+## 2.6.0
+
+### Changed
+
+- Redesigned the `fortls` website to be more aesthetically pleasing and user-friendly
+  ([#112](https://github.com/gnikit/fortls/issues/112))
+
+### Fixed
+
+- Fixed bug where submodule procedure scopes would terminate early if keyword modifiers were used
+  ([#119](https://github.com/gnikit/fortls/issues/119))
+
+## 2.5.0
+
+### Added
+
+- Added `textDocument/documentHighlight` request for Vim and EMACS
+  ([#43](https://github.com/gnikit/fortls/issues/43))
+- Added `pre-commit` hook with `flake`, `black`, `isort` and `pyupgrade`
+  ([#106](https://github.com/gnikit/fortls/issues/106))
+- Added test support for diagnostic messages
+
+### Changed
+
+- Changed code structure, reformatted files and changed name conventions
+  ([#109](https://github.com/gnikit/fortls/issues/109))
+- Updated CONTRIBUTING instructions
+
 ## 2.4.0
 
 ### Added
 
+- Added multiple unittests for diagnostic messages
+- Added `pre-commit` hook to the project
+  ([#106](https://github.com/gnikit/fortls/issues/106))
 - Added Code of Conduct
+- Added basic support for hovering over `ASSOCIATE` blocks
+  ([#62](https://github.com/gnikit/fortls/issues/62))
+
+### Changed
+
+- Changed the naming convention for Fortran Objects
+  ([#109](https://github.com/gnikit/fortls/issues/109))
+- Formatted all files with `pre-commit`
 
 ## 2.3.1
 
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 97945037..8e2bde23 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -33,20 +33,32 @@ pip install -e .[dev,docs]
 
 ### Testing 🧪
 
-To verify that your cloning of the GitHub repository work as expected open a terminal and run:
+To verify that your cloning of the GitHub repository worked as expected open a terminal and run:
 
 ```sh
 pytest -v
 ```
+
 This will run the entire unit test suite. You can also run this to verify that you haven't broken anything in the code.
 
-👉 **Tip!** You can run individual tests by selecting the path to the Python file and the method 
+👉 **Tip!** You can run individual tests by selecting the path to the Python file and the method
+
 ```sh
 pytest test/test_interface.py::test_version_update_pypi
 ```
 
 ### Developing & Debugging 🐞️
 
+❗️ Before you start developing, open a terminal inside `fortls` and run:
+
+```sh
+pre-commit install
+```
+
+This will ensure that all you commits meet the formatting standards of the project.
+
+---
+
 You can now start writing code! Your local `fortls` version will be updated with every code change you make, so you can use your normal code editor to checkout the `fortls` features that you have implemented.
 It is however considerably easier to create compact unittests to check if your changes have worked.
 
@@ -54,10 +66,8 @@ A `fortls` test normally involves writing a Python function which sends a JSONRP
 Often times small bits of Fortran source code also have to be submited to be used by the test.
 You can find varisous test examples in the `tests` directory.
 
-
 👉 **Tip!** You can attach a debugger to the main `fortls` source code during unittesting which should allow you to pause, break, step into, etc. while testing, thus making it easier to find mistakes.
 
-
 ### Merging
 
 To merge your changes to the main `fortls` repository push your branch on GitHub and open a [Pull Request](https://github.com/gnikit/fortls/pulls). Ping `@gnikit` to review your PR.
diff --git a/README.md b/README.md
index 4764b10e..ebf1d301 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@
 [![Tests](https://github.com/gnikit/fortls/actions/workflows/main.yml/badge.svg)](https://github.com/gnikit/fortls/actions/workflows/main.yml)
 [![Documentation](https://github.com/gnikit/fortls/actions/workflows/docs.yml/badge.svg)](https://github.com/gnikit/fortls/actions/workflows/docs.yml)
 [![codecov](https://codecov.io/gh/gnikit/fortls/branch/master/graph/badge.svg?token=ZEXUX0R65M)](https://codecov.io/gh/gnikit/fortls)
-[![GitHub license](https://img.shields.io/github/license/gnikit/fortls)](https://github.com/gnikit/fortls/blob/dev/LICENSE)
+[![GitHub license](https://img.shields.io/github/license/gnikit/fortls)](https://github.com/gnikit/fortls/blob/master/LICENSE)
 [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
 
 [![GitHub Sponsor](https://img.shields.io/static/v1?style=social&label=Sponsor&message=%E2%9D%A4&logo=GitHub&color&link=%3Curl%3E)](https://github.com/sponsors/gnikit)
@@ -137,22 +137,23 @@ An example for a Configuration file is given below
 
 ## Implemented server requests
 
-| Request                       | Description                                            |
-| ----------------------------- | ------------------------------------------------------ |
-| `workspace/symbol`            | Get workspace-wide symbols                             |
-| `textDocument/documentSymbol` | Get document symbols e.g. functions, subroutines, etc. |
-| `textDocument/completion`     | Suggested tab-completion when typing                   |
-| `textDocument/signatureHelp`  | Get signature information at a given cursor position   |
-| `textDocument/definition`     | GoTo definition/Peek definition                        |
-| `textDocument/references`     | Find all/Peek references                               |
-| `textDocument/hover`          | Show messages and signatures upon hover                |
-| `textDocument/implementation` | GoTo implementation/Peek implementation                |
-| `textDocument/rename`         | Rename a symbol across the workspace                   |
-| `textDocument/didOpen`        | Document synchronisation upon opening                  |
-| `textDocument/didSave`        | Document synchronisation upon saving                   |
-| `textDocument/didClose`       | Document synchronisation upon closing                  |
-| `textDocument/didChange`      | Document synchronisation upon changes to the document  |
-| `textDocument/codeAction`     | **Experimental** Generate code                         |
+| Request                          | Description                                            |
+| -------------------------------- | ------------------------------------------------------ |
+| `workspace/symbol`               | Get workspace-wide symbols                             |
+| `textDocument/documentSymbol`    | Get document symbols e.g. functions, subroutines, etc. |
+| `textDocument/completion`        | Suggested tab-completion when typing                   |
+| `textDocument/signatureHelp`     | Get signature information at a given cursor position   |
+| `textDocument/definition`        | GoTo definition/Peek definition                        |
+| `textDocument/references`        | Find all/Peek references                               |
+| `textDocument/documentHighlight` | Same as `textDocument/references`                      |
+| `textDocument/hover`             | Show messages and signatures upon hover                |
+| `textDocument/implementation`    | GoTo implementation/Peek implementation                |
+| `textDocument/rename`            | Rename a symbol across the workspace                   |
+| `textDocument/didOpen`           | Document synchronisation upon opening                  |
+| `textDocument/didSave`           | Document synchronisation upon saving                   |
+| `textDocument/didClose`          | Document synchronisation upon closing                  |
+| `textDocument/didChange`         | Document synchronisation upon changes to the document  |
+| `textDocument/codeAction`        | **Experimental** Generate code                         |
 
 ## Acknowledgements
 
@@ -173,4 +174,4 @@ please provide example code to reproduce the observed issue.
 
 ## License
 
-This project is made available under the [MIT License](https://github.com/gnikit/fortls/blob/dev/LICENSE).
+This project is made available under the [MIT License](https://github.com/gnikit/fortls/blob/master/LICENSE).
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 00000000..8a44efdc
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,12 @@
+# Security Policy
+
+## Supported Versions
+
+`fortls` supports **ONLY** the latest Release. An autoupdate function is enabled by default to fetch the newest updates from `PyPi`.
+For Anaconda environments the autoupdate functionality is disabled and it is up to the user to update to the latest version.
+
+
+## Reporting a Vulnerability
+
+The codebase is regularly scanned and patched for any potential security vulnerabilities.
+If you manage to find a vulnerability in the Language Server please open an [Bug Report](https://github.com/gnikit/fortls/issues) with prefix: **SECURITY:**.
diff --git a/assets/LICENSE b/assets/LICENSE
new file mode 100644
index 00000000..9badf7ab
--- /dev/null
+++ b/assets/LICENSE
@@ -0,0 +1,402 @@
+Attribution-NonCommercial-NoDerivatives 4.0 International
+
+=======================================================================
+
+Creative Commons Corporation ("Creative Commons") is not a law firm and
+does not provide legal services or legal advice. Distribution of
+Creative Commons public licenses does not create a lawyer-client or
+other relationship. Creative Commons makes its licenses and related
+information available on an "as-is" basis. Creative Commons gives no
+warranties regarding its licenses, any material licensed under their
+terms and conditions, or any related information. Creative Commons
+disclaims all liability for damages resulting from their use to the
+fullest extent possible.
+
+Using Creative Commons Public Licenses
+
+Creative Commons public licenses provide a standard set of terms and
+conditions that creators and other rights holders may use to share
+original works of authorship and other material subject to copyright
+and certain other rights specified in the public license below. The
+following considerations are for informational purposes only, are not
+exhaustive, and do not form part of our licenses.
+
+     Considerations for licensors: Our public licenses are
+     intended for use by those authorized to give the public
+     permission to use material in ways otherwise restricted by
+     copyright and certain other rights. Our licenses are
+     irrevocable. Licensors should read and understand the terms
+     and conditions of the license they choose before applying it.
+     Licensors should also secure all rights necessary before
+     applying our licenses so that the public can reuse the
+     material as expected. Licensors should clearly mark any
+     material not subject to the license. This includes other CC-
+     licensed material, or material used under an exception or
+     limitation to copyright. More considerations for licensors:
+    wiki.creativecommons.org/Considerations_for_licensors
+
+     Considerations for the public: By using one of our public
+     licenses, a licensor grants the public permission to use the
+     licensed material under specified terms and conditions. If
+     the licensor's permission is not necessary for any reason--for
+     example, because of any applicable exception or limitation to
+     copyright--then that use is not regulated by the license. Our
+     licenses grant only permissions under copyright and certain
+     other rights that a licensor has authority to grant. Use of
+     the licensed material may still be restricted for other
+     reasons, including because others have copyright or other
+     rights in the material. A licensor may make special requests,
+     such as asking that all changes be marked or described.
+     Although not required by our licenses, you are encouraged to
+     respect those requests where reasonable. More considerations
+     for the public:
+    wiki.creativecommons.org/Considerations_for_licensees
+
+=======================================================================
+
+Creative Commons Attribution-NonCommercial-NoDerivatives 4.0
+International Public License
+
+By exercising the Licensed Rights (defined below), You accept and agree
+to be bound by the terms and conditions of this Creative Commons
+Attribution-NonCommercial-NoDerivatives 4.0 International Public
+License ("Public License"). To the extent this Public License may be
+interpreted as a contract, You are granted the Licensed Rights in
+consideration of Your acceptance of these terms and conditions, and the
+Licensor grants You such rights in consideration of benefits the
+Licensor receives from making the Licensed Material available under
+these terms and conditions.
+
+
+Section 1 -- Definitions.
+
+  a. Adapted Material means material subject to Copyright and Similar
+     Rights that is derived from or based upon the Licensed Material
+     and in which the Licensed Material is translated, altered,
+     arranged, transformed, or otherwise modified in a manner requiring
+     permission under the Copyright and Similar Rights held by the
+     Licensor. For purposes of this Public License, where the Licensed
+     Material is a musical work, performance, or sound recording,
+     Adapted Material is always produced where the Licensed Material is
+     synched in timed relation with a moving image.
+
+  b. Copyright and Similar Rights means copyright and/or similar rights
+     closely related to copyright including, without limitation,
+     performance, broadcast, sound recording, and Sui Generis Database
+     Rights, without regard to how the rights are labeled or
+     categorized. For purposes of this Public License, the rights
+     specified in Section 2(b)(1)-(2) are not Copyright and Similar
+     Rights.
+
+  c. Effective Technological Measures means those measures that, in the
+     absence of proper authority, may not be circumvented under laws
+     fulfilling obligations under Article 11 of the WIPO Copyright
+     Treaty adopted on December 20, 1996, and/or similar international
+     agreements.
+
+  d. Exceptions and Limitations means fair use, fair dealing, and/or
+     any other exception or limitation to Copyright and Similar Rights
+     that applies to Your use of the Licensed Material.
+
+  e. Licensed Material means the artistic or literary work, database,
+     or other material to which the Licensor applied this Public
+     License.
+
+  f. Licensed Rights means the rights granted to You subject to the
+     terms and conditions of this Public License, which are limited to
+     all Copyright and Similar Rights that apply to Your use of the
+     Licensed Material and that the Licensor has authority to license.
+
+  g. Licensor means the individual(s) or entity(ies) granting rights
+     under this Public License.
+
+  h. NonCommercial means not primarily intended for or directed towards
+     commercial advantage or monetary compensation. For purposes of
+     this Public License, the exchange of the Licensed Material for
+     other material subject to Copyright and Similar Rights by digital
+     file-sharing or similar means is NonCommercial provided there is
+     no payment of monetary compensation in connection with the
+     exchange.
+
+  i. Share means to provide material to the public by any means or
+     process that requires permission under the Licensed Rights, such
+     as reproduction, public display, public performance, distribution,
+     dissemination, communication, or importation, and to make material
+     available to the public including in ways that members of the
+     public may access the material from a place and at a time
+     individually chosen by them.
+
+  j. Sui Generis Database Rights means rights other than copyright
+     resulting from Directive 96/9/EC of the European Parliament and of
+     the Council of 11 March 1996 on the legal protection of databases,
+     as amended and/or succeeded, as well as other essentially
+     equivalent rights anywhere in the world.
+
+  k. You means the individual or entity exercising the Licensed Rights
+     under this Public License. Your has a corresponding meaning.
+
+
+Section 2 -- Scope.
+
+  a. License grant.
+
+       1. Subject to the terms and conditions of this Public License,
+          the Licensor hereby grants You a worldwide, royalty-free,
+          non-sublicensable, non-exclusive, irrevocable license to
+          exercise the Licensed Rights in the Licensed Material to:
+
+            a. reproduce and Share the Licensed Material, in whole or
+               in part, for NonCommercial purposes only; and
+
+            b. produce and reproduce, but not Share, Adapted Material
+               for NonCommercial purposes only.
+
+       2. Exceptions and Limitations. For the avoidance of doubt, where
+          Exceptions and Limitations apply to Your use, this Public
+          License does not apply, and You do not need to comply with
+          its terms and conditions.
+
+       3. Term. The term of this Public License is specified in Section
+          6(a).
+
+       4. Media and formats; technical modifications allowed. The
+          Licensor authorizes You to exercise the Licensed Rights in
+          all media and formats whether now known or hereafter created,
+          and to make technical modifications necessary to do so. The
+          Licensor waives and/or agrees not to assert any right or
+          authority to forbid You from making technical modifications
+          necessary to exercise the Licensed Rights, including
+          technical modifications necessary to circumvent Effective
+          Technological Measures. For purposes of this Public License,
+          simply making modifications authorized by this Section 2(a)
+          (4) never produces Adapted Material.
+
+       5. Downstream recipients.
+
+            a. Offer from the Licensor -- Licensed Material. Every
+               recipient of the Licensed Material automatically
+               receives an offer from the Licensor to exercise the
+               Licensed Rights under the terms and conditions of this
+               Public License.
+
+            b. No downstream restrictions. You may not offer or impose
+               any additional or different terms or conditions on, or
+               apply any Effective Technological Measures to, the
+               Licensed Material if doing so restricts exercise of the
+               Licensed Rights by any recipient of the Licensed
+               Material.
+
+       6. No endorsement. Nothing in this Public License constitutes or
+          may be construed as permission to assert or imply that You
+          are, or that Your use of the Licensed Material is, connected
+          with, or sponsored, endorsed, or granted official status by,
+          the Licensor or others designated to receive attribution as
+          provided in Section 3(a)(1)(A)(i).
+
+  b. Other rights.
+
+       1. Moral rights, such as the right of integrity, are not
+          licensed under this Public License, nor are publicity,
+          privacy, and/or other similar personality rights; however, to
+          the extent possible, the Licensor waives and/or agrees not to
+          assert any such rights held by the Licensor to the limited
+          extent necessary to allow You to exercise the Licensed
+          Rights, but not otherwise.
+
+       2. Patent and trademark rights are not licensed under this
+          Public License.
+
+       3. To the extent possible, the Licensor waives any right to
+          collect royalties from You for the exercise of the Licensed
+          Rights, whether directly or through a collecting society
+          under any voluntary or waivable statutory or compulsory
+          licensing scheme. In all other cases the Licensor expressly
+          reserves any right to collect such royalties, including when
+          the Licensed Material is used other than for NonCommercial
+          purposes.
+
+
+Section 3 -- License Conditions.
+
+Your exercise of the Licensed Rights is expressly made subject to the
+following conditions.
+
+  a. Attribution.
+
+       1. If You Share the Licensed Material, You must:
+
+            a. retain the following if it is supplied by the Licensor
+               with the Licensed Material:
+
+                 i. identification of the creator(s) of the Licensed
+                    Material and any others designated to receive
+                    attribution, in any reasonable manner requested by
+                    the Licensor (including by pseudonym if
+                    designated);
+
+                ii. a copyright notice;
+
+               iii. a notice that refers to this Public License;
+
+                iv. a notice that refers to the disclaimer of
+                    warranties;
+
+                 v. a URI or hyperlink to the Licensed Material to the
+                    extent reasonably practicable;
+
+            b. indicate if You modified the Licensed Material and
+               retain an indication of any previous modifications; and
+
+            c. indicate the Licensed Material is licensed under this
+               Public License, and include the text of, or the URI or
+               hyperlink to, this Public License.
+
+          For the avoidance of doubt, You do not have permission under
+          this Public License to Share Adapted Material.
+
+       2. You may satisfy the conditions in Section 3(a)(1) in any
+          reasonable manner based on the medium, means, and context in
+          which You Share the Licensed Material. For example, it may be
+          reasonable to satisfy the conditions by providing a URI or
+          hyperlink to a resource that includes the required
+          information.
+
+       3. If requested by the Licensor, You must remove any of the
+          information required by Section 3(a)(1)(A) to the extent
+          reasonably practicable.
+
+
+Section 4 -- Sui Generis Database Rights.
+
+Where the Licensed Rights include Sui Generis Database Rights that
+apply to Your use of the Licensed Material:
+
+  a. for the avoidance of doubt, Section 2(a)(1) grants You the right
+     to extract, reuse, reproduce, and Share all or a substantial
+     portion of the contents of the database for NonCommercial purposes
+     only and provided You do not Share Adapted Material;
+
+  b. if You include all or a substantial portion of the database
+     contents in a database in which You have Sui Generis Database
+     Rights, then the database in which You have Sui Generis Database
+     Rights (but not its individual contents) is Adapted Material; and
+
+  c. You must comply with the conditions in Section 3(a) if You Share
+     all or a substantial portion of the contents of the database.
+
+For the avoidance of doubt, this Section 4 supplements and does not
+replace Your obligations under this Public License where the Licensed
+Rights include other Copyright and Similar Rights.
+
+
+Section 5 -- Disclaimer of Warranties and Limitation of Liability.
+
+  a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
+     EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
+     AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
+     ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
+     IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
+     WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
+     PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
+     ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
+     KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
+     ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
+
+  b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
+     TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
+     NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
+     INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
+     COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
+     USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
+     ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
+     DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
+     IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
+
+  c. The disclaimer of warranties and limitation of liability provided
+     above shall be interpreted in a manner that, to the extent
+     possible, most closely approximates an absolute disclaimer and
+     waiver of all liability.
+
+
+Section 6 -- Term and Termination.
+
+  a. This Public License applies for the term of the Copyright and
+     Similar Rights licensed here. However, if You fail to comply with
+     this Public License, then Your rights under this Public License
+     terminate automatically.
+
+  b. Where Your right to use the Licensed Material has terminated under
+     Section 6(a), it reinstates:
+
+       1. automatically as of the date the violation is cured, provided
+          it is cured within 30 days of Your discovery of the
+          violation; or
+
+       2. upon express reinstatement by the Licensor.
+
+     For the avoidance of doubt, this Section 6(b) does not affect any
+     right the Licensor may have to seek remedies for Your violations
+     of this Public License.
+
+  c. For the avoidance of doubt, the Licensor may also offer the
+     Licensed Material under separate terms or conditions or stop
+     distributing the Licensed Material at any time; however, doing so
+     will not terminate this Public License.
+
+  d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
+     License.
+
+
+Section 7 -- Other Terms and Conditions.
+
+  a. The Licensor shall not be bound by any additional or different
+     terms or conditions communicated by You unless expressly agreed.
+
+  b. Any arrangements, understandings, or agreements regarding the
+     Licensed Material not stated herein are separate from and
+     independent of the terms and conditions of this Public License.
+
+
+Section 8 -- Interpretation.
+
+  a. For the avoidance of doubt, this Public License does not, and
+     shall not be interpreted to, reduce, limit, restrict, or impose
+     conditions on any use of the Licensed Material that could lawfully
+     be made without permission under this Public License.
+
+  b. To the extent possible, if any provision of this Public License is
+     deemed unenforceable, it shall be automatically reformed to the
+     minimum extent necessary to make it enforceable. If the provision
+     cannot be reformed, it shall be severed from this Public License
+     without affecting the enforceability of the remaining terms and
+     conditions.
+
+  c. No term or condition of this Public License will be waived and no
+     failure to comply consented to unless expressly agreed to by the
+     Licensor.
+
+  d. Nothing in this Public License constitutes or may be interpreted
+     as a limitation upon, or waiver of, any privileges and immunities
+     that apply to the Licensor or You, including from the legal
+     processes of any jurisdiction or authority.
+
+=======================================================================
+
+Creative Commons is not a party to its public
+licenses. Notwithstanding, Creative Commons may elect to apply one of
+its public licenses to material it publishes and in those instances
+will be considered the “Licensor.” The text of the Creative Commons
+public licenses is dedicated to the public domain under the CC0 Public
+Domain Dedication. Except for the limited purpose of indicating that
+material is shared under a Creative Commons public license or as
+otherwise permitted by the Creative Commons policies published at
+creativecommons.org/policies, Creative Commons does not authorize the
+use of the trademark "Creative Commons" or any other trademark or logo
+of Creative Commons without its prior written consent including,
+without limitation, in connection with any unauthorized modifications
+to any of its public licenses or any other arrangements,
+understandings, or agreements concerning use of licensed material. For
+the avoidance of doubt, this paragraph does not form part of the
+public licenses.
+
+Creative Commons may be contacted at creativecommons.org.
diff --git a/assets/README.md b/assets/README.md
new file mode 100644
index 00000000..fe0093f3
--- /dev/null
+++ b/assets/README.md
@@ -0,0 +1,10 @@
+# Info about Licenses
+
+All the contents of this directory (original URL: <https://github.com/gnikit/fortls/tree/master/assets>) and any subsequent directories or links or copies of this directory
+are licensed under the
+[Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International](https://creativecommons.org/licenses/by-nc-nd/4.0/) license.
+
+The `symbol-class.svg` is licensed by Microsoft under a [Creative Commons Attribution 4.0 International License](https://creativecommons.org/licenses/by/4.0/legalcode),
+which allows for derivative works to change the original license.
+
+The owner of this repository reserve all other rights, whether under their respective copyrights, patents, or trademarks, whether by implication, estoppel or otherwise.
diff --git a/assets/f.svg b/assets/f.svg
new file mode 100644
index 00000000..95c3b71a
--- /dev/null
+++ b/assets/f.svg
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   width="12.673404mm"
+   height="12.298135mm"
+   viewBox="0 0 12.673404 12.298135"
+   version="1.1"
+   id="svg834"
+   inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
+   sodipodi:docname="f.svg"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:svg="http://www.w3.org/2000/svg">
+  <sodipodi:namedview
+     id="namedview836"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     inkscape:pagecheckerboard="0"
+     inkscape:document-units="mm"
+     showgrid="false"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0"
+     inkscape:zoom="6.0007856"
+     inkscape:cx="23.580246"
+     inkscape:cy="49.576842"
+     inkscape:window-width="1400"
+     inkscape:window-height="1023"
+     inkscape:window-x="520"
+     inkscape:window-y="0"
+     inkscape:window-maximized="0"
+     inkscape:current-layer="layer1" />
+  <defs
+     id="defs831" />
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-73.283318,-109.96873)">
+    <rect
+       style="fill:#734f96;fill-opacity:1;stroke:none;stroke-width:0.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
+       id="rect25873"
+       width="12.673404"
+       height="12.298135"
+       x="73.283318"
+       y="109.96873"
+       ry="3.5308964" />
+    <g
+       aria-label="F"
+       id="text1843"
+       style="font-size:10.5833px;line-height:1.25;font-family:Caladea;-inkscape-font-specification:Caladea;fill:#ffffff;stroke-width:0.3">
+      <path
+         d="m 75.688322,113.09097 h 0.89958 c 0.03175,0 0.07408,0 0.105833,0 0.338666,0.0318 0.402166,0.11641 0.402166,0.60325 v 4.46615 0.381 c 0,0.48683 -0.05292,0.5715 -0.402166,0.60325 -0.03175,0 -0.07408,0.0106 -0.105833,0.0106 h -0.888997 v 0.77258 h 4.339153 v -0.77258 h -0.804331 c -0.04233,0 -0.08467,-0.0106 -0.126999,-0.0106 -0.497415,-0.0318 -0.560915,-0.10584 -0.560915,-0.60325 v -2.0955 h 1.132413 c 0.761998,0 1.037163,0.254 1.037163,0.99483 v 0.35984 h 0.677332 v -3.50308 h -0.698498 v 0.381 c 0,0.0741 0,0.14817 -0.01058,0.22225 -0.07408,0.60325 -0.328082,0.79375 -1.079496,0.79375 h -1.05833 v -2.60349 h 2.296576 c 1.312329,0 1.799161,0.37041 1.947327,1.36524 0.02117,0.11642 0.03175,0.22225 0.03175,0.33867 l 0.01058,0.49741 h 0.719665 v -2.98449 h -7.863392 z"
+         style="font-weight:bold;font-family:Clarendon;-inkscape-font-specification:'Clarendon Bold'"
+         id="path824" />
+    </g>
+  </g>
+</svg>
diff --git a/assets/icon.svg b/assets/icon.svg
new file mode 100644
index 00000000..43480fc0
--- /dev/null
+++ b/assets/icon.svg
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   width="13.883744mm"
+   height="14.512883mm"
+   viewBox="0 0 13.883744 14.512883"
+   version="1.1"
+   id="svg9853"
+   inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
+   sodipodi:docname="icon.svg"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:svg="http://www.w3.org/2000/svg">
+  <sodipodi:namedview
+     id="namedview9855"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     inkscape:pagecheckerboard="0"
+     inkscape:document-units="mm"
+     showgrid="false"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0"
+     inkscape:zoom="6.0007856"
+     inkscape:cx="-1.9164157"
+     inkscape:cy="57.242505"
+     inkscape:window-width="1920"
+     inkscape:window-height="1023"
+     inkscape:window-x="0"
+     inkscape:window-y="0"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="layer1" />
+  <defs
+     id="defs9850" />
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-82.59595,-96.799377)">
+    <g
+       id="g865">
+      <path
+         fill="currentColor"
+         d="m 92.925121,106.02129 h 0.709225 l 2.667214,-2.82639 v -0.75259 l -1.338595,-1.4077 h -0.699287 l -1.818094,1.91575 h -4.994795 v -1.32314 l 1.858064,-1.95828 v -0.752592 l -1.997912,-2.116971 h -0.719164 l -3.995827,4.234053 v 0.75259 l 1.997925,2.11713 h 0.709226 l 1.138806,-1.2175 v 6.12899 l 0.49944,0.52844 h 4.645142 v 0.55069 l 1.328619,1.41849 h 0.709229 l 2.66721,-2.82619 v -0.7526 l -1.338591,-1.40777 h -0.699291 l -1.858055,1.95835 h -4.994909 v -4.23429 h 4.185618 v 0.50722 z m 1.688227,-3.86378 0.62936,0.66757 -1.997906,2.11709 -0.629356,-0.66757 z m 0,5.29272 0.62936,0.66756 -1.997906,2.11701 -0.629356,-0.66757 z m -9.669883,-4.66817 -1.288645,-1.37608 3.286565,-3.482563 1.298635,1.365556 z"
+         id="path2-75"
+         style="fill:#ee9d28;fill-opacity:1;stroke-width:1.02831" />
+      <rect
+         style="fill:#734f96;fill-opacity:1;stroke:none;stroke-width:0.0313294;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         id="rect881"
+         width="6.6605945"
+         height="6.6605945"
+         x="89.819099"
+         y="99.595383"
+         ry="1.2387406" />
+      <path
+         inkscape:connector-curvature="0"
+         id="path1412"
+         d="m 90.726921,105.24001 c 0,-0.26484 0.0019,-0.28844 0.02524,-0.28893 0.01389,-2.7e-4 0.150824,-0.006 0.304329,-0.0136 0.247197,-0.0116 0.28666,-0.0169 0.34531,-0.0463 0.04637,-0.0233 0.07509,-0.0505 0.09582,-0.0909 0.02938,-0.0573 0.02961,-0.0705 0.02961,-1.88174 0,-1.702 -0.0016,-1.82712 -0.02497,-1.86966 -0.03152,-0.0576 -0.08618,-0.10087 -0.15336,-0.12139 -0.02883,-0.009 -0.180559,-0.0195 -0.337192,-0.0237 l -0.284784,-0.008 v -0.28655 -0.28655 h 2.422475 2.422472 v 1.07425 1.07425 l -0.255943,-0.001 c -0.140772,-7.8e-4 -0.272169,-0.006 -0.291998,-0.0115 -0.03546,-0.01 -0.03632,-0.0138 -0.05332,-0.23288 -0.02181,-0.28125 -0.03963,-0.4058 -0.08485,-0.59288 -0.09776,-0.40447 -0.30404,-0.62451 -0.640881,-0.68363 -0.141325,-0.0248 -0.757914,-0.0488 -1.264909,-0.0492 l -0.349676,-2.8e-4 v 0.80772 0.80772 l 0.183851,-0.009 c 0.210196,-0.0104 0.375427,-0.0358 0.455189,-0.0699 0.08494,-0.0363 0.174641,-0.12813 0.216628,-0.2217 0.04387,-0.0978 0.07258,-0.22622 0.09561,-0.4279 0.0091,-0.0793 0.01947,-0.15879 0.02313,-0.17664 0.0066,-0.0323 0.0078,-0.0324 0.28757,-0.0324 h 0.280902 v 1.29054 1.29055 h -0.286266 -0.286263 l -0.0096,-0.14888 c -0.01744,-0.26959 -0.06992,-0.52491 -0.133,-0.64689 -0.08622,-0.16674 -0.255921,-0.24169 -0.597013,-0.2637 -0.07534,-0.005 -0.15851,-0.0122 -0.184819,-0.0162 l -0.04783,-0.007 0.0056,0.81193 c 0.0059,0.85636 0.0085,0.88982 0.07343,0.96159 0.06681,0.0738 0.105775,0.0789 0.784481,0.10206 0.01381,4.3e-4 0.01802,0.0682 0.01802,0.28901 v 0.28839 h -1.39148 -1.391481 z"
+         style="fill:#ffffff;stroke-width:0.0313297" />
+    </g>
+  </g>
+</svg>
diff --git a/assets/logo.svg b/assets/logo.svg
index 0c7e595e..0af1e46f 100644
--- a/assets/logo.svg
+++ b/assets/logo.svg
@@ -2,9 +2,9 @@
 <!-- Created with Inkscape (http://www.inkscape.org/) -->
 
 <svg
-   width="210mm"
-   height="297mm"
-   viewBox="0 0 210 297"
+   width="50.282166mm"
+   height="15.736053mm"
+   viewBox="0 0 50.282166 15.736053"
    version="1.1"
    id="svg996"
    sodipodi:docname="logo.svg"
@@ -23,33 +23,39 @@
      inkscape:pagecheckerboard="0"
      inkscape:document-units="mm"
      showgrid="false"
-     inkscape:zoom="0.75109156"
-     inkscape:cx="663.70071"
-     inkscape:cy="570.50302"
+     inkscape:zoom="3.0043662"
+     inkscape:cx="65.071961"
+     inkscape:cy="52.756551"
      inkscape:window-width="1920"
-     inkscape:window-height="1053"
-     inkscape:window-x="1920"
+     inkscape:window-height="1023"
+     inkscape:window-x="0"
      inkscape:window-y="0"
      inkscape:window-maximized="1"
-     inkscape:current-layer="layer1" />
+     inkscape:current-layer="layer1"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0" />
   <defs
      id="defs993" />
   <g
      inkscape:label="Layer 1"
      inkscape:groupmode="layer"
-     id="layer1">
+     id="layer1"
+     transform="translate(-53.047507,-96.478807)">
     <g
-       id="g21395"
-       inkscape:export-filename="/home/gn/Code/Python/fortls/assets/logo.png"
-       inkscape:export-xdpi="300.22787"
-       inkscape:export-ydpi="300.22787">
+       aria-label="ort"
+       id="text18702"
+       style="font-size:14.1111px;line-height:1.25;font-family:Caladea;-inkscape-font-specification:Caladea;letter-spacing:0.590021px;word-spacing:0px;fill:#ffffff;stroke-width:0.0700042" />
+    <g
+       id="g1038">
       <rect
          style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.0914084;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
          id="rect18694"
          width="23.999231"
          height="15.736053"
-         x="37.717922"
-         y="130.50795"
+         x="79.330437"
+         y="96.478806"
          ry="2.9265988"
          inkscape:export-filename="/home/gn/Code/TypeScript/vscode-fortran-support/assets/modern-fortran-logo.png"
          inkscape:export-xdpi="150.11394"
@@ -59,48 +65,48 @@
          id="rect18696"
          width="33.257595"
          height="15.736053"
-         x="11.43499"
-         y="130.50795"
+         x="53.047508"
+         y="96.478806"
          ry="2.9265988"
          inkscape:export-filename="/home/gn/Code/TypeScript/vscode-fortran-support/assets/modern-fortran-logo.png"
          inkscape:export-xdpi="150.11394"
          inkscape:export-ydpi="150.11394" />
-      <text
-         xml:space="preserve"
+      <path
+         d="m 64.105935,106.50585 c 0,2.14489 1.53811,3.683 3.97933,3.683 2.088443,-0.0141 3.499553,-1.12889 3.852331,-2.86456 0.52211,-2.46944 -1.143,-4.50144 -3.866442,-4.50144 -2.412998,0 -3.951108,1.55222 -3.965219,3.683 z m 1.834443,0 c 0,-0.18344 0.01411,-0.35278 0.04233,-0.53622 0.169333,-1.46756 1.001888,-2.25778 2.088443,-2.25778 1.086555,0 1.904999,0.79022 2.088443,2.25778 0.04233,0.35278 0.04233,0.71967 0,1.07244 -0.169333,1.46756 -1.001888,2.27189 -2.088443,2.27189 -1.100666,0 -1.91911,-0.80433 -2.088443,-2.27189 -0.02822,-0.18344 -0.04233,-0.36689 -0.04233,-0.53622 z"
+         style="font-size:14.1111px;line-height:1.25;font-family:Clarendon;-inkscape-font-specification:Clarendon;letter-spacing:0.590021px;word-spacing:0px;fill:#ffffff;stroke-width:0.0700042"
+         id="path843"
+         transform="translate(1.2291667e-6)" />
+      <path
+         d="m 73.190835,103.93763 h 0.691444 c 0.634999,0 0.719666,0.0847 0.719666,0.70556 v 3.17499 l 0.01411,0.49389 c 0.01411,0.57856 -0.127,0.70556 -0.691444,0.70556 h -0.705555 v 0.93133 h 4.741329 v -0.93133 h -0.77611 c -0.719666,0 -0.846666,-0.11289 -0.846666,-0.70556 v -2.13077 c 0,-0.98778 0.211666,-1.59456 0.677333,-2.08845 0.451555,-0.47977 0.846666,-0.635 1.269999,-0.56444 -0.141111,0.11289 -0.254,0.26811 -0.310444,0.43744 -0.183445,0.59267 0.225777,1.15711 0.860777,1.17122 0.592666,0.0141 1.072443,-0.52211 0.959555,-1.24177 -0.08467,-0.56445 -0.493889,-0.98778 -1.185333,-1.11478 -0.112889,-0.0282 -0.225778,-0.0282 -0.338666,-0.0282 -0.635,0 -1.171222,0.29633 -1.777999,1.10066 -0.05644,0.0706 -0.112889,0.14112 -0.155222,0.21167 v -1.07244 h -3.146775 z"
+         id="path845"
+         style="font-size:14.1111px;line-height:1.25;font-family:Caladea;-inkscape-font-specification:Caladea;letter-spacing:0.590021px;word-spacing:0px;fill:#ffffff;stroke-width:0.0700042"
+         transform="translate(1.2291667e-6)" />
+      <path
+         d="m 80.2437,103.90941 h 1.227666 v 3.61244 c 0,1.778 0.663222,2.56822 2.130776,2.58233 1.354666,0.0141 2.088443,-0.74789 2.088443,-2.14489 v -0.70555 h -0.846666 v 0.53622 c 0,0.0706 0,0.14111 0,0.21167 -0.04233,0.55033 -0.183444,0.83255 -0.409222,1.00189 -0.380999,0.28222 -0.959555,0.11288 -1.142999,-0.36689 -0.04233,-0.127 -0.07055,-0.28222 -0.07055,-0.47978 l -0.01411,-0.35278 v -0.18344 -3.71122 h 2.116665 v -0.91722 h -2.116665 v -2.44122 h -0.889 c 0,0.15522 0,0.29633 -0.02822,0.45155 -0.169333,1.19945 -0.677333,1.80622 -1.580443,1.97556 -0.155222,0.0282 -0.310444,0.0423 -0.465667,0.0282 z"
+         id="path847"
          style="font-size:14.1111px;line-height:1.25;font-family:Caladea;-inkscape-font-specification:Caladea;letter-spacing:0.590021px;word-spacing:0px;fill:#ffffff;stroke-width:0.0700042"
-         x="22.070086"
-         y="143.9781"
-         id="text18702"><tspan
-           sodipodi:role="line"
-           id="tspan18700"
-           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.1111px;font-family:Clarendon;-inkscape-font-specification:Clarendon;letter-spacing:0.590021px;fill:#ffffff;stroke-width:0.0700042"
-           x="22.070086"
-           y="143.9781"
-           dx="0">o<tspan
-   style="letter-spacing:0.590021px"
-   id="tspan18698">rt</tspan></tspan></text>
-      <text
-         xml:space="preserve"
-         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16.2278px;line-height:1.25;font-family:Clarendon;-inkscape-font-specification:Clarendon;fill:#ffffff;stroke-width:0.264583"
-         x="12.420631"
-         y="144.21799"
-         id="text18706"><tspan
-           sodipodi:role="line"
-           id="tspan18704"
-           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16.2278px;font-family:Clarendon;-inkscape-font-specification:Clarendon;fill:#ffffff;stroke-width:0.264583"
-           x="12.420631"
-           y="144.21799">F</tspan></text>
-      <text
-         xml:space="preserve"
-         style="font-size:16.2278px;line-height:1.25;font-family:'Segoe UI';-inkscape-font-specification:'Segoe UI';stroke-width:0.264583"
-         x="44.405636"
-         y="144.05333"
-         id="text18710"><tspan
-           sodipodi:role="line"
-           id="tspan18708"
-           style="font-weight:bold;font-size:16.2278px;stroke-width:0.264583"
-           x="44.405636"
-           y="144.05333">LS</tspan></text>
+         transform="translate(1.2291667e-6)" />
+      <g
+         aria-label="F"
+         id="text18706"
+         style="font-size:16.2278px;line-height:1.25;font-family:Clarendon;-inkscape-font-specification:Clarendon;fill:#ffffff;stroke-width:0.264583">
+        <path
+           d="m 54.195428,99.705684 h 1.379363 c 0.04868,0 0.113594,0 0.162278,0 0.519289,0.04868 0.616656,0.178506 0.616656,0.924986 v 6.84813 0.5842 c 0,0.74648 -0.08114,0.8763 -0.616656,0.92499 -0.04868,0 -0.113595,0.0162 -0.162278,0.0162 h -1.363135 v 1.18463 h 6.653398 v -1.18463 h -1.233313 c -0.06491,0 -0.129823,-0.0162 -0.194734,-0.0162 -0.762706,-0.0487 -0.860073,-0.16228 -0.860073,-0.92499 v -3.2131 h 1.736374 c 1.168402,0 1.590325,0.38946 1.590325,1.52541 v 0.55174 h 1.038579 v -5.3714 h -1.071035 v 0.5842 c 0,0.1136 0,0.22719 -0.01623,0.34079 -0.113594,0.92498 -0.503061,1.21708 -1.655235,1.21708 h -1.62278 v -3.992036 h 3.521433 c 2.012247,0 2.758726,0.567976 2.985915,2.093386 0.03245,0.17851 0.04868,0.34078 0.04868,0.51929 l 0.01623,0.76271 h 1.10349 V 98.504827 H 54.195428 Z"
+           id="path840" />
+      </g>
+      <g
+         aria-label="LS"
+         id="text18710"
+         style="font-size:16.2278px;line-height:1.25;font-family:'Segoe UI';-inkscape-font-specification:'Segoe UI';stroke-width:0.264583">
+        <path
+           d="M 94.084508,110.02419 H 87.317642 V 98.661563 h 2.559365 v 9.286617 h 4.207501 z"
+           style="font-weight:bold"
+           id="path850" />
+        <path
+           d="m 95.090822,109.58839 v -2.5356 q 0.689365,0.57844 1.497585,0.87161 0.808221,0.28526 1.632289,0.28526 0.483348,0 0.839915,-0.0872 0.364492,-0.0872 0.602204,-0.23771 0.245636,-0.15848 0.364495,-0.3645 0.11885,-0.21394 0.11885,-0.45957 0,-0.3328 -0.190167,-0.59428 -0.19017,-0.26149 -0.522966,-0.48335 -0.324873,-0.22186 -0.776526,-0.42788 -0.451653,-0.20602 -0.974619,-0.41996 -1.331187,-0.55466 -1.988856,-1.35496 -0.649746,-0.80029 -0.649746,-1.93339 0,-0.88746 0.356568,-1.52135 0.356568,-0.641826 0.966695,-1.05386 0.618051,-0.412034 1.426271,-0.602203 0.808221,-0.198093 1.711526,-0.198093 0.88746,0 1.5689,0.110932 0.68936,0.103008 1.2678,0.324873 v 2.369191 q -0.28526,-0.19809 -0.62598,-0.34864 -0.3328,-0.15055 -0.68936,-0.24564 -0.35657,-0.103 -0.71314,-0.15055 -0.348643,-0.0475 -0.665593,-0.0475 -0.435805,0 -0.792373,0.0872 -0.356567,0.0792 -0.602203,0.22979 -0.245636,0.15055 -0.380339,0.36449 -0.134704,0.20602 -0.134704,0.4675 0,0.28526 0.150551,0.51504 0.150551,0.22187 0.427882,0.42789 0.27733,0.19809 0.673517,0.39618 0.396186,0.19017 0.895381,0.39619 0.681441,0.28525 1.220251,0.61013 0.54674,0.31695 0.935,0.72106 0.38827,0.40411 0.59428,0.92707 0.20602,0.51504 0.20602,1.20441 0,0.95085 -0.36449,1.60059 -0.35657,0.64183 -0.97462,1.04594 -0.61805,0.39618 -1.44212,0.5705 -0.816143,0.17433 -1.727372,0.17433 -0.935,0 -1.782839,-0.15848 -0.839916,-0.15847 -1.457967,-0.47542 z"
+           style="font-weight:bold"
+           id="path852" />
+      </g>
     </g>
   </g>
 </svg>
diff --git a/assets/logo2-animated.svg b/assets/logo2-animated.svg
new file mode 100644
index 00000000..47aaddbb
--- /dev/null
+++ b/assets/logo2-animated.svg
@@ -0,0 +1,209 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   width="26.453466mm"
+   height="16.533251mm"
+   viewBox="0 0 26.453467 16.533251"
+   version="1.1"
+   id="svg23532"
+   sodipodi:docname="logo2-animated.svg"
+   inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:svg="http://www.w3.org/2000/svg">
+  <sodipodi:namedview
+     id="namedview23534"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     inkscape:pagecheckerboard="0"
+     inkscape:document-units="mm"
+     showgrid="false"
+     inkscape:zoom="8.4863924"
+     inkscape:cx="47.311034"
+     inkscape:cy="29.989186"
+     inkscape:window-width="1920"
+     inkscape:window-height="1023"
+     inkscape:window-x="0"
+     inkscape:window-y="0"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="layer1"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0" />
+  <defs
+     id="defs23529" />
+  <style
+     id="style4">
+      @keyframes cursor-blink {
+         0%  {opacity: 1;}
+         25% {opacity: 0;}
+         50% {opacity: 1;}
+      }
+
+      #cursor {
+         animation: cursor-blink 1.5s infinite;
+         animation-timing-function: ease-in-out;
+      }
+   </style>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-50.797219,-79.456839)">
+    <rect
+       style="fill:#1e1e1e;fill-opacity:1;stroke:#1e1e1e;stroke-width:0.317166;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+       id="rect2268"
+       width="26.136299"
+       height="15.335978"
+       x="50.955803"
+       y="80.495529"
+       ry="0.067764252" />
+    <rect
+       style="fill:#094771;fill-opacity:1;stroke:#454545;stroke-width:0.22;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+       id="rect27716"
+       width="16.411741"
+       height="4.6883922"
+       x="59.662621"
+       y="87.264824" />
+    <rect
+       style="fill:#252526;fill-opacity:1;stroke:none;stroke-width:0.118454;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+       id="rect190284"
+       width="16.411741"
+       height="0.17200001"
+       x="59.662621"
+       y="91.783218"
+       ry="0" />
+    <g
+       aria-label="Fort"
+       transform="scale(0.97145175,1.0293872)"
+       id="text69430"
+       style="font-size:3.50005px;line-height:1.25;font-family:Caladea;-inkscape-font-specification:Caladea;fill:#18a3ff;stroke-width:0.656262">
+      <path
+         d="m 66.355023,86.485801 h 0.297504 c 0.0105,0 0.0245,0 0.035,0 0.112001,0.0105 0.133002,0.0385 0.133002,0.199503 v 1.477021 0.126002 c 0,0.161003 -0.0175,0.189003 -0.133002,0.199503 -0.0105,0 -0.0245,0.0035 -0.035,0.0035 h -0.294004 v 0.255504 h 1.435021 V 88.49133 H 67.52754 c -0.014,0 -0.028,-0.0035 -0.042,-0.0035 -0.164502,-0.0105 -0.185502,-0.035 -0.185502,-0.199503 v -0.69301 h 0.374505 c 0.252004,0 0.343005,0.084 0.343005,0.329005 v 0.119002 h 0.224003 v -1.158517 h -0.231003 v 0.126002 c 0,0.0245 0,0.049 -0.0035,0.0735 -0.0245,0.199503 -0.108502,0.262504 -0.357005,0.262504 h -0.350005 v -0.861013 h 0.75951 c 0.434007,0 0.595009,0.122502 0.64401,0.451507 0.007,0.0385 0.0105,0.0735 0.0105,0.112002 l 0.0035,0.164502 h 0.238003 v -0.987014 h -2.600537 z"
+         style="font-family:Clarendon;-inkscape-font-specification:Clarendon"
+         id="path1004" />
+      <path
+         d="m 68.612549,87.892822 c 0,0.532007 0.381505,0.913513 0.987014,0.913513 0.518007,-0.0035 0.868012,-0.280004 0.955513,-0.710511 0.129502,-0.612508 -0.283504,-1.116515 -0.959013,-1.116515 -0.598509,0 -0.980014,0.385005 -0.983514,0.913513 z m 0.455006,0 c 0,-0.0455 0.0035,-0.0875 0.0105,-0.133002 0.042,-0.364006 0.248504,-0.560008 0.518008,-0.560008 0.269504,0 0.472506,0.196002 0.518007,0.560008 0.0105,0.0875 0.0105,0.178502 0,0.266003 -0.042,0.364006 -0.248503,0.563509 -0.518007,0.563509 -0.273004,0 -0.476007,-0.199503 -0.518008,-0.563509 -0.007,-0.0455 -0.0105,-0.091 -0.0105,-0.133001 z"
+         style="font-family:Clarendon;-inkscape-font-specification:Clarendon"
+         id="path1006" />
+      <path
+         d="m 70.719578,87.255812 h 0.171502 c 0.157503,0 0.178503,0.021 0.178503,0.175003 v 0.787511 l 0.0035,0.122502 c 0.0035,0.143502 -0.0315,0.175002 -0.171502,0.175002 h -0.175003 v 0.231004 h 1.176017 V 88.51583 h -0.192503 c -0.178502,0 -0.210003,-0.028 -0.210003,-0.175002 V 87.81232 c 0,-0.245003 0.0525,-0.395505 0.168003,-0.518007 0.112001,-0.119002 0.210003,-0.157502 0.315004,-0.140002 -0.035,0.028 -0.063,0.0665 -0.077,0.108502 -0.0455,0.147002 0.056,0.287004 0.213503,0.290504 0.147002,0.0035 0.266004,-0.129502 0.238003,-0.308005 -0.021,-0.140002 -0.122501,-0.245003 -0.294004,-0.276504 -0.028,-0.007 -0.056,-0.007 -0.084,-0.007 -0.157502,0 -0.290504,0.0735 -0.441006,0.273004 -0.014,0.0175 -0.028,0.035 -0.0385,0.0525 v -0.266004 h -0.780511 z"
+         style="font-family:Clarendon;-inkscape-font-specification:Clarendon"
+         id="path1008" />
+      <path
+         d="m 72.510525,87.248812 h 0.304505 v 0.896013 c 0,0.441006 0.164502,0.637009 0.528507,0.640509 0.336005,0.0035 0.518008,-0.185502 0.518008,-0.532007 v -0.175003 h -0.210003 v 0.133002 c 0,0.0175 0,0.035 0,0.0525 -0.0105,0.136502 -0.0455,0.206503 -0.101502,0.248503 -0.0945,0.07 -0.238003,0.028 -0.283504,-0.091 -0.0105,-0.0315 -0.0175,-0.07 -0.0175,-0.119002 l -0.0035,-0.0875 v -0.0455 -0.920514 h 0.525007 V 87.021309 H 73.245536 V 86.4158 h -0.220503 c 0,0.0385 0,0.0735 -0.007,0.112002 -0.042,0.297504 -0.168003,0.448006 -0.392006,0.490007 -0.0385,0.007 -0.077,0.0105 -0.115502,0.007 z"
+         style="font-family:Clarendon;-inkscape-font-specification:Clarendon"
+         id="path1010" />
+    </g>
+    <g
+       aria-label="LS"
+       transform="scale(0.97145175,1.0293872)"
+       id="text81726"
+       style="font-size:3.65019px;line-height:1.25;font-family:'Segoe UI';-inkscape-font-specification:'Segoe UI';fill:#eef5f9;stroke-width:0.059514">
+      <path
+         d="m 75.604349,88.761787 h -1.522101 v -2.555846 h 0.575689 v 2.088879 h 0.946412 z"
+         style="font-weight:bold"
+         id="path1013" />
+      <path
+         d="m 75.830703,88.66376 v -0.570342 q 0.155062,0.130109 0.336859,0.196055 0.181796,0.06416 0.367157,0.06416 0.108722,0 0.188926,-0.0196 0.08199,-0.01961 0.135456,-0.05347 0.05525,-0.03565 0.08199,-0.08199 0.02674,-0.04812 0.02674,-0.103374 0,-0.07486 -0.04278,-0.133674 -0.04278,-0.05882 -0.117633,-0.108721 -0.07307,-0.04991 -0.174667,-0.09625 -0.101592,-0.04634 -0.219225,-0.09446 -0.29943,-0.124762 -0.447363,-0.304776 -0.14615,-0.180015 -0.14615,-0.434886 0,-0.19962 0.08021,-0.342206 0.0802,-0.144367 0.217443,-0.237048 0.139021,-0.09268 0.320817,-0.135456 0.181797,-0.04456 0.384981,-0.04456 0.19962,0 0.352899,0.02495 0.155062,0.02317 0.285171,0.07308 v 0.532914 q -0.06416,-0.04456 -0.140803,-0.07842 -0.07486,-0.03386 -0.155062,-0.05525 -0.0802,-0.02317 -0.160408,-0.03386 -0.07842,-0.01069 -0.149715,-0.01069 -0.09803,0 -0.178232,0.0196 -0.0802,0.01782 -0.135456,0.05169 -0.05525,0.03386 -0.08555,0.08199 -0.0303,0.04634 -0.0303,0.105157 0,0.06416 0.03386,0.115851 0.03386,0.0499 0.09624,0.09624 0.06238,0.04456 0.151497,0.08912 0.08912,0.04278 0.201402,0.08912 0.15328,0.06416 0.274478,0.137239 0.12298,0.07129 0.210313,0.162191 0.08733,0.0909 0.133674,0.208531 0.04634,0.115851 0.04634,0.270913 0,0.213878 -0.08199,0.360028 -0.0802,0.144368 -0.219225,0.235266 -0.139021,0.08912 -0.324383,0.128327 -0.183578,0.03921 -0.388545,0.03921 -0.210314,0 -0.401022,-0.03565 -0.188926,-0.03565 -0.327947,-0.106939 z"
+         style="font-weight:bold"
+         id="path1015" />
+    </g>
+    <path
+       fill="currentColor"
+       d="m 62.921918,90.020477 h 0.187439 l 0.704913,-0.74698 v -0.1989 l -0.353775,-0.37204 h -0.184813 l -0.4805,0.50631 h -1.320064 v -0.34969 l 0.491064,-0.51755 v -0.1989 l -0.528024,-0.55949 h -0.190066 l -1.056049,1.11901 v 0.1989 l 0.528027,0.55953 h 0.18744 l 0.300973,-0.32177 v 1.61982 l 0.131996,0.13966 h 1.227655 v 0.14554 l 0.351138,0.37489 h 0.18744 l 0.704912,-0.74693 v -0.1989 l -0.353774,-0.37206 h -0.184814 l -0.491061,0.51757 H 61.46188 v -1.11907 h 1.106208 v 0.13405 z m 0.446178,-1.02115 0.166332,0.17643 -0.528022,0.55952 -0.166332,-0.17643 z m 0,1.3988 0.166332,0.17643 -0.528022,0.5595 -0.166332,-0.17643 z m -2.555634,-1.23374 -0.340572,-0.36368 0.868599,-0.9204 0.343213,0.3609 z"
+       id="path2"
+       style="fill:#ee9d28;fill-opacity:1;stroke-width:0.271769" />
+    <g
+       aria-label="Fort"
+       transform="scale(0.97145175,1.0293872)"
+       id="text195534"
+       style="font-size:3.50005px;line-height:1.25;font-family:Caladea;-inkscape-font-specification:Caladea;fill:#d4d4d4;stroke-width:0.656262">
+      <path
+         d="m 53.799134,81.891578 h 0.297504 c 0.0105,0 0.0245,0 0.035,0 0.112001,0.0105 0.133002,0.0385 0.133002,0.199503 v 1.477021 0.126002 c 0,0.161003 -0.0175,0.189003 -0.133002,0.199503 -0.0105,0 -0.0245,0.0035 -0.035,0.0035 h -0.294004 v 0.255504 h 1.435021 v -0.255504 h -0.266004 c -0.014,0 -0.028,-0.0035 -0.042,-0.0035 -0.164502,-0.0105 -0.185503,-0.035 -0.185503,-0.199503 v -0.69301 h 0.374506 c 0.252003,0 0.343005,0.084 0.343005,0.329005 v 0.119002 h 0.224003 v -1.158517 h -0.231003 v 0.126002 c 0,0.0245 0,0.049 -0.0035,0.0735 -0.0245,0.199503 -0.108502,0.262504 -0.357006,0.262504 h -0.350005 v -0.861013 h 0.759511 c 0.434007,0 0.595009,0.122502 0.64401,0.451507 0.007,0.0385 0.0105,0.0735 0.0105,0.112001 l 0.0035,0.164503 h 0.238003 v -0.987014 h -2.600537 z"
+         style="font-family:Clarendon;-inkscape-font-specification:Clarendon"
+         id="path949" />
+      <path
+         d="m 56.05666,83.298599 c 0,0.532007 0.381505,0.913513 0.987014,0.913513 0.518007,-0.0035 0.868012,-0.280004 0.955513,-0.710511 0.129502,-0.612508 -0.283504,-1.116516 -0.959013,-1.116516 -0.598509,0 -0.980014,0.385006 -0.983514,0.913514 z m 0.455006,0 c 0,-0.0455 0.0035,-0.0875 0.0105,-0.133002 0.042,-0.364006 0.248504,-0.560008 0.518008,-0.560008 0.269503,0 0.472506,0.196002 0.518007,0.560008 0.0105,0.0875 0.0105,0.178502 0,0.266003 -0.042,0.364006 -0.248504,0.563509 -0.518007,0.563509 -0.273004,0 -0.476007,-0.199503 -0.518008,-0.563509 -0.007,-0.0455 -0.0105,-0.091 -0.0105,-0.133001 z"
+         style="font-family:Clarendon;-inkscape-font-specification:Clarendon"
+         id="path951" />
+      <path
+         d="m 58.163689,82.661589 h 0.171502 c 0.157503,0 0.178503,0.021 0.178503,0.175003 v 0.787511 l 0.0035,0.122502 c 0.0035,0.143502 -0.0315,0.175002 -0.171503,0.175002 h -0.175002 v 0.231004 h 1.176017 v -0.231004 h -0.192503 c -0.178503,0 -0.210003,-0.028 -0.210003,-0.175002 v -0.528508 c 0,-0.245003 0.0525,-0.395505 0.168002,-0.518007 0.112002,-0.119002 0.210003,-0.157502 0.315005,-0.140002 -0.035,0.028 -0.063,0.0665 -0.077,0.108502 -0.0455,0.147002 0.056,0.287004 0.213503,0.290504 0.147002,0.0035 0.266004,-0.129502 0.238003,-0.308005 -0.021,-0.140002 -0.122501,-0.245003 -0.294004,-0.276504 -0.028,-0.007 -0.056,-0.007 -0.084,-0.007 -0.157502,0 -0.290504,0.0735 -0.441006,0.273004 -0.014,0.0175 -0.028,0.035 -0.0385,0.0525 v -0.266004 h -0.780511 z"
+         style="font-family:Clarendon;-inkscape-font-specification:Clarendon"
+         id="path953" />
+      <path
+         d="m 59.954636,82.654589 h 0.304504 v 0.896013 c 0,0.441006 0.164503,0.637009 0.528508,0.640509 0.336005,0.0035 0.518007,-0.185502 0.518007,-0.532007 v -0.175003 h -0.210003 v 0.133002 c 0,0.0175 0,0.035 0,0.0525 -0.0105,0.136502 -0.0455,0.206503 -0.101501,0.248503 -0.0945,0.07 -0.238003,0.028 -0.283504,-0.091 -0.0105,-0.0315 -0.0175,-0.07 -0.0175,-0.119002 l -0.0035,-0.0875 v -0.0455 -0.920514 h 0.525007 v -0.227503 h -0.525007 v -0.605509 h -0.220503 c 0,0.0385 0,0.0735 -0.007,0.112002 -0.042,0.297504 -0.168002,0.448006 -0.392005,0.490007 -0.0385,0.007 -0.077,0.0105 -0.115502,0.007 z"
+         style="font-family:Clarendon;-inkscape-font-specification:Clarendon"
+         id="path955" />
+    </g>
+    <path
+       id="rect224117"
+       style="fill:none;stroke:#282828;stroke-width:0.207346;stroke-linecap:square;paint-order:markers stroke fill"
+       d="M 77.14556,87.051176 H 51.781841 V 83.667264 H 77.14556"
+       sodipodi:nodetypes="cccc" />
+    <path
+       style="fill:#aeafad;fill-opacity:1;stroke:#aeafad;stroke-width:0.20122;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="m 59.653227,87.159715 v -3.5987"
+       id="cursor" />
+    <g
+       id="g22415"
+       transform="translate(27.973565,75.283141)">
+      <rect
+         style="fill:#333333;fill-opacity:1;stroke:#333333;stroke-width:0.177537;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+         id="rect2270"
+         width="26.275463"
+         height="0.2450036"
+         x="22.912422"
+         y="5.0824447"
+         ry="0" />
+      <rect
+         style="fill:#333333;fill-opacity:1;stroke:#333333;stroke-width:0.339742;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+         id="rect22125"
+         width="26.113001"
+         height="0.90277708"
+         x="22.993525"
+         y="4.3435688"
+         ry="0.066498294" />
+    </g>
+    <g
+       id="g16561"
+       transform="translate(0.192802,-88.008971)">
+      <ellipse
+         style="fill:#52c22b;fill-opacity:1;stroke:none;stroke-width:0.207239;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;paint-order:markers stroke fill"
+         id="circle2009-3"
+         cx="53.077602"
+         cy="168.08707"
+         rx="0.31191254"
+         ry="0.3305144" />
+      <ellipse
+         style="fill:#e6bf28;fill-opacity:1;stroke:none;stroke-width:0.207239;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;paint-order:markers stroke fill"
+         id="circle2011-6"
+         cx="52.167198"
+         cy="168.08707"
+         rx="0.31191254"
+         ry="0.3305144" />
+      <ellipse
+         style="fill:#fe5a51;fill-opacity:1;stroke:none;stroke-width:0.207239;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;paint-order:markers stroke fill"
+         id="circle2013-7"
+         cx="51.256775"
+         cy="168.08707"
+         rx="0.31191254"
+         ry="0.3305144" />
+    </g>
+    <rect
+       style="fill:#252526;fill-opacity:1;stroke:none;stroke-width:0.048285;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+       id="rect24953"
+       width="4.6903944"
+       height="0.1"
+       x="87.264824"
+       y="-76.074356"
+       ry="0"
+       transform="rotate(90)" />
+  </g>
+</svg>
diff --git a/assets/logos.workspace.svg b/assets/logos.workspace.svg
new file mode 100644
index 00000000..44725457
--- /dev/null
+++ b/assets/logos.workspace.svg
@@ -0,0 +1,1338 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   width="210mm"
+   height="297mm"
+   viewBox="0 0 210 297"
+   version="1.1"
+   id="svg824"
+   inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
+   sodipodi:docname="logos.workspace.svg"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:svg="http://www.w3.org/2000/svg">
+  <sodipodi:namedview
+     id="namedview826"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     inkscape:pagecheckerboard="0"
+     inkscape:document-units="mm"
+     showgrid="false"
+     inkscape:zoom="1.4142136"
+     inkscape:cx="174.30182"
+     inkscape:cy="225.92062"
+     inkscape:window-width="1920"
+     inkscape:window-height="1053"
+     inkscape:window-x="0"
+     inkscape:window-y="0"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="layer1"
+     showguides="false" />
+  <defs
+     id="defs821">
+    <marker
+       style="overflow:visible"
+       id="Arrow1Lstart"
+       refX="0.0"
+       refY="0.0"
+       orient="auto"
+       inkscape:stockid="Arrow1Lstart"
+       inkscape:isstock="true">
+      <path
+         transform="scale(0.8) translate(12.5,0)"
+         style="fill-rule:evenodd;fill:context-stroke;stroke:context-stroke;stroke-width:1.0pt"
+         d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+         id="path1623" />
+    </marker>
+  </defs>
+  <style
+     id="style6">
+      @keyframes cursor-blink {
+         0%  {opacity: 1;}
+         25% {opacity: 0;}
+         50% {opacity: 1;}
+      }
+
+      #cursor {
+         animation: cursor-blink 1.5s infinite;
+         animation-timing-function: ease-in-out;
+      }
+   </style>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <g
+       id="g223431"
+       transform="matrix(0.8667064,0,0,0.93342485,9.4892506,12.902558)" />
+    <rect
+       style="fill:none;stroke:#800000;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none"
+       id="rect973"
+       width="14.158861"
+       height="13.477776"
+       x="62.896824"
+       y="12.106527"
+       ry="0" />
+    <rect
+       style="fill:none;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round"
+       id="rect1115"
+       width="4.8208375"
+       height="0.11758652"
+       x="72.037224"
+       y="24.904179"
+       ry="0.058793258" />
+    <path
+       style="fill:#800000;stroke:#800000;stroke-width:0.1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+       d="M 62.85693,13.230116 H 77.103754"
+       id="path1541" />
+    <circle
+       style="fill:#800000;stroke:none;stroke-width:0.1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;paint-order:markers stroke fill"
+       id="circle2009"
+       cx="64.338875"
+       cy="12.720037"
+       r="0.15493163" />
+    <circle
+       style="fill:#800000;stroke:none;stroke-width:0.1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;paint-order:markers stroke fill"
+       id="circle2011"
+       cx="63.886662"
+       cy="12.720037"
+       r="0.15493163" />
+    <circle
+       style="fill:#800000;stroke:none;stroke-width:0.1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;paint-order:markers stroke fill"
+       id="circle2013"
+       cx="63.434444"
+       cy="12.720037"
+       r="0.15493163" />
+    <rect
+       style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.0176317;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="rect18694"
+       width="4.6291838"
+       height="3.0353088"
+       x="70.267403"
+       y="15.820318"
+       ry="0.5645082"
+       inkscape:export-filename="/home/gn/Code/TypeScript/vscode-fortran-support/assets/modern-fortran-logo.png"
+       inkscape:export-xdpi="150.11394"
+       inkscape:export-ydpi="150.11394" />
+    <rect
+       style="fill:#734f96;fill-opacity:1;stroke:none;stroke-width:0.0207558;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="rect18696"
+       width="6.4150186"
+       height="3.0353088"
+       x="65.197716"
+       y="15.820318"
+       ry="0.5645082"
+       inkscape:export-filename="/home/gn/Code/TypeScript/vscode-fortran-support/assets/modern-fortran-logo.png"
+       inkscape:export-xdpi="150.11394"
+       inkscape:export-ydpi="150.11394" />
+    <text
+       xml:space="preserve"
+       style="font-size:2.72187px;line-height:1.25;font-family:Caladea;-inkscape-font-specification:Caladea;letter-spacing:0.113808px;word-spacing:0px;fill:#ffffff;stroke-width:0.013503"
+       x="67.249107"
+       y="18.418552"
+       id="text18702"><tspan
+         sodipodi:role="line"
+         id="tspan18700"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.72187px;font-family:Clarendon;-inkscape-font-specification:Clarendon;letter-spacing:0.113808px;fill:#ffffff;stroke-width:0.013503"
+         x="67.249107"
+         y="18.418552"
+         dx="0">o<tspan
+   style="letter-spacing:0.113808px;stroke-width:0.013503"
+   id="tspan18698">rt</tspan></tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.13016px;line-height:1.25;font-family:Clarendon;-inkscape-font-specification:Clarendon;fill:#ffffff;stroke-width:0.0510351"
+       x="65.38784"
+       y="18.464832"
+       id="text18706"><tspan
+         sodipodi:role="line"
+         id="tspan18704"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.13016px;font-family:Clarendon;-inkscape-font-specification:Clarendon;fill:#ffffff;stroke-width:0.0510351"
+         x="65.38784"
+         y="18.464832">F</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:3.13016px;line-height:1.25;font-family:'Segoe UI';-inkscape-font-specification:'Segoe UI';stroke-width:0.0510351"
+       x="71.557388"
+       y="18.433064"
+       id="text18710"><tspan
+         sodipodi:role="line"
+         id="tspan18708"
+         style="font-weight:bold;font-size:3.13016px;stroke-width:0.0510351"
+         x="71.557388"
+         y="18.433064">LS</tspan></text>
+    <path
+       fill="currentColor"
+       d="m 81.381269,18.057486 h 0.18785 l 0.70644,-0.70643 v -0.18786 l -0.35454,-0.35189 h -0.18521 l -0.48154,0.47889 h -1.32292 v -0.33073 l 0.49212,-0.48948 v -0.18785 l -0.52916,-0.52917 h -0.1905 l -1.058345,1.05834 v 0.18785 l 0.529175,0.52917 h 0.18786 l 0.30162,-0.30427 v 1.53193 l 0.13229,0.1323 h 1.23031 v 0.13758 l 0.3519,0.35454 h 0.18785 l 0.70644,-0.70644 v -0.18785 l -0.35454,-0.3519 h -0.18521 l -0.49212,0.48948 h -1.32292 v -1.05833 h 1.1086 v 0.127 z m 0.44714,-0.96573 0.16669,0.16669 -0.52917,0.52917 -0.16668,-0.16669 z m 0,1.32292 0.16669,0.16669 -0.52917,0.52917 -0.16668,-0.16669 z m -2.56116,-1.16681 -0.34132,-0.34396 0.87048,-0.87048 0.34396,0.34131 z"
+       id="path67898"
+       style="fill:#000000;fill-opacity:1;stroke-width:0.264583" />
+    <text
+       xml:space="preserve"
+       style="font-size:3.175px;line-height:1.25;font-family:Caladea;-inkscape-font-specification:Caladea;stroke-width:0.264583"
+       x="74.395203"
+       y="194.45642"
+       id="text79286"><tspan
+         sodipodi:role="line"
+         id="tspan79284"
+         style="stroke-width:0.264583" /></text>
+    <text
+       xml:space="preserve"
+       style="font-size:3.175px;line-height:1.25;font-family:Caladea;-inkscape-font-specification:Caladea;stroke-width:0.264583"
+       x="69.863312"
+       y="194.28471"
+       id="text116792"><tspan
+         sodipodi:role="line"
+         id="tspan116790"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Clarendon;-inkscape-font-specification:Clarendon;stroke-width:0.264583"
+         x="69.863312"
+         y="194.28471" /></text>
+    <g
+       id="g191971"
+       transform="translate(-23.913695,-149.54291)">
+      <rect
+         style="fill:#094771;fill-opacity:1;stroke:#454545;stroke-width:0.164422;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+         id="rect191955"
+         width="7.8585901"
+         height="2.0093398"
+         x="68.879715"
+         y="193.16545" />
+      <g
+         id="g191967"
+         transform="translate(-0.26984)">
+        <text
+           xml:space="preserve"
+           style="font-size:1.4111px;line-height:1.25;font-family:Caladea;-inkscape-font-specification:Caladea;fill:#18a3ff;fill-opacity:1;stroke-width:0.264582"
+           x="71.120277"
+           y="194.8504"
+           id="text191959"><tspan
+             sodipodi:role="line"
+             id="tspan191957"
+             style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:1.4111px;font-family:Clarendon;-inkscape-font-specification:Clarendon;fill:#18a3ff;fill-opacity:1;stroke-width:0.264582"
+             x="71.120277"
+             y="194.8504"
+             dx="0 0 0 0.079999566">Fort</tspan></text>
+        <text
+           xml:space="preserve"
+           style="font-size:1.47163px;line-height:1.25;font-family:'Segoe UI';-inkscape-font-specification:'Segoe UI';fill:#eef5f9;fill-opacity:1;stroke-width:0.023994"
+           x="74.131889"
+           y="194.85643"
+           id="text191963"><tspan
+             sodipodi:role="line"
+             id="tspan191961"
+             style="font-weight:bold;font-size:1.47163px;fill:#eef5f9;fill-opacity:1;stroke-width:0.023994"
+             x="74.131889"
+             y="194.85643">LS</tspan></text>
+        <path
+           fill="currentColor"
+           d="m 70.495774,194.32778 h 0.07779 l 0.292548,-0.29256 v -0.0779 l -0.146821,-0.14571 h -0.0767 l -0.199414,0.1983 h -0.547844 v -0.13696 l 0.203798,-0.2027 v -0.0779 l -0.219137,-0.21913 h -0.07888 l -0.438274,0.43827 v 0.0779 l 0.219138,0.21914 h 0.07779 l 0.124908,-0.12602 v 0.63441 l 0.05478,0.0547 h 0.509493 v 0.057 l 0.145727,0.14683 h 0.07779 l 0.292548,-0.29254 v -0.0779 l -0.146821,-0.14572 h -0.0767 l -0.203797,0.20271 h -0.547857 v -0.43829 h 0.459091 v 0.0525 z m 0.18517,-0.39994 0.06903,0.0691 -0.219136,0.21914 -0.06903,-0.0691 z m 0,0.54785 0.06903,0.0691 -0.219136,0.21913 -0.06903,-0.0691 z m -1.060622,-0.4832 -0.141342,-0.14244 0.36048,-0.36048 0.142438,0.14135 z"
+           id="path191965"
+           style="fill:#ffffff;fill-opacity:1;stroke-width:0.109568" />
+      </g>
+      <rect
+         style="fill:#252526;fill-opacity:1;stroke:none;stroke-width:0.0797276;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+         id="rect191969"
+         width="7.8490243"
+         height="0.1629246"
+         x="68.879715"
+         y="195.01186"
+         ry="0" />
+    </g>
+    <g
+       id="g224022-1"
+       transform="matrix(0.91352716,0,0,0.91550544,-16.04449,-115.9497)">
+      <rect
+         style="fill:#1e1e1e;fill-opacity:1;stroke:#333333;stroke-width:0.15;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+         id="rect2268-1"
+         width="11.863286"
+         height="7.5460467"
+         x="65.624542"
+         y="179.59247" />
+      <rect
+         style="fill:#333333;fill-opacity:1;stroke:#333333;stroke-width:0.15;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+         id="rect2270-5"
+         width="11.863285"
+         height="0.68629307"
+         x="65.624542"
+         y="178.90616" />
+    </g>
+    <g
+       id="g190145-9"
+       transform="translate(-21.715759,-131.16326)">
+      <circle
+         style="fill:#ffff00;stroke:none;stroke-width:0.0835516;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;paint-order:markers stroke fill"
+         id="circle2009-3-77"
+         cx="66.796471"
+         cy="179.39244"
+         r="0.12944785" />
+      <circle
+         style="fill:#00ff00;fill-opacity:1;stroke:none;stroke-width:0.0835516;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;paint-order:markers stroke fill"
+         id="circle2011-6-6"
+         cx="66.41864"
+         cy="179.39244"
+         r="0.12944785" />
+      <circle
+         style="fill:#ff0000;stroke:none;stroke-width:0.0835516;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;paint-order:markers stroke fill"
+         id="circle2013-7-7"
+         cx="66.040802"
+         cy="179.39244"
+         r="0.12944785" />
+    </g>
+    <g
+       id="g223494-3"
+       transform="translate(-23.691601,-140.89197)">
+      <rect
+         style="fill:#094771;fill-opacity:1;stroke:#454545;stroke-width:0.164422;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+         id="rect27716-6"
+         width="7.8585901"
+         height="2.0093398"
+         x="71.238846"
+         y="192.55693"
+         transform="matrix(0.86670639,0,0,0.93342484,9.4892506,12.902558)" />
+      <rect
+         style="fill:#252526;fill-opacity:1;stroke:none;stroke-width:0.0715535;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+         id="rect190284-5"
+         width="7.8585901"
+         height="0.13107002"
+         x="71.238846"
+         y="194.43518"
+         ry="0"
+         transform="matrix(0.86670639,0,0,0.93342484,9.4892506,12.902558)" />
+      <text
+         xml:space="preserve"
+         style="font-size:1.4111px;line-height:1.25;font-family:Caladea;-inkscape-font-specification:Caladea;fill:#18a3ff;fill-opacity:1;stroke-width:0.264582"
+         x="73.209572"
+         y="194.24188"
+         id="text69430-63"><tspan
+           sodipodi:role="line"
+           id="tspan69428-9"
+           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:1.4111px;font-family:Clarendon;-inkscape-font-specification:Clarendon;fill:#18a3ff;fill-opacity:1;stroke-width:0.264582"
+           x="73.209572"
+           y="194.24188"
+           dx="0 0 0 0.079999566">Fort</tspan></text>
+      <text
+         xml:space="preserve"
+         style="font-size:1.47163px;line-height:1.25;font-family:'Segoe UI';-inkscape-font-specification:'Segoe UI';fill:#eef5f9;fill-opacity:1;stroke-width:0.023994"
+         x="76.221184"
+         y="194.24791"
+         id="text81726-48"><tspan
+           sodipodi:role="line"
+           id="tspan81724-1"
+           style="font-weight:bold;font-size:1.47163px;fill:#eef5f9;fill-opacity:1;stroke-width:0.023994"
+           x="76.221184"
+           y="194.24791">LS</tspan></text>
+      <path
+         fill="currentColor"
+         d="m 72.585066,193.71926 h 0.07779 l 0.292548,-0.29256 v -0.0779 l -0.146821,-0.14571 h -0.0767 l -0.199414,0.1983 h -0.547844 v -0.13696 l 0.203798,-0.2027 v -0.0779 l -0.219137,-0.21913 h -0.07888 l -0.438274,0.43827 v 0.0779 l 0.219138,0.21914 h 0.07779 l 0.124908,-0.12602 v 0.63441 l 0.05478,0.0547 h 0.509493 v 0.057 l 0.145727,0.14683 h 0.07779 l 0.292548,-0.29254 v -0.0779 l -0.146821,-0.14572 h -0.0767 l -0.203797,0.20271 h -0.547857 v -0.43829 h 0.459091 v 0.0525 z m 0.18517,-0.39994 0.06903,0.0691 -0.219136,0.21914 -0.06903,-0.0691 z m 0,0.54785 0.06903,0.0691 -0.219136,0.21913 -0.06903,-0.0691 z m -1.060622,-0.4832 -0.141342,-0.14244 0.36048,-0.36048 0.142438,0.14135 z"
+         id="path2-2"
+         style="fill:#ffffff;fill-opacity:1;stroke-width:0.109568" />
+    </g>
+    <text
+       xml:space="preserve"
+       style="font-size:1.4111px;line-height:1.25;font-family:Caladea;-inkscape-font-specification:Caladea;fill:#d4d4d4;fill-opacity:1;stroke-width:0.264582"
+       x="44.429081"
+       y="51.486362"
+       id="text195534-9"><tspan
+         sodipodi:role="line"
+         id="tspan195532-3"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:1.4111px;font-family:Clarendon;-inkscape-font-specification:Clarendon;fill:#d4d4d4;fill-opacity:1;stroke-width:0.264582"
+         x="44.429081"
+         y="51.486362"
+         dx="0 0 0 0.079999566">Fort</tspan></text>
+    <path
+       id="rect224117-9"
+       style="fill:none;stroke:#282828;stroke-width:0.0833266;stroke-linecap:square;paint-order:markers stroke fill"
+       d="m 54.700265,51.653146 h -10.457 v -1.32557 h 10.457"
+       sodipodi:nodetypes="cccc" />
+    <rect
+       style="fill:#1e1e1e;fill-opacity:1;stroke:#1e1e1e;stroke-width:0.317166;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+       id="rect2268"
+       width="26.136299"
+       height="15.335978"
+       x="23.474056"
+       y="6.883688"
+       ry="0.067764252" />
+    <rect
+       style="fill:#094771;fill-opacity:1;stroke:#454545;stroke-width:0.22;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+       id="rect27716"
+       width="16.411741"
+       height="4.6883922"
+       x="32.180874"
+       y="13.652987" />
+    <rect
+       style="fill:#252526;fill-opacity:1;stroke:none;stroke-width:0.118454;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+       id="rect190284"
+       width="16.411741"
+       height="0.17200001"
+       x="32.180874"
+       y="18.171381"
+       ry="0" />
+    <text
+       xml:space="preserve"
+       style="font-size:3.50005px;line-height:1.25;font-family:Caladea;-inkscape-font-specification:Caladea;fill:#18a3ff;fill-opacity:1;stroke-width:0.656262"
+       x="38.030666"
+       y="17.236485"
+       id="text69430"
+       transform="scale(0.97145175,1.0293872)"><tspan
+         sodipodi:role="line"
+         id="tspan69428"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.50005px;font-family:Clarendon;-inkscape-font-specification:Clarendon;fill:#18a3ff;fill-opacity:1;stroke-width:0.656262"
+         x="38.030666"
+         y="17.236485"
+         dx="0 0 0 0.19842868">Fort</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:3.65019px;line-height:1.25;font-family:'Segoe UI';-inkscape-font-specification:'Segoe UI';fill:#eef5f9;fill-opacity:1;stroke-width:0.059514"
+       x="45.500587"
+       y="17.251438"
+       id="text81726"
+       transform="scale(0.97145175,1.0293872)"><tspan
+         sodipodi:role="line"
+         id="tspan81724"
+         style="font-weight:bold;font-size:3.65019px;fill:#eef5f9;fill-opacity:1;stroke-width:0.059514"
+         x="45.500587"
+         y="17.251438">LS</tspan></text>
+    <path
+       fill="currentColor"
+       d="m 35.440173,16.408638 h 0.187439 l 0.704913,-0.74698 v -0.1989 l -0.353775,-0.37204 h -0.184813 l -0.4805,0.50631 h -1.320064 v -0.34969 l 0.491064,-0.51755 v -0.1989 l -0.528024,-0.55949 h -0.190066 l -1.056049,1.11901 v 0.1989 l 0.528027,0.55953 h 0.18744 l 0.300973,-0.32177 v 1.61982 l 0.131996,0.13966 h 1.227655 v 0.14554 l 0.351138,0.37489 h 0.18744 l 0.704912,-0.74693 v -0.1989 l -0.353774,-0.37206 h -0.184814 l -0.491061,0.51757 h -1.320095 v -1.11907 h 1.106208 v 0.13405 z m 0.446178,-1.02115 0.166332,0.17643 -0.528022,0.55952 -0.166332,-0.17643 z m 0,1.3988 0.166332,0.17643 -0.528022,0.5595 -0.166332,-0.17643 z m -2.555634,-1.23374 -0.340572,-0.36368 0.868599,-0.9204 0.343213,0.3609 z"
+       id="path2"
+       style="fill:#ee9d28;fill-opacity:1;stroke-width:0.271769" />
+    <text
+       xml:space="preserve"
+       style="font-size:3.50005px;line-height:1.25;font-family:Caladea;-inkscape-font-specification:Caladea;fill:#d4d4d4;fill-opacity:1;stroke-width:0.656262"
+       x="25.474777"
+       y="12.642262"
+       id="text195534"
+       transform="scale(0.97145175,1.0293872)"><tspan
+         sodipodi:role="line"
+         id="tspan195532"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.50005px;font-family:Clarendon;-inkscape-font-specification:Clarendon;fill:#d4d4d4;fill-opacity:1;stroke-width:0.656262"
+         x="25.474777"
+         y="12.642262"
+         dx="0 0 0 0.19842868">Fort</tspan></text>
+    <path
+       id="rect224117"
+       style="fill:none;stroke:#282828;stroke-width:0.207346;stroke-linecap:square;paint-order:markers stroke fill"
+       d="M 49.663815,13.439337 H 24.300096 v -3.383912 h 25.363719"
+       sodipodi:nodetypes="cccc" />
+    <path
+       style="fill:#aeafad;fill-opacity:1;stroke:#aeafad;stroke-width:0.20122;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="M 32.171482,13.547876 V 9.9491763"
+       id="cursor" />
+    <g
+       id="g21395"
+       inkscape:export-filename="/home/gn/Code/Python/fortls/assets/logo.png"
+       inkscape:export-xdpi="300.22787"
+       inkscape:export-ydpi="300.22787"
+       transform="translate(111.32803,-127.94961)">
+      <rect
+         style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.0914084;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         id="rect18694-3"
+         width="23.999231"
+         height="15.736053"
+         x="37.717922"
+         y="130.50795"
+         ry="2.9265988"
+         inkscape:export-filename="/home/gn/Code/TypeScript/vscode-fortran-support/assets/modern-fortran-logo.png"
+         inkscape:export-xdpi="150.11394"
+         inkscape:export-ydpi="150.11394" />
+      <rect
+         style="fill:#734f96;fill-opacity:1;stroke:none;stroke-width:0.107605;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         id="rect18696-6"
+         width="33.257595"
+         height="15.736053"
+         x="11.43499"
+         y="130.50795"
+         ry="2.9265988"
+         inkscape:export-filename="/home/gn/Code/TypeScript/vscode-fortran-support/assets/modern-fortran-logo.png"
+         inkscape:export-xdpi="150.11394"
+         inkscape:export-ydpi="150.11394" />
+      <text
+         xml:space="preserve"
+         style="font-size:14.1111px;line-height:1.25;font-family:Caladea;-inkscape-font-specification:Caladea;letter-spacing:0.590021px;word-spacing:0px;fill:#ffffff;stroke-width:0.0700042"
+         x="22.070086"
+         y="143.9781"
+         id="text18702-7"><tspan
+           sodipodi:role="line"
+           id="tspan18700-5"
+           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.1111px;font-family:Clarendon;-inkscape-font-specification:Clarendon;letter-spacing:0.590021px;fill:#ffffff;stroke-width:0.0700042"
+           x="22.070086"
+           y="143.9781"
+           dx="0">o<tspan
+   style="letter-spacing:0.590021px"
+   id="tspan18698-3">rt</tspan></tspan></text>
+      <text
+         xml:space="preserve"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16.2278px;line-height:1.25;font-family:Clarendon;-inkscape-font-specification:Clarendon;fill:#ffffff;stroke-width:0.264583"
+         x="12.420631"
+         y="144.21799"
+         id="text18706-5"><tspan
+           sodipodi:role="line"
+           id="tspan18704-6"
+           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16.2278px;font-family:Clarendon;-inkscape-font-specification:Clarendon;fill:#ffffff;stroke-width:0.264583"
+           x="12.420631"
+           y="144.21799">F</tspan></text>
+      <text
+         xml:space="preserve"
+         style="font-size:16.2278px;line-height:1.25;font-family:'Segoe UI';-inkscape-font-specification:'Segoe UI';stroke-width:0.264583"
+         x="44.405636"
+         y="144.05333"
+         id="text18710-2"><tspan
+           sodipodi:role="line"
+           id="tspan18708-9"
+           style="font-weight:bold;font-size:16.2278px;stroke-width:0.264583"
+           x="44.405636"
+           y="144.05333">LS</tspan></text>
+    </g>
+    <g
+       id="g947"
+       transform="matrix(0.91352716,0,0,0.91550544,-15.982658,-107.10827)">
+      <rect
+         style="fill:#1e1e1e;fill-opacity:1;stroke:#333333;stroke-width:0.15;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+         id="rect943"
+         width="11.863286"
+         height="7.5460467"
+         x="65.624542"
+         y="179.59247" />
+      <rect
+         style="fill:#333333;fill-opacity:1;stroke:#333333;stroke-width:0.15;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+         id="rect945"
+         width="11.863285"
+         height="0.68629307"
+         x="65.624542"
+         y="178.90616" />
+    </g>
+    <g
+       id="g955"
+       transform="translate(-21.653926,-122.32183)">
+      <circle
+         style="fill:#ffff00;stroke:none;stroke-width:0.0835516;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;paint-order:markers stroke fill"
+         id="circle949"
+         cx="66.796471"
+         cy="179.39244"
+         r="0.12944785" />
+      <circle
+         style="fill:#00ff00;fill-opacity:1;stroke:none;stroke-width:0.0835516;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;paint-order:markers stroke fill"
+         id="circle951"
+         cx="66.41864"
+         cy="179.39244"
+         r="0.12944785" />
+      <circle
+         style="fill:#ff0000;stroke:none;stroke-width:0.0835516;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;paint-order:markers stroke fill"
+         id="circle953"
+         cx="66.040802"
+         cy="179.39244"
+         r="0.12944785" />
+    </g>
+    <g
+       id="g971"
+       transform="translate(-23.629768,-132.05054)">
+      <rect
+         style="fill:#094771;fill-opacity:1;stroke:#454545;stroke-width:0.164422;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+         id="rect957"
+         width="7.8585901"
+         height="2.0093398"
+         x="71.238846"
+         y="192.55693"
+         transform="matrix(0.86670639,0,0,0.93342484,9.4892506,12.902558)" />
+      <rect
+         style="fill:#252526;fill-opacity:1;stroke:none;stroke-width:0.0715535;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+         id="rect959"
+         width="7.8585901"
+         height="0.13107002"
+         x="71.238846"
+         y="194.43518"
+         ry="0"
+         transform="matrix(0.86670639,0,0,0.93342484,9.4892506,12.902558)" />
+      <text
+         xml:space="preserve"
+         style="font-size:1.4111px;line-height:1.25;font-family:Caladea;-inkscape-font-specification:Caladea;fill:#18a3ff;fill-opacity:1;stroke-width:0.264582"
+         x="73.209572"
+         y="194.24188"
+         id="text963"><tspan
+           sodipodi:role="line"
+           id="tspan961"
+           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:1.4111px;font-family:Clarendon;-inkscape-font-specification:Clarendon;fill:#18a3ff;fill-opacity:1;stroke-width:0.264582"
+           x="73.209572"
+           y="194.24188"
+           dx="0 0 0 0.079999566">Fort</tspan></text>
+      <text
+         xml:space="preserve"
+         style="font-size:1.47163px;line-height:1.25;font-family:'Segoe UI';-inkscape-font-specification:'Segoe UI';fill:#eef5f9;fill-opacity:1;stroke-width:0.023994"
+         x="76.221184"
+         y="194.24791"
+         id="text967"><tspan
+           sodipodi:role="line"
+           id="tspan965"
+           style="font-weight:bold;font-size:1.47163px;fill:#eef5f9;fill-opacity:1;stroke-width:0.023994"
+           x="76.221184"
+           y="194.24791">LS</tspan></text>
+      <path
+         fill="currentColor"
+         d="m 72.585066,193.71926 h 0.07779 l 0.292548,-0.29256 v -0.0779 l -0.146821,-0.14571 h -0.0767 l -0.199414,0.1983 h -0.547844 v -0.13696 l 0.203798,-0.2027 v -0.0779 l -0.219137,-0.21913 h -0.07888 l -0.438274,0.43827 v 0.0779 l 0.219138,0.21914 h 0.07779 l 0.124908,-0.12602 v 0.63441 l 0.05478,0.0547 h 0.509493 v 0.057 l 0.145727,0.14683 h 0.07779 l 0.292548,-0.29254 v -0.0779 l -0.146821,-0.14572 h -0.0767 l -0.203797,0.20271 h -0.547857 v -0.43829 h 0.459091 v 0.0525 z m 0.18517,-0.39994 0.06903,0.0691 -0.219136,0.21914 -0.06903,-0.0691 z m 0,0.54785 0.06903,0.0691 -0.219136,0.21913 -0.06903,-0.0691 z m -1.060622,-0.4832 -0.141342,-0.14244 0.36048,-0.36048 0.142438,0.14135 z"
+         id="path969"
+         style="fill:#ffffff;fill-opacity:1;stroke-width:0.109568" />
+    </g>
+    <text
+       xml:space="preserve"
+       style="font-size:1.4111px;line-height:1.25;font-family:Caladea;-inkscape-font-specification:Caladea;fill:#d4d4d4;fill-opacity:1;stroke-width:0.264582"
+       x="44.490917"
+       y="60.327793"
+       id="text975"><tspan
+         sodipodi:role="line"
+         id="tspan973"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:1.4111px;font-family:Clarendon;-inkscape-font-specification:Clarendon;fill:#d4d4d4;fill-opacity:1;stroke-width:0.264582"
+         x="44.490917"
+         y="60.327793"
+         dx="0 0 0 0.079999566">Fort</tspan></text>
+    <path
+       id="path977"
+       style="fill:none;stroke:#282828;stroke-width:0.0833266;stroke-linecap:square;paint-order:markers stroke fill"
+       d="m 54.762096,60.494576 h -10.457 v -1.32557 h 10.457"
+       sodipodi:nodetypes="cccc" />
+    <path
+       style="fill:#aeafad;fill-opacity:1;stroke:#aeafad;stroke-width:0.0811248;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="m 47.571954,60.536966 v -1.40945"
+       id="path979" />
+    <rect
+       style="fill:#1e1e1e;fill-opacity:1;stroke:#333333;stroke-width:0.340251;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+       id="rect2268-36"
+       width="26.113464"
+       height="17.639082"
+       x="36.730507"
+       y="67.191696" />
+    <rect
+       style="fill:#333333;fill-opacity:1;stroke:#333333;stroke-width:0.340251;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+       id="rect2270-7"
+       width="26.113462"
+       height="1.6042281"
+       x="36.730507"
+       y="65.587433" />
+    <ellipse
+       style="fill:#52c22b;fill-opacity:1;stroke:none;stroke-width:0.207239;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;paint-order:markers stroke fill"
+       id="circle2009-3-5"
+       cx="39.562702"
+       cy="66.581467"
+       rx="0.31191254"
+       ry="0.3305144" />
+    <ellipse
+       style="fill:#e6bf28;fill-opacity:1;stroke:none;stroke-width:0.207239;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;paint-order:markers stroke fill"
+       id="circle2011-6-3"
+       cx="38.652298"
+       cy="66.581467"
+       rx="0.31191254"
+       ry="0.3305144" />
+    <ellipse
+       style="fill:#fe5a51;fill-opacity:1;stroke:none;stroke-width:0.207239;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;paint-order:markers stroke fill"
+       id="circle2013-7-5"
+       cx="37.741875"
+       cy="66.581467"
+       rx="0.31191254"
+       ry="0.3305144" />
+    <rect
+       style="fill:#094771;fill-opacity:1;stroke:#454545;stroke-width:0.36682;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+       id="rect27716-62"
+       width="16.411741"
+       height="4.7888179"
+       x="45.490467"
+       y="75.565987" />
+    <rect
+       style="fill:#252526;fill-opacity:1;stroke:none;stroke-width:0.159633;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+       id="rect190284-9"
+       width="16.411741"
+       height="0.31237644"
+       x="45.490467"
+       y="80.042374"
+       ry="0" />
+    <text
+       xml:space="preserve"
+       style="font-size:3.50005px;line-height:1.25;font-family:Caladea;-inkscape-font-specification:Caladea;fill:#18a3ff;fill-opacity:1;stroke-width:0.656262"
+       x="51.731392"
+       y="77.381996"
+       id="text69430-12"
+       transform="scale(0.97145175,1.0293872)"><tspan
+         sodipodi:role="line"
+         id="tspan69428-7"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.50005px;font-family:Clarendon;-inkscape-font-specification:Clarendon;fill:#18a3ff;fill-opacity:1;stroke-width:0.656262"
+         x="51.731392"
+         y="77.381996"
+         dx="0 0 0 0.19842868">Fort</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:3.65019px;line-height:1.25;font-family:'Segoe UI';-inkscape-font-specification:'Segoe UI';fill:#eef5f9;fill-opacity:1;stroke-width:0.059514"
+       x="59.201309"
+       y="77.396957"
+       id="text81726-0"
+       transform="scale(0.97145175,1.0293872)"><tspan
+         sodipodi:role="line"
+         id="tspan81724-93"
+         style="font-weight:bold;font-size:3.65019px;fill:#eef5f9;fill-opacity:1;stroke-width:0.059514"
+         x="59.201309"
+         y="77.396957">LS</tspan></text>
+    <path
+       fill="currentColor"
+       d="m 48.749768,78.321657 h 0.187439 l 0.704913,-0.74698 v -0.1989 l -0.353775,-0.37204 h -0.184813 l -0.4805,0.50632 h -1.320064 v -0.3497 l 0.491064,-0.51755 v -0.1989 l -0.528024,-0.55949 h -0.190066 l -1.056049,1.11901 v 0.1989 l 0.528027,0.55953 h 0.18744 l 0.300973,-0.32177 v 1.61982 l 0.131996,0.13966 h 1.227655 v 0.14554 l 0.351138,0.37489 h 0.18744 l 0.704912,-0.74693 v -0.1989 L 49.2857,78.402107 h -0.184814 l -0.491061,0.51757 H 47.28973 v -1.11907 h 1.106208 v 0.13405 z m 0.446178,-1.02115 0.166332,0.17643 -0.528022,0.55952 -0.166332,-0.17643 z m 0,1.3988 0.166332,0.17643 -0.528022,0.5595 -0.166332,-0.17642 z m -2.555634,-1.23374 -0.340572,-0.36368 0.868599,-0.9204 0.343213,0.3609 z"
+       id="path2-6"
+       style="fill:#ffffff;fill-opacity:1;stroke-width:0.271769" />
+    <text
+       xml:space="preserve"
+       style="font-size:3.50005px;line-height:1.25;font-family:Caladea;-inkscape-font-specification:Caladea;fill:#d4d4d4;fill-opacity:1;stroke-width:0.656262"
+       x="39.109043"
+       y="72.759682"
+       id="text195534-06"
+       transform="scale(0.97145175,1.0293872)"><tspan
+         sodipodi:role="line"
+         id="tspan195532-2"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.50005px;font-family:Clarendon;-inkscape-font-specification:Clarendon;fill:#d4d4d4;fill-opacity:1;stroke-width:0.656262"
+         x="39.109043"
+         y="72.759682"
+         dx="0 0 0 0.19842868">Fort</tspan></text>
+    <path
+       id="rect224117-6"
+       style="fill:none;stroke:#282828;stroke-width:0.206681;stroke-linecap:square;paint-order:markers stroke fill"
+       d="M 62.741604,75.323747 H 37.544819 v -3.38453 h 25.196785"
+       sodipodi:nodetypes="cccc" />
+    <path
+       style="fill:#aeafad;fill-opacity:1;stroke:#aeafad;stroke-width:0.20122;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="m 45.416515,75.431977 v -3.5987"
+       id="cursor-1" />
+    <rect
+       style="fill:#1e1e1e;fill-opacity:1;stroke:#333333;stroke-width:0.34;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+       id="rect2268-8"
+       width="26.113464"
+       height="17.639082"
+       x="36.615356"
+       y="88.123032" />
+    <rect
+       style="fill:#333333;fill-opacity:1;stroke:#333333;stroke-width:0.339742;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+       id="rect2270-79"
+       width="26.113001"
+       height="0.90277708"
+       x="36.615227"
+       y="87.243858"
+       ry="0.066498294" />
+    <g
+       id="g16561-2"
+       transform="translate(-14.159058,-80.39182)">
+      <ellipse
+         style="fill:#52c22b;fill-opacity:1;stroke:none;stroke-width:0.207239;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;paint-order:markers stroke fill"
+         id="circle2009-3-0"
+         cx="53.077602"
+         cy="168.08707"
+         rx="0.31191254"
+         ry="0.3305144" />
+      <ellipse
+         style="fill:#e6bf28;fill-opacity:1;stroke:none;stroke-width:0.207239;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;paint-order:markers stroke fill"
+         id="circle2011-6-23"
+         cx="52.167198"
+         cy="168.08707"
+         rx="0.31191254"
+         ry="0.3305144" />
+      <ellipse
+         style="fill:#fe5a51;fill-opacity:1;stroke:none;stroke-width:0.207239;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;paint-order:markers stroke fill"
+         id="circle2013-7-75"
+         cx="51.256775"
+         cy="168.08707"
+         rx="0.31191254"
+         ry="0.3305144" />
+    </g>
+    <rect
+       style="fill:#094771;fill-opacity:1;stroke:#454545;stroke-width:0.36682;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+       id="rect27716-92"
+       width="16.411741"
+       height="4.7888179"
+       x="45.375324"
+       y="96.497292" />
+    <rect
+       style="fill:#252526;fill-opacity:1;stroke:none;stroke-width:0.159633;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+       id="rect190284-28"
+       width="16.411741"
+       height="0.31237644"
+       x="45.375324"
+       y="100.97371"
+       ry="0" />
+    <text
+       xml:space="preserve"
+       style="font-size:3.50005px;line-height:1.25;font-family:Caladea;-inkscape-font-specification:Caladea;fill:#18a3ff;fill-opacity:1;stroke-width:0.656262"
+       x="51.612862"
+       y="97.715782"
+       id="text69430-9"
+       transform="scale(0.97145175,1.0293872)"><tspan
+         sodipodi:role="line"
+         id="tspan69428-73"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.50005px;font-family:Clarendon;-inkscape-font-specification:Clarendon;fill:#18a3ff;fill-opacity:1;stroke-width:0.656262"
+         x="51.612862"
+         y="97.715782"
+         dx="0 0 0 0.19842868">Fort</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:3.65019px;line-height:1.25;font-family:'Segoe UI';-inkscape-font-specification:'Segoe UI';fill:#eef5f9;fill-opacity:1;stroke-width:0.059514"
+       x="59.082779"
+       y="97.730728"
+       id="text81726-6"
+       transform="scale(0.97145175,1.0293872)"><tspan
+         sodipodi:role="line"
+         id="tspan81724-12"
+         style="font-weight:bold;font-size:3.65019px;fill:#eef5f9;fill-opacity:1;stroke-width:0.059514"
+         x="59.082779"
+         y="97.730728">LS</tspan></text>
+    <path
+       fill="currentColor"
+       d="m 48.63462,99.252998 h 0.187439 l 0.704913,-0.74698 v -0.1989 l -0.353775,-0.37204 h -0.184813 l -0.4805,0.50631 H 47.18782 v -0.34969 l 0.491064,-0.51755 v -0.1989 l -0.528024,-0.55949 h -0.190066 l -1.056049,1.11901 v 0.1989 l 0.528027,0.55953 h 0.18744 l 0.300973,-0.32177 v 1.61982 l 0.131996,0.139662 h 1.227655 v 0.14554 l 0.351138,0.37489 h 0.18744 l 0.704912,-0.746932 v -0.1989 l -0.353774,-0.37206 h -0.184814 l -0.491061,0.51757 h -1.320095 v -1.11907 h 1.106208 v 0.13405 z m 0.446178,-1.02115 0.166332,0.17643 -0.528022,0.55952 -0.166332,-0.17643 z m 0,1.3988 0.166332,0.17643 -0.528022,0.559502 -0.166332,-0.17643 z m -2.555634,-1.23374 -0.340572,-0.36368 0.868599,-0.9204 0.343213,0.3609 z"
+       id="path2-93"
+       style="fill:#ffffff;fill-opacity:1;stroke-width:0.271769" />
+    <text
+       xml:space="preserve"
+       style="font-size:3.50005px;line-height:1.25;font-family:Caladea;-inkscape-font-specification:Caladea;fill:#d4d4d4;fill-opacity:1;stroke-width:0.656262"
+       x="38.990505"
+       y="93.09346"
+       id="text195534-1"
+       transform="scale(0.97145175,1.0293872)"><tspan
+         sodipodi:role="line"
+         id="tspan195532-9"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.50005px;font-family:Clarendon;-inkscape-font-specification:Clarendon;fill:#d4d4d4;fill-opacity:1;stroke-width:0.656262"
+         x="38.990505"
+         y="93.09346"
+         dx="0 0 0 0.19842868">Fort</tspan></text>
+    <path
+       id="rect224117-4"
+       style="fill:none;stroke:#282828;stroke-width:0.206681;stroke-linecap:square;paint-order:markers stroke fill"
+       d="M 62.626456,96.255089 H 37.429671 v -3.38453 h 25.196785"
+       sodipodi:nodetypes="cccc" />
+    <path
+       style="fill:#aeafad;fill-opacity:1;stroke:#aeafad;stroke-width:0.20122;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="m 45.301367,96.363319 v -3.5987"
+       id="cursor-7" />
+    <rect
+       style="fill:#1e1e1e;fill-opacity:1;stroke:#333333;stroke-width:0.317166;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+       id="rect2268-84"
+       width="26.136299"
+       height="15.335978"
+       x="36.730331"
+       y="108.10555"
+       ry="0.067764252" />
+    <rect
+       style="fill:#333333;fill-opacity:1;stroke:#333333;stroke-width:0.339742;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+       id="rect2270-50"
+       width="26.113001"
+       height="0.90277708"
+       x="36.741619"
+       y="107.24953"
+       ry="0.066498294" />
+    <g
+       id="g16561-3"
+       transform="translate(-14.032666,-60.397885)">
+      <ellipse
+         style="fill:#52c22b;fill-opacity:1;stroke:none;stroke-width:0.207239;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;paint-order:markers stroke fill"
+         id="circle2009-3-6"
+         cx="53.077602"
+         cy="168.08707"
+         rx="0.31191254"
+         ry="0.3305144" />
+      <ellipse
+         style="fill:#e6bf28;fill-opacity:1;stroke:none;stroke-width:0.207239;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;paint-order:markers stroke fill"
+         id="circle2011-6-1"
+         cx="52.167198"
+         cy="168.08707"
+         rx="0.31191254"
+         ry="0.3305144" />
+      <ellipse
+         style="fill:#fe5a51;fill-opacity:1;stroke:none;stroke-width:0.207239;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;paint-order:markers stroke fill"
+         id="circle2013-7-06"
+         cx="51.256775"
+         cy="168.08707"
+         rx="0.31191254"
+         ry="0.3305144" />
+    </g>
+    <rect
+       style="fill:#094771;fill-opacity:1;stroke:#454545;stroke-width:0.36682;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+       id="rect27716-3"
+       width="16.411741"
+       height="4.7888179"
+       x="45.501705"
+       y="114.90376" />
+    <rect
+       style="fill:#252526;fill-opacity:1;stroke:none;stroke-width:0.159633;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+       id="rect190284-20"
+       width="16.411741"
+       height="0.31237644"
+       x="45.501705"
+       y="119.38013"
+       ry="0" />
+    <text
+       xml:space="preserve"
+       style="font-size:3.50005px;line-height:1.25;font-family:Caladea;-inkscape-font-specification:Caladea;fill:#18a3ff;fill-opacity:1;stroke-width:0.656262"
+       x="51.742966"
+       y="115.59673"
+       id="text69430-61"
+       transform="scale(0.97145175,1.0293872)"><tspan
+         sodipodi:role="line"
+         id="tspan69428-55"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.50005px;font-family:Clarendon;-inkscape-font-specification:Clarendon;fill:#18a3ff;fill-opacity:1;stroke-width:0.656262"
+         x="51.742966"
+         y="115.59673"
+         dx="0 0 0 0.19842868">Fort</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:3.65019px;line-height:1.25;font-family:'Segoe UI';-inkscape-font-specification:'Segoe UI';fill:#eef5f9;fill-opacity:1;stroke-width:0.059514"
+       x="59.212883"
+       y="115.61169"
+       id="text81726-47"
+       transform="scale(0.97145175,1.0293872)"><tspan
+         sodipodi:role="line"
+         id="tspan81724-6"
+         style="font-weight:bold;font-size:3.65019px;fill:#eef5f9;fill-opacity:1;stroke-width:0.059514"
+         x="59.212883"
+         y="115.61169">LS</tspan></text>
+    <path
+       fill="currentColor"
+       d="m 48.761012,117.65942 h 0.187439 l 0.704913,-0.74698 v -0.1989 l -0.353775,-0.37204 h -0.184813 l -0.4805,0.50631 h -1.320064 v -0.34969 l 0.491064,-0.51755 v -0.1989 l -0.528024,-0.55949 h -0.190066 l -1.056049,1.11901 v 0.1989 l 0.528027,0.55953 h 0.18744 l 0.300973,-0.32177 v 1.61982 l 0.131996,0.13966 h 1.227655 v 0.14554 l 0.351138,0.37489 h 0.18744 l 0.704912,-0.74693 v -0.1989 l -0.353774,-0.37206 H 49.11213 l -0.491061,0.51757 h -1.320095 v -1.11907 h 1.106208 v 0.13405 z m 0.446178,-1.02115 0.166332,0.17643 -0.528022,0.55952 -0.166332,-0.17643 z m 0,1.3988 0.166332,0.17643 -0.528022,0.5595 -0.166332,-0.17643 z m -2.555634,-1.23374 -0.340572,-0.36368 0.868599,-0.9204 0.343213,0.3609 z"
+       id="path2-56"
+       style="fill:#ffffff;fill-opacity:1;stroke-width:0.271769" />
+    <text
+       xml:space="preserve"
+       style="font-size:3.50005px;line-height:1.25;font-family:Caladea;-inkscape-font-specification:Caladea;fill:#d4d4d4;fill-opacity:1;stroke-width:0.656262"
+       x="39.120617"
+       y="110.97442"
+       id="text195534-93"
+       transform="scale(0.97145175,1.0293872)"><tspan
+         sodipodi:role="line"
+         id="tspan195532-7"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.50005px;font-family:Clarendon;-inkscape-font-specification:Clarendon;fill:#d4d4d4;fill-opacity:1;stroke-width:0.656262"
+         x="39.120617"
+         y="110.97442"
+         dx="0 0 0 0.19842868">Fort</tspan></text>
+    <path
+       id="rect224117-45"
+       style="fill:none;stroke:#282828;stroke-width:0.206723;stroke-linecap:square;paint-order:markers stroke fill"
+       d="M 62.763361,114.66149 H 37.556084 V 111.277 h 25.207277"
+       sodipodi:nodetypes="cccc" />
+    <path
+       style="fill:#aeafad;fill-opacity:1;stroke:#aeafad;stroke-width:0.20122;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="m 45.427759,114.76974 v -3.5987"
+       id="cursor-2" />
+    <g
+       id="g22415"
+       transform="translate(0.49181969,1.6713022)">
+      <rect
+         style="fill:#333333;fill-opacity:1;stroke:#333333;stroke-width:0.177537;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+         id="rect2270"
+         width="26.275463"
+         height="0.2450036"
+         x="22.912422"
+         y="5.0824447"
+         ry="0" />
+      <rect
+         style="fill:#333333;fill-opacity:1;stroke:#333333;stroke-width:0.339742;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+         id="rect22125"
+         width="26.113001"
+         height="0.90277708"
+         x="22.993525"
+         y="4.3435688"
+         ry="0.066498294" />
+    </g>
+    <g
+       id="g16561"
+       transform="translate(-27.288943,-161.62081)">
+      <ellipse
+         style="fill:#52c22b;fill-opacity:1;stroke:none;stroke-width:0.207239;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;paint-order:markers stroke fill"
+         id="circle2009-3"
+         cx="53.077602"
+         cy="168.08707"
+         rx="0.31191254"
+         ry="0.3305144" />
+      <ellipse
+         style="fill:#e6bf28;fill-opacity:1;stroke:none;stroke-width:0.207239;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;paint-order:markers stroke fill"
+         id="circle2011-6"
+         cx="52.167198"
+         cy="168.08707"
+         rx="0.31191254"
+         ry="0.3305144" />
+      <ellipse
+         style="fill:#fe5a51;fill-opacity:1;stroke:none;stroke-width:0.207239;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;paint-order:markers stroke fill"
+         id="circle2013-7"
+         cx="51.256775"
+         cy="168.08707"
+         rx="0.31191254"
+         ry="0.3305144" />
+    </g>
+    <rect
+       style="fill:#1e1e1e;fill-opacity:1;stroke:#333333;stroke-width:0.317166;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+       id="rect2268-5"
+       width="26.136299"
+       height="15.335978"
+       x="37.484535"
+       y="128.22388"
+       ry="0.067764252" />
+    <rect
+       style="fill:#094771;fill-opacity:1;stroke:#454545;stroke-width:0.36682;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+       id="rect27716-4"
+       width="16.411741"
+       height="4.7888179"
+       x="46.255913"
+       y="135.02209" />
+    <rect
+       style="fill:#252526;fill-opacity:1;stroke:none;stroke-width:0.159633;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+       id="rect190284-7"
+       width="16.411741"
+       height="0.31237644"
+       x="46.255913"
+       y="139.49849"
+       ry="0" />
+    <text
+       xml:space="preserve"
+       style="font-size:3.50005px;line-height:1.25;font-family:Caladea;-inkscape-font-specification:Caladea;fill:#18a3ff;fill-opacity:1;stroke-width:0.656262"
+       x="52.519333"
+       y="135.1407"
+       id="text69430-4"
+       transform="scale(0.97145175,1.0293872)"><tspan
+         sodipodi:role="line"
+         id="tspan69428-4"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.50005px;font-family:Clarendon;-inkscape-font-specification:Clarendon;fill:#18a3ff;fill-opacity:1;stroke-width:0.656262"
+         x="52.519333"
+         y="135.1407"
+         dx="0 0 0 0.19842868">Fort</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:3.65019px;line-height:1.25;font-family:'Segoe UI';-inkscape-font-specification:'Segoe UI';fill:#eef5f9;fill-opacity:1;stroke-width:0.059514"
+       x="59.989254"
+       y="135.15565"
+       id="text81726-3"
+       transform="scale(0.97145175,1.0293872)"><tspan
+         sodipodi:role="line"
+         id="tspan81724-0"
+         style="font-weight:bold;font-size:3.65019px;fill:#eef5f9;fill-opacity:1;stroke-width:0.059514"
+         x="59.989254"
+         y="135.15565">LS</tspan></text>
+    <path
+       fill="currentColor"
+       d="m 49.515216,137.77774 h 0.187439 l 0.704913,-0.74698 v -0.1989 l -0.353775,-0.37204 H 49.86898 l -0.4805,0.50631 h -1.320064 v -0.34969 l 0.491064,-0.51755 v -0.1989 L 48.031456,135.3405 H 47.84139 l -1.056049,1.11901 v 0.1989 l 0.528027,0.55953 h 0.18744 l 0.300973,-0.32177 v 1.61982 l 0.131996,0.13966 h 1.227655 v 0.14554 l 0.351138,0.37489 h 0.18744 l 0.704912,-0.74693 v -0.1989 l -0.353774,-0.37206 h -0.184814 l -0.491061,0.51757 h -1.320095 v -1.11907 h 1.106208 v 0.13405 z m 0.446178,-1.02115 0.166332,0.17643 -0.528022,0.55952 -0.166332,-0.17643 z m 0,1.3988 0.166332,0.17643 -0.528022,0.5595 -0.166332,-0.17643 z m -2.555634,-1.23374 -0.340572,-0.36368 0.868599,-0.9204 0.343213,0.3609 z"
+       id="path2-7"
+       style="fill:#ffffff;fill-opacity:1;stroke-width:0.271769" />
+    <text
+       xml:space="preserve"
+       style="font-size:3.50005px;line-height:1.25;font-family:Caladea;-inkscape-font-specification:Caladea;fill:#d4d4d4;fill-opacity:1;stroke-width:0.656262"
+       x="39.896984"
+       y="130.51839"
+       id="text195534-8"
+       transform="scale(0.97145175,1.0293872)"><tspan
+         sodipodi:role="line"
+         id="tspan195532-6"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.50005px;font-family:Clarendon;-inkscape-font-specification:Clarendon;fill:#d4d4d4;fill-opacity:1;stroke-width:0.656262"
+         x="39.896984"
+         y="130.51839"
+         dx="0 0 0 0.19842868">Fort</tspan></text>
+    <path
+       id="rect224117-8"
+       style="fill:none;stroke:#282828;stroke-width:0.206723;stroke-linecap:square;paint-order:markers stroke fill"
+       d="M 63.517565,134.77981 H 38.310288 v -3.38449 h 25.207277"
+       sodipodi:nodetypes="cccc" />
+    <path
+       style="fill:#aeafad;fill-opacity:1;stroke:#aeafad;stroke-width:0.20122;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="m 46.181963,134.88806 v -3.5987"
+       id="cursor-8" />
+    <g
+       id="g22415-4"
+       transform="translate(14.502301,123.01148)">
+      <rect
+         style="fill:#333333;fill-opacity:1;stroke:#333333;stroke-width:0.177537;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+         id="rect2270-3"
+         width="26.275463"
+         height="0.2450036"
+         x="22.912422"
+         y="5.0824447"
+         ry="0" />
+      <rect
+         style="fill:#333333;fill-opacity:1;stroke:#333333;stroke-width:0.339742;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+         id="rect22125-1"
+         width="26.113001"
+         height="0.90277708"
+         x="22.993525"
+         y="4.3435688"
+         ry="0.066498294" />
+    </g>
+    <g
+       id="g16561-4"
+       transform="translate(-13.278462,-40.280628)">
+      <ellipse
+         style="fill:#52c22b;fill-opacity:1;stroke:none;stroke-width:0.207239;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;paint-order:markers stroke fill"
+         id="circle2009-3-92"
+         cx="53.077602"
+         cy="168.08707"
+         rx="0.31191254"
+         ry="0.3305144" />
+      <ellipse
+         style="fill:#e6bf28;fill-opacity:1;stroke:none;stroke-width:0.207239;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;paint-order:markers stroke fill"
+         id="circle2011-6-0"
+         cx="52.167198"
+         cy="168.08707"
+         rx="0.31191254"
+         ry="0.3305144" />
+      <ellipse
+         style="fill:#fe5a51;fill-opacity:1;stroke:none;stroke-width:0.207239;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;paint-order:markers stroke fill"
+         id="circle2013-7-68"
+         cx="51.256775"
+         cy="168.08707"
+         rx="0.31191254"
+         ry="0.3305144" />
+    </g>
+    <rect
+       style="fill:#1e1e1e;fill-opacity:1;stroke:#1e1e1e;stroke-width:0.317166;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+       id="rect2268-9"
+       width="26.136299"
+       height="15.335978"
+       x="38.039223"
+       y="148.53128"
+       ry="0.067764252" />
+    <rect
+       style="fill:#094771;fill-opacity:1;stroke:#454545;stroke-width:0.22;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+       id="rect27716-2"
+       width="16.411741"
+       height="4.6883922"
+       x="46.746044"
+       y="155.30058" />
+    <rect
+       style="fill:#252526;fill-opacity:1;stroke:none;stroke-width:0.118454;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+       id="rect190284-6"
+       width="16.411741"
+       height="0.17200001"
+       x="46.746044"
+       y="159.81897"
+       ry="0" />
+    <text
+       xml:space="preserve"
+       style="font-size:3.50005px;line-height:1.25;font-family:Caladea;-inkscape-font-specification:Caladea;fill:#18a3ff;fill-opacity:1;stroke-width:0.656262"
+       x="53.023861"
+       y="154.84029"
+       id="text69430-6"
+       transform="scale(0.97145175,1.0293872)"><tspan
+         sodipodi:role="line"
+         id="tspan69428-49"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.50005px;font-family:Clarendon;-inkscape-font-specification:Clarendon;fill:#18a3ff;fill-opacity:1;stroke-width:0.656262"
+         x="53.023861"
+         y="154.84029"
+         dx="0 0 0 0.19842868">Fort</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:3.65019px;line-height:1.25;font-family:'Segoe UI';-inkscape-font-specification:'Segoe UI';fill:#eef5f9;fill-opacity:1;stroke-width:0.059514"
+       x="60.493782"
+       y="154.85524"
+       id="text81726-5"
+       transform="scale(0.97145175,1.0293872)"><tspan
+         sodipodi:role="line"
+         id="tspan81724-04"
+         style="font-weight:bold;font-size:3.65019px;fill:#eef5f9;fill-opacity:1;stroke-width:0.059514"
+         x="60.493782"
+         y="154.85524">LS</tspan></text>
+    <path
+       fill="currentColor"
+       d="m 50.005343,158.05624 h 0.187439 l 0.704913,-0.74698 v -0.1989 l -0.353775,-0.37204 h -0.184813 l -0.4805,0.50631 h -1.320064 v -0.34969 l 0.491064,-0.51755 v -0.1989 L 48.521583,155.619 h -0.190066 l -1.056049,1.11901 v 0.1989 l 0.528027,0.55953 h 0.18744 l 0.300973,-0.32177 v 1.61982 l 0.131996,0.13966 h 1.227655 v 0.14554 l 0.351138,0.37489 h 0.18744 l 0.704912,-0.74693 v -0.1989 l -0.353774,-0.37206 h -0.184814 l -0.491061,0.51757 h -1.320095 v -1.11907 h 1.106208 v 0.13405 z m 0.446178,-1.02115 0.166332,0.17643 -0.528022,0.55952 -0.166332,-0.17643 z m 0,1.3988 0.166332,0.17643 -0.528022,0.5595 -0.166332,-0.17643 z m -2.555634,-1.23374 -0.340572,-0.36368 0.868599,-0.9204 0.343213,0.3609 z"
+       id="path2-8"
+       style="fill:#ffffff;fill-opacity:1;stroke-width:0.271769" />
+    <text
+       xml:space="preserve"
+       style="font-size:3.50005px;line-height:1.25;font-family:Caladea;-inkscape-font-specification:Caladea;fill:#d4d4d4;fill-opacity:1;stroke-width:0.656262"
+       x="40.467972"
+       y="150.24606"
+       id="text195534-7"
+       transform="scale(0.97145175,1.0293872)"><tspan
+         sodipodi:role="line"
+         id="tspan195532-1"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.50005px;font-family:Clarendon;-inkscape-font-specification:Clarendon;fill:#d4d4d4;fill-opacity:1;stroke-width:0.656262"
+         x="40.467972"
+         y="150.24606"
+         dx="0 0 0 0.19842868">Fort</tspan></text>
+    <path
+       id="rect224117-7"
+       style="fill:none;stroke:#282828;stroke-width:0.207346;stroke-linecap:square;paint-order:markers stroke fill"
+       d="M 64.228985,155.08694 H 38.865266 v -3.38392 h 25.363719"
+       sodipodi:nodetypes="cccc" />
+    <path
+       style="fill:#aeafad;fill-opacity:1;stroke:#aeafad;stroke-width:0.20122;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       d="m 46.736652,155.19548 v -3.5987"
+       id="cursor-27" />
+    <g
+       id="g22415-2"
+       transform="translate(15.056989,143.3189)">
+      <rect
+         style="fill:#333333;fill-opacity:1;stroke:#333333;stroke-width:0.177537;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+         id="rect2270-2"
+         width="26.275463"
+         height="0.2450036"
+         x="22.912422"
+         y="5.0824447"
+         ry="0" />
+      <rect
+         style="fill:#333333;fill-opacity:1;stroke:#333333;stroke-width:0.339742;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+         id="rect22125-6"
+         width="26.113001"
+         height="0.90277708"
+         x="22.993525"
+         y="4.3435688"
+         ry="0.066498294" />
+    </g>
+    <g
+       id="g16561-1"
+       transform="translate(-12.723773,-19.973215)">
+      <ellipse
+         style="fill:#52c22b;fill-opacity:1;stroke:none;stroke-width:0.207239;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;paint-order:markers stroke fill"
+         id="circle2009-3-06"
+         cx="53.077602"
+         cy="168.08707"
+         rx="0.31191254"
+         ry="0.3305144" />
+      <ellipse
+         style="fill:#e6bf28;fill-opacity:1;stroke:none;stroke-width:0.207239;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;paint-order:markers stroke fill"
+         id="circle2011-6-15"
+         cx="52.167198"
+         cy="168.08707"
+         rx="0.31191254"
+         ry="0.3305144" />
+      <ellipse
+         style="fill:#fe5a51;fill-opacity:1;stroke:none;stroke-width:0.207239;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;paint-order:markers stroke fill"
+         id="circle2013-7-9"
+         cx="51.256775"
+         cy="168.08707"
+         rx="0.31191254"
+         ry="0.3305144" />
+    </g>
+    <rect
+       style="fill:#252526;fill-opacity:1;stroke:none;stroke-width:0.048285;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+       id="rect24953"
+       width="4.6903944"
+       height="0.1"
+       x="13.652988"
+       y="-48.592613"
+       ry="0"
+       transform="rotate(90)" />
+    <g
+       id="g8408">
+      <g
+         id="g8289-3"
+         transform="translate(13.324521,21.881251)">
+        <rect
+           style="fill:#734f96;fill-opacity:1;stroke:none;stroke-width:0.0803091;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+           id="rect881-5"
+           width="17.073618"
+           height="17.073618"
+           x="80.569901"
+           y="37.571503"
+           ry="3.1753597" />
+        <path
+           inkscape:connector-curvature="0"
+           id="path1412-6"
+           d="m 82.896988,52.040811 c 0,-0.678884 0.005,-0.739363 0.0647,-0.740624 0.0356,-6.83e-4 0.38662,-0.01646 0.78011,-0.03492 0.63366,-0.02971 0.73482,-0.04329 0.88516,-0.118757 0.11887,-0.05967 0.19248,-0.129523 0.24561,-0.233059 0.0753,-0.146811 0.0759,-0.180765 0.0759,-4.823619 0,-4.362865 -0.004,-4.683588 -0.064,-4.792632 -0.0808,-0.147644 -0.22092,-0.258563 -0.39312,-0.311162 -0.0739,-0.02257 -0.46284,-0.04989 -0.86435,-0.06072 l -0.73001,-0.01969 v -0.73453 -0.734534 h 6.20972 6.20971 v 2.753713 2.753713 l -0.65608,-0.0037 c -0.36085,-0.002 -0.69767,-0.01525 -0.7485,-0.02938 -0.0909,-0.02527 -0.0931,-0.03523 -0.13669,-0.596958 -0.0559,-0.720949 -0.10159,-1.040214 -0.21749,-1.519774 -0.2506,-1.036815 -0.77937,-1.600851 -1.64282,-1.752393 -0.36227,-0.06358 -1.94282,-0.125014 -3.24244,-0.126028 l -0.89635,-7.06e-4 v 2.070491 2.07049 l 0.47128,-0.02337 c 0.53881,-0.02672 0.96236,-0.09179 1.16682,-0.179251 0.21774,-0.09315 0.44767,-0.328446 0.5553,-0.568292 0.11246,-0.250583 0.18604,-0.579882 0.24509,-1.096865 0.0232,-0.203294 0.0499,-0.40705 0.0593,-0.452791 0.017,-0.08282 0.0201,-0.08317 0.73715,-0.08317 h 0.72006 v 3.308151 3.308152 h -0.73381 -0.7338 l -0.0247,-0.381625 c -0.0447,-0.691072 -0.17924,-1.345547 -0.34093,-1.658227 -0.22102,-0.427414 -0.65602,-0.619546 -1.53037,-0.67595 -0.19313,-0.01246 -0.40632,-0.03121 -0.47376,-0.04166 l -0.12261,-0.01901 0.0143,2.081285 c 0.0151,2.195187 0.0217,2.280958 0.18824,2.464916 0.17127,0.189141 0.27114,0.202133 2.01092,0.261634 0.0354,0.0011 0.0462,0.174915 0.0462,0.740833 v 0.739251 h -3.56689 -3.56689 z"
+           style="fill:#ffffff;stroke-width:0.0803098" />
+      </g>
+      <path
+         fill="currentColor"
+         d="m 93.559169,67.77356 h 0.709225 l 2.667214,-2.826386 v -0.752591 l -1.338595,-1.407704 h -0.699287 l -1.818094,1.915751 h -4.994795 v -1.323144 l 1.858064,-1.958276 v -0.752592 l -1.997912,-2.116971 h -0.719164 l -3.995827,4.234056 v 0.752591 l 1.997925,2.117123 h 0.709226 l 1.138806,-1.217498 v 6.128995 l 0.49944,0.52844 h 4.645142 v 0.550685 l 1.328619,1.418493 h 0.709229 l 2.66721,-2.826197 v -0.752591 l -1.338591,-1.407779 h -0.699291 l -1.858055,1.958353 h -4.994909 v -4.234284 h 4.185618 v 0.507216 z m 1.688227,-3.863779 0.62936,0.667569 -1.997906,2.117085 -0.629356,-0.667569 z m 0,5.292715 0.62936,0.667565 -1.997906,2.117009 -0.629356,-0.667569 z m -9.669883,-4.668168 -1.288645,-1.376075 3.286565,-3.482566 1.298635,1.365556 z"
+         id="path2-75-2"
+         style="fill:#ee9d28;fill-opacity:1;stroke-width:1.02831" />
+    </g>
+    <g
+       id="g8289-9"
+       transform="translate(13.768431,41.57926)">
+      <rect
+         style="fill:#734f96;fill-opacity:1;stroke:none;stroke-width:0.0803091;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         id="rect881-1"
+         width="17.073618"
+         height="17.073618"
+         x="80.569901"
+         y="37.571503"
+         ry="3.1753597" />
+      <path
+         inkscape:connector-curvature="0"
+         id="path1412-2"
+         d="m 82.896988,52.040811 c 0,-0.678884 0.005,-0.739363 0.0647,-0.740624 0.0356,-6.83e-4 0.38662,-0.01646 0.78011,-0.03492 0.63366,-0.02971 0.73482,-0.04329 0.88516,-0.118757 0.11887,-0.05967 0.19248,-0.129523 0.24561,-0.233059 0.0753,-0.146811 0.0759,-0.180765 0.0759,-4.823619 0,-4.362865 -0.004,-4.683588 -0.064,-4.792632 -0.0808,-0.147644 -0.22092,-0.258563 -0.39312,-0.311162 -0.0739,-0.02257 -0.46284,-0.04989 -0.86435,-0.06072 l -0.73001,-0.01969 v -0.73453 -0.734534 h 6.20972 6.20971 v 2.753713 2.753713 l -0.65608,-0.0037 c -0.36085,-0.002 -0.69767,-0.01525 -0.7485,-0.02938 -0.0909,-0.02527 -0.0931,-0.03523 -0.13669,-0.596958 -0.0559,-0.720949 -0.10159,-1.040214 -0.21749,-1.519774 -0.2506,-1.036815 -0.77937,-1.600851 -1.64282,-1.752393 -0.36227,-0.06358 -1.94282,-0.125014 -3.24244,-0.126028 l -0.89635,-7.06e-4 v 2.070491 2.07049 l 0.47128,-0.02337 c 0.53881,-0.02672 0.96236,-0.09179 1.16682,-0.179251 0.21774,-0.09315 0.44767,-0.328446 0.5553,-0.568292 0.11246,-0.250583 0.18604,-0.579882 0.24509,-1.096865 0.0232,-0.203294 0.0499,-0.40705 0.0593,-0.452791 0.017,-0.08282 0.0201,-0.08317 0.73715,-0.08317 h 0.72006 v 3.308151 3.308152 h -0.73381 -0.7338 l -0.0247,-0.381625 c -0.0447,-0.691072 -0.17924,-1.345547 -0.34093,-1.658227 -0.22102,-0.427414 -0.65602,-0.619546 -1.53037,-0.67595 -0.19313,-0.01246 -0.40632,-0.03121 -0.47376,-0.04166 l -0.12261,-0.01901 0.0143,2.081285 c 0.0151,2.195187 0.0217,2.280958 0.18824,2.464916 0.17127,0.189141 0.27114,0.202133 2.01092,0.261634 0.0354,0.0011 0.0462,0.174915 0.0462,0.740833 v 0.739251 h -3.56689 -3.56689 z"
+         style="fill:#ffffff;stroke-width:0.0803098" />
+    </g>
+    <path
+       fill="currentColor"
+       d="m 90.870195,88.065541 h 0.709225 l 2.667214,-2.826386 V 84.486564 L 92.908039,83.07886 h -0.699287 l -1.818094,1.915751 h -4.994795 v -1.323144 l 1.858064,-1.958276 v -0.752592 l -1.997912,-2.116971 h -0.719164 l -3.995827,4.234056 v 0.752591 l 1.997925,2.117123 h 0.709226 L 84.386981,84.7299 v 6.128995 l 0.49944,0.52844 h 4.645142 v 0.550685 l 1.328619,1.418493 h 0.709229 l 2.66721,-2.826197 V 89.777725 L 92.89803,88.369946 h -0.699291 l -1.858055,1.958353 h -4.994909 v -4.234284 h 4.185618 v 0.507216 z m 1.688227,-3.863779 0.62936,0.667569 -1.997906,2.117085 -0.629356,-0.667569 z m 0,5.292715 0.62936,0.667565 -1.997906,2.117009 -0.629356,-0.667569 z m -9.669883,-4.668168 -1.288645,-1.376075 3.286565,-3.482566 1.298635,1.365556 z"
+       id="path2-75-7"
+       style="fill:#ee9d28;fill-opacity:1;stroke-width:1.02831" />
+    <g
+       id="g8801"
+       transform="translate(6.0296061,-2.0590232)">
+      <g
+         id="g8289-0"
+         transform="matrix(0.52098308,0,0,0.52098308,49.726592,86.424231)">
+        <rect
+           style="fill:#734f96;fill-opacity:1;stroke:none;stroke-width:0.0803091;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+           id="rect881-9"
+           width="17.073618"
+           height="17.073618"
+           x="80.569901"
+           y="37.571503"
+           ry="3.1753597" />
+        <path
+           inkscape:connector-curvature="0"
+           id="path1412-3"
+           d="m 82.896988,52.040811 c 0,-0.678884 0.005,-0.739363 0.0647,-0.740624 0.0356,-6.83e-4 0.38662,-0.01646 0.78011,-0.03492 0.63366,-0.02971 0.73482,-0.04329 0.88516,-0.118757 0.11887,-0.05967 0.19248,-0.129523 0.24561,-0.233059 0.0753,-0.146811 0.0759,-0.180765 0.0759,-4.823619 0,-4.362865 -0.004,-4.683588 -0.064,-4.792632 -0.0808,-0.147644 -0.22092,-0.258563 -0.39312,-0.311162 -0.0739,-0.02257 -0.46284,-0.04989 -0.86435,-0.06072 l -0.73001,-0.01969 v -0.73453 -0.734534 h 6.20972 6.20971 v 2.753713 2.753713 l -0.65608,-0.0037 c -0.36085,-0.002 -0.69767,-0.01525 -0.7485,-0.02938 -0.0909,-0.02527 -0.0931,-0.03523 -0.13669,-0.596958 -0.0559,-0.720949 -0.10159,-1.040214 -0.21749,-1.519774 -0.2506,-1.036815 -0.77937,-1.600851 -1.64282,-1.752393 -0.36227,-0.06358 -1.94282,-0.125014 -3.24244,-0.126028 l -0.89635,-7.06e-4 v 2.070491 2.07049 l 0.47128,-0.02337 c 0.53881,-0.02672 0.96236,-0.09179 1.16682,-0.179251 0.21774,-0.09315 0.44767,-0.328446 0.5553,-0.568292 0.11246,-0.250583 0.18604,-0.579882 0.24509,-1.096865 0.0232,-0.203294 0.0499,-0.40705 0.0593,-0.452791 0.017,-0.08282 0.0201,-0.08317 0.73715,-0.08317 h 0.72006 v 3.308151 3.308152 h -0.73381 -0.7338 l -0.0247,-0.381625 c -0.0447,-0.691072 -0.17924,-1.345547 -0.34093,-1.658227 -0.22102,-0.427414 -0.65602,-0.619546 -1.53037,-0.67595 -0.19313,-0.01246 -0.40632,-0.03121 -0.47376,-0.04166 l -0.12261,-0.01901 0.0143,2.081285 c 0.0151,2.195187 0.0217,2.280958 0.18824,2.464916 0.17127,0.189141 0.27114,0.202133 2.01092,0.261634 0.0354,0.0011 0.0462,0.174915 0.0462,0.740833 v 0.739251 h -3.56689 -3.56689 z"
+           style="fill:#ffffff;stroke-width:0.0803098" />
+      </g>
+      <path
+         fill="currentColor"
+         d="m 88.325708,110.57693 h 0.709225 l 2.667214,-2.82639 v -0.75259 l -1.338595,-1.4077 h -0.699287 l -1.818094,1.91575 h -4.994795 v -1.32315 l 1.858064,-1.95827 v -0.75259 l -1.997912,-2.11697 h -0.719164 l -3.995827,4.23405 v 0.75259 l 1.997925,2.11713 h 0.709226 l 1.138806,-1.2175 v 6.12899 l 0.49944,0.52844 h 4.645142 v 0.55069 l 1.328619,1.41849 h 0.709229 l 2.66721,-2.8262 v -0.75259 l -1.338591,-1.40778 h -0.699291 l -1.858055,1.95836 h -4.994909 v -4.23429 h 4.185618 v 0.50722 z m 1.688227,-3.86378 0.62936,0.66757 -1.997906,2.11708 -0.629356,-0.66757 z m 0,5.29272 0.62936,0.66756 -1.997906,2.11701 -0.629356,-0.66757 z m -9.669883,-4.66817 -1.288645,-1.37608 3.286565,-3.48256 1.298635,1.36555 z"
+         id="path2-75-6"
+         style="fill:#ee9d28;fill-opacity:1;stroke-width:1.02831" />
+    </g>
+    <path
+       fill="currentColor"
+       d="m 94.010049,127.46429 h 0.709225 l 2.667214,-2.82639 v -0.75259 l -1.338595,-1.4077 h -0.699287 l -1.818094,1.91575 h -4.994795 v -1.32314 l 1.858064,-1.95828 v -0.75259 l -1.997912,-2.11697 h -0.719164 l -3.995827,4.23405 v 0.75259 l 1.997925,2.11713 h 0.709226 l 1.138806,-1.2175 v 6.12899 l 0.49944,0.52844 h 4.645142 v 0.55069 l 1.328619,1.41849 h 0.709229 l 2.66721,-2.82619 v -0.7526 l -1.338591,-1.40777 h -0.699291 l -1.858055,1.95835 h -4.994909 v -4.23429 h 4.185618 v 0.50722 z m 1.688227,-3.86378 0.62936,0.66757 -1.997906,2.11709 -0.629356,-0.66757 z m 0,5.29272 0.62936,0.66756 -1.997906,2.11701 -0.629356,-0.66757 z m -9.669883,-4.66817 -1.288645,-1.37608 3.286565,-3.48256 1.298635,1.36555 z"
+       id="path2-75-2-1"
+       style="fill:#ee9d28;fill-opacity:1;stroke-width:1.02831" />
+    <g
+       id="g8289-3-6"
+       transform="matrix(0.71405296,0,0,0.71405296,34.507534,93.850335)">
+      <rect
+         style="fill:#734f96;fill-opacity:1;stroke:none;stroke-width:0.0803091;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         id="rect881-5-2"
+         width="17.073618"
+         height="17.073618"
+         x="80.569901"
+         y="37.571503"
+         ry="3.1753597" />
+      <path
+         inkscape:connector-curvature="0"
+         id="path1412-6-6"
+         d="m 82.896988,52.040811 c 0,-0.678884 0.005,-0.739363 0.0647,-0.740624 0.0356,-6.83e-4 0.38662,-0.01646 0.78011,-0.03492 0.63366,-0.02971 0.73482,-0.04329 0.88516,-0.118757 0.11887,-0.05967 0.19248,-0.129523 0.24561,-0.233059 0.0753,-0.146811 0.0759,-0.180765 0.0759,-4.823619 0,-4.362865 -0.004,-4.683588 -0.064,-4.792632 -0.0808,-0.147644 -0.22092,-0.258563 -0.39312,-0.311162 -0.0739,-0.02257 -0.46284,-0.04989 -0.86435,-0.06072 l -0.73001,-0.01969 v -0.73453 -0.734534 h 6.20972 6.20971 v 2.753713 2.753713 l -0.65608,-0.0037 c -0.36085,-0.002 -0.69767,-0.01525 -0.7485,-0.02938 -0.0909,-0.02527 -0.0931,-0.03523 -0.13669,-0.596958 -0.0559,-0.720949 -0.10159,-1.040214 -0.21749,-1.519774 -0.2506,-1.036815 -0.77937,-1.600851 -1.64282,-1.752393 -0.36227,-0.06358 -1.94282,-0.125014 -3.24244,-0.126028 l -0.89635,-7.06e-4 v 2.070491 2.07049 l 0.47128,-0.02337 c 0.53881,-0.02672 0.96236,-0.09179 1.16682,-0.179251 0.21774,-0.09315 0.44767,-0.328446 0.5553,-0.568292 0.11246,-0.250583 0.18604,-0.579882 0.24509,-1.096865 0.0232,-0.203294 0.0499,-0.40705 0.0593,-0.452791 0.017,-0.08282 0.0201,-0.08317 0.73715,-0.08317 h 0.72006 v 3.308151 3.308152 h -0.73381 -0.7338 l -0.0247,-0.381625 c -0.0447,-0.691072 -0.17924,-1.345547 -0.34093,-1.658227 -0.22102,-0.427414 -0.65602,-0.619546 -1.53037,-0.67595 -0.19313,-0.01246 -0.40632,-0.03121 -0.47376,-0.04166 l -0.12261,-0.01901 0.0143,2.081285 c 0.0151,2.195187 0.0217,2.280958 0.18824,2.464916 0.17127,0.189141 0.27114,0.202133 2.01092,0.261634 0.0354,0.0011 0.0462,0.174915 0.0462,0.740833 v 0.739251 h -3.56689 -3.56689 z"
+         style="fill:#ffffff;stroke-width:0.0803098" />
+    </g>
+    <g
+       id="g855"
+       transform="translate(4.5677783,-58.219095)">
+      <path
+         fill="currentColor"
+         d="m 92.925121,106.02129 h 0.709225 l 2.667214,-2.82639 v -0.75259 l -1.338595,-1.4077 h -0.699287 l -1.818094,1.91575 h -4.994795 v -1.32314 l 1.858064,-1.95828 v -0.752592 l -1.997912,-2.116971 h -0.719164 l -3.995827,4.234053 v 0.75259 l 1.997925,2.11713 h 0.709226 l 1.138806,-1.2175 v 6.12899 l 0.49944,0.52844 h 4.645142 v 0.55069 l 1.328619,1.41849 h 0.709229 l 2.66721,-2.82619 v -0.7526 l -1.338591,-1.40777 h -0.699291 l -1.858055,1.95835 h -4.994909 v -4.23429 h 4.185618 v 0.50722 z m 1.688227,-3.86378 0.62936,0.66757 -1.997906,2.11709 -0.629356,-0.66757 z m 0,5.29272 0.62936,0.66756 -1.997906,2.11701 -0.629356,-0.66757 z m -9.669883,-4.66817 -1.288645,-1.37608 3.286565,-3.482563 1.298635,1.365556 z"
+         id="path2-75"
+         style="fill:#ee9d28;fill-opacity:1;stroke-width:1.02831" />
+      <g
+         id="g9019"
+         transform="matrix(0.74879652,0,0,0.74879652,20.393144,68.451434)">
+        <g
+           id="g8289"
+           transform="matrix(0.52098308,0,0,0.52098308,50.741163,22.017883)">
+          <rect
+             style="fill:#734f96;fill-opacity:1;stroke:none;stroke-width:0.0803091;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+             id="rect881"
+             width="17.073618"
+             height="17.073618"
+             x="80.569901"
+             y="37.571503"
+             ry="3.1753597" />
+          <path
+             inkscape:connector-curvature="0"
+             id="path1412"
+             d="m 82.896988,52.040811 c 0,-0.678884 0.005,-0.739363 0.0647,-0.740624 0.0356,-6.83e-4 0.38662,-0.01646 0.78011,-0.03492 0.63366,-0.02971 0.73482,-0.04329 0.88516,-0.118757 0.11887,-0.05967 0.19248,-0.129523 0.24561,-0.233059 0.0753,-0.146811 0.0759,-0.180765 0.0759,-4.823619 0,-4.362865 -0.004,-4.683588 -0.064,-4.792632 -0.0808,-0.147644 -0.22092,-0.258563 -0.39312,-0.311162 -0.0739,-0.02257 -0.46284,-0.04989 -0.86435,-0.06072 l -0.73001,-0.01969 v -0.73453 -0.734534 h 6.20972 6.20971 v 2.753713 2.753713 l -0.65608,-0.0037 c -0.36085,-0.002 -0.69767,-0.01525 -0.7485,-0.02938 -0.0909,-0.02527 -0.0931,-0.03523 -0.13669,-0.596958 -0.0559,-0.720949 -0.10159,-1.040214 -0.21749,-1.519774 -0.2506,-1.036815 -0.77937,-1.600851 -1.64282,-1.752393 -0.36227,-0.06358 -1.94282,-0.125014 -3.24244,-0.126028 l -0.89635,-7.06e-4 v 2.070491 2.07049 l 0.47128,-0.02337 c 0.53881,-0.02672 0.96236,-0.09179 1.16682,-0.179251 0.21774,-0.09315 0.44767,-0.328446 0.5553,-0.568292 0.11246,-0.250583 0.18604,-0.579882 0.24509,-1.096865 0.0232,-0.203294 0.0499,-0.40705 0.0593,-0.452791 0.017,-0.08282 0.0201,-0.08317 0.73715,-0.08317 h 0.72006 v 3.308151 3.308152 h -0.73381 -0.7338 l -0.0247,-0.381625 c -0.0447,-0.691072 -0.17924,-1.345547 -0.34093,-1.658227 -0.22102,-0.427414 -0.65602,-0.619546 -1.53037,-0.67595 -0.19313,-0.01246 -0.40632,-0.03121 -0.47376,-0.04166 l -0.12261,-0.01901 0.0143,2.081285 c 0.0151,2.195187 0.0217,2.280958 0.18824,2.464916 0.17127,0.189141 0.27114,0.202133 2.01092,0.261634 0.0354,0.0011 0.0462,0.174915 0.0462,0.740833 v 0.739251 h -3.56689 -3.56689 z"
+             style="fill:#ffffff;stroke-width:0.0803098" />
+        </g>
+      </g>
+    </g>
+  </g>
+</svg>
diff --git a/assets/lsp/completion-ani.gif b/assets/lsp/completion-ani.gif
new file mode 100644
index 00000000..a9b7a5d8
Binary files /dev/null and b/assets/lsp/completion-ani.gif differ
diff --git a/assets/lsp/completion-ani.mp4 b/assets/lsp/completion-ani.mp4
new file mode 100644
index 00000000..9be903d0
Binary files /dev/null and b/assets/lsp/completion-ani.mp4 differ
diff --git a/assets/lsp/completion.png b/assets/lsp/completion.png
new file mode 100644
index 00000000..12030076
Binary files /dev/null and b/assets/lsp/completion.png differ
diff --git a/assets/lsp/definition-goto.gif b/assets/lsp/definition-goto.gif
new file mode 100644
index 00000000..c08fde61
Binary files /dev/null and b/assets/lsp/definition-goto.gif differ
diff --git a/assets/lsp/definition-goto.mp4 b/assets/lsp/definition-goto.mp4
new file mode 100644
index 00000000..28048622
Binary files /dev/null and b/assets/lsp/definition-goto.mp4 differ
diff --git a/assets/lsp/definition-peek.png b/assets/lsp/definition-peek.png
new file mode 100644
index 00000000..5609f6e1
Binary files /dev/null and b/assets/lsp/definition-peek.png differ
diff --git a/assets/lsp/diagnostics1.png b/assets/lsp/diagnostics1.png
new file mode 100644
index 00000000..fb45ec7f
Binary files /dev/null and b/assets/lsp/diagnostics1.png differ
diff --git a/assets/lsp/doc-highlight.png b/assets/lsp/doc-highlight.png
new file mode 100644
index 00000000..e0c34c8b
Binary files /dev/null and b/assets/lsp/doc-highlight.png differ
diff --git a/assets/lsp/hover.png b/assets/lsp/hover.png
new file mode 100644
index 00000000..c9c64c7e
Binary files /dev/null and b/assets/lsp/hover.png differ
diff --git a/assets/lsp/hover2.png b/assets/lsp/hover2.png
new file mode 100644
index 00000000..fb2db710
Binary files /dev/null and b/assets/lsp/hover2.png differ
diff --git a/assets/lsp/references-peek.png b/assets/lsp/references-peek.png
new file mode 100644
index 00000000..224d1530
Binary files /dev/null and b/assets/lsp/references-peek.png differ
diff --git a/assets/lsp/rename.gif b/assets/lsp/rename.gif
new file mode 100644
index 00000000..f55f1c76
Binary files /dev/null and b/assets/lsp/rename.gif differ
diff --git a/assets/lsp/rename.mp4 b/assets/lsp/rename.mp4
new file mode 100644
index 00000000..14130796
Binary files /dev/null and b/assets/lsp/rename.mp4 differ
diff --git a/assets/lsp/rename2.gif b/assets/lsp/rename2.gif
new file mode 100644
index 00000000..041037d2
Binary files /dev/null and b/assets/lsp/rename2.gif differ
diff --git a/assets/lsp/rename2.mp4 b/assets/lsp/rename2.mp4
new file mode 100644
index 00000000..b8f10fda
Binary files /dev/null and b/assets/lsp/rename2.mp4 differ
diff --git a/assets/lsp/sig-help.gif b/assets/lsp/sig-help.gif
new file mode 100644
index 00000000..6c8f9e2e
Binary files /dev/null and b/assets/lsp/sig-help.gif differ
diff --git a/assets/lsp/sig-help.mp4 b/assets/lsp/sig-help.mp4
new file mode 100644
index 00000000..4cb7c8c8
Binary files /dev/null and b/assets/lsp/sig-help.mp4 differ
diff --git a/assets/lsp/symbols-crop.png b/assets/lsp/symbols-crop.png
new file mode 100644
index 00000000..993c63b7
Binary files /dev/null and b/assets/lsp/symbols-crop.png differ
diff --git a/assets/lsp/symbols-doc.png b/assets/lsp/symbols-doc.png
new file mode 100644
index 00000000..e98e74c4
Binary files /dev/null and b/assets/lsp/symbols-doc.png differ
diff --git a/assets/lsp/symbols-workspace.png b/assets/lsp/symbols-workspace.png
new file mode 100644
index 00000000..35bd5019
Binary files /dev/null and b/assets/lsp/symbols-workspace.png differ
diff --git a/assets/lsp/symbols.png b/assets/lsp/symbols.png
new file mode 100644
index 00000000..a9d41315
Binary files /dev/null and b/assets/lsp/symbols.png differ
diff --git a/assets/symbol-class.svg b/assets/symbol-class.svg
new file mode 100644
index 00000000..ec3b6b68
--- /dev/null
+++ b/assets/symbol-class.svg
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   aria-hidden="true"
+   role="img"
+   width="1em"
+   height="1em"
+   preserveAspectRatio="xMidYMid meet"
+   viewBox="0 0 16 16"
+   version="1.1"
+   id="svg4"
+   sodipodi:docname="symbol-class.svg"
+   inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:svg="http://www.w3.org/2000/svg">
+  <defs
+     id="defs8" />
+  <sodipodi:namedview
+     id="namedview6"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     inkscape:pagecheckerboard="0"
+     showgrid="false"
+     inkscape:zoom="35.084517"
+     inkscape:cx="6.398834"
+     inkscape:cy="8.0804875"
+     inkscape:window-width="1920"
+     inkscape:window-height="1023"
+     inkscape:window-x="0"
+     inkscape:window-y="0"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg4" />
+  <path
+     fill="currentColor"
+     d="M11.34 9.71h.71l2.67-2.67v-.71L13.38 5h-.7l-1.82 1.81h-5V5.56l1.86-1.85V3l-2-2H5L1 5v.71l2 2h.71l1.14-1.15v5.79l.5.5H10v.52l1.33 1.34h.71l2.67-2.67v-.71L13.37 10h-.7l-1.86 1.85h-5v-4H10v.48l1.34 1.38zm1.69-3.65l.63.63l-2 2l-.63-.63l2-2zm0 5l.63.63l-2 2l-.63-.63l2-2zM3.35 6.65l-1.29-1.3l3.29-3.29l1.3 1.29l-3.3 3.3z"
+     id="path2" />
+</svg>
diff --git a/docs/Makefile b/docs/Makefile
index aaa17963..bbc97288 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -6,7 +6,7 @@
 SPHINXOPTS    ?=
 SPHINXBUILD   ?= sphinx-build
 SPHINXAPIDOC  ?= sphinx-apidoc
-PANDOC		  ?= pandoc	
+PANDOC		  ?= pandoc
 SOURCEDIR     = .
 BUILDDIR      = _build
 
diff --git a/docs/README.md b/docs/README.md
deleted file mode 120000
index 32d46ee8..00000000
--- a/docs/README.md
+++ /dev/null
@@ -1,1 +0,0 @@
-../README.md
\ No newline at end of file
diff --git a/docs/conf.py b/docs/conf.py
index b9829596..c33b9108 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -55,7 +55,7 @@
 # -- Project information -----------------------------------------------------
 
 project = "fortls"
-copyright = "2021, Giannis Nikiteas"
+copyright = "2021-2022, Giannis Nikiteas"
 author = "Giannis Nikiteas"
 
 # The full version, including alpha/beta/rc tags
@@ -77,8 +77,13 @@
     "sphinx.ext.inheritance_diagram",
     "sphinx_autodoc_typehints",
     "sphinx.ext.autosectionlabel",
+    "sphinx_design",
+    "sphinx_copybutton",
     "myst_parser",
+    "sphinx_sitemap",
 ]
+# For sphinx_design in Markdown
+myst_enable_extensions = ["colon_fence"]
 
 # Add any paths that contain templates here, relative to this directory.
 templates_path = ["_templates"]
@@ -98,13 +103,21 @@
 #
 html_theme = "alabaster"
 html_theme = "sphinx_rtd_theme"
-
+html_theme = "furo"
+html_title = "fortls"
+html_logo = "../assets/logo.svg"
+html_favicon = "../assets/icon.svg"
+html_baseurl = "https://gnikit.github.io/fortls/"
 
 # Add any paths that contain custom static files (such as style sheets) here,
 # relative to this directory. They are copied after the builtin static files,
 # so a file named "default.css" will overwrite the builtin "default.css".
 html_static_path = ["_static"]
 
+# Add any extra paths that contain custom files (such as robots.txt or
+# .htaccess) here, relative to this directory. These files are copied
+# directly to the root of the documentation.
+html_extra_path = ["html_extra"]
 
 display_toc = True
 # autodoc_default_flags = ["members"]
diff --git a/docs/contact.rst b/docs/contact.rst
new file mode 100644
index 00000000..42e6a89c
--- /dev/null
+++ b/docs/contact.rst
@@ -0,0 +1,17 @@
+Contact Us
+===============
+
+Are you a company that uses ``fortls``?
+Do you need technical support?
+Is there a feature missing that you would like to see or have you spotted a bug?
+
+**Reach out and let us know!**
+
+
+You can reach out in a number of ways:
+
+- Start a `GitHub Discussion <https://github.com/gnikit/fortls/discussions>`__.
+- Ask a question on `Fortran Language Discourse <https://fortran-lang.discourse.group/>`__ and tag `@gnikit <https://fortran-lang.discourse.group/u/gnikit/>`__ in your post.
+- For Feature Requests open an issue on `GitHub <https://github.com/gnikit/fortls/issues/new/choose>`__.
+- For Bug Reports, open a bug report on `GitHub <https://github.com/gnikit/fortls/issues/new/choose>`__. Make sure to check the open GitHub issues!
+- For any other inquiry contact ``gnikit [@] duck [.] com``
diff --git a/docs/contributing.rst b/docs/contributing.rst
new file mode 100644
index 00000000..285d041b
--- /dev/null
+++ b/docs/contributing.rst
@@ -0,0 +1,44 @@
+
+Contributing to fortls
+======================
+
+There are a few ways you can support the ``fortls`` project.
+
+Financial Support
+------------------
+
+You can support us financially by becoming a **GitHub Sponsor** or by
+making a **PayPal Donation**.
+
+.. grid:: 2
+    :gutter: 0
+    :class-container: sd-text-center sd-pt-4
+    :class-row: sd-align-minor-center
+
+    .. grid-item::
+        .. button-link:: https://github.com/sponsors/gnikit
+            :ref-type: ref
+            :outline:
+            :color: danger
+            :class: sd-px-2 sd-fs-4
+
+            Become a **Sponsor**
+            :octicon:`mark-github;2em;sd-text-black`
+            :octicon:`heart-fill;2em;sd-text-danger`
+
+    .. grid-item::
+        .. button-link:: https://www.paypal.com/paypalme/inikit
+            :ref-type: ref
+            :color: primary
+            :class: sd-px-2 sd-fs-5
+
+            Make a **Donation**
+            :fa:`fa-paypal`
+
+
+Contributing Code
+-----------------
+
+.. include:: ./../CONTRIBUTING.md
+    :parser: myst_parser.sphinx_
+    :start-line: 2
diff --git a/docs/editor_integration.rst b/docs/editor_integration.rst
index 31e1241e..c9c19920 100644
--- a/docs/editor_integration.rst
+++ b/docs/editor_integration.rst
@@ -1,20 +1,20 @@
 Editor Integration
 ===================
 
-Visual Studio Code
-------------------
+`Visual Studio Code <https://code.visualstudio.com/>`__
+-------------------------------------------------------
 
 The Language Server is natively supported through the `Modern Fortran`_ extension.
-Install ``fortls`` then install the extension and all of the server's features should be instantly available.
+Install ``fortls`` then install the extension and all the server's features should be instantly available.
 
-.. _Modern Fortran: https://marketplace.visualstudio.com/items?itemName=krvajalm.linter-gfortran
+.. _Modern Fortran: https://marketplace.visualstudio.com/items?itemName=fortran-lang.linter-gfortran
 
-.. note::
+.. important::
     Make sure that ``fortls`` is reachable in your ``$PATH``. If not you can specify the option
     ``"fortran.fortls.path": "/custom/path/to/fortls"``
 
-Atom
-----
+`Atom <https://atom.io/>`__
+---------------------------
 
 Firstly ``fortls`` then install the `language-fortran`_ plugin by `@dparkins`_ to get Fortran syntax highlighting.
 Finally, install either `fortran-lsp`_ by `@gnikit`_ or `ide-fortran`_ by `@hansec`_
@@ -31,8 +31,8 @@ Finally, install either `fortran-lsp`_ by `@gnikit`_ or `ide-fortran`_ by `@hans
 .. _ide-fortran: https://atom.io/packages/ide-fortran
 .. _@hansec: https://github.com/hansec
 
-Sublime Text
-------------
+`Sublime Text <https://www.sublimetext.com/>`__
+-----------------------------------------------
 
 Firstly, install ``fortls`` then install the `LSP`_ package from package control.
 Finally, install the `Fortran`_ package and add the following in your configuration
@@ -56,8 +56,84 @@ For more details see the LSP `documentation`_.
 .. _documentation: https://lsp.sublimetext.io/language_servers/#fortran
 
 
-Vim, Neovim, GVim
------------------
+`neovim <https://neovim.io/>`__
+-------------------------------
+
+.. warning::
+    For neovim versions < 0.5.0 follow the instructions in the :ref:`vim` section.
+
+Neovim version >= 0.5.0 `natively supports LSP <https://neovim.io/doc/lsp/>`_.
+To enable the native LSP functionality install the `lspconfig`_ plugin with your
+favourite plugin manager.
+
+Then in your configuration file (i.e. ``init.lua``) add the following:
+
+.. code-block:: lua
+
+    require'lspconfig'.fortls.setup{}
+
+
+If additional ``fortls`` options need to be passed to you can do that through
+the ``cmd`` option in ``setup{}``
+
+
+.. code-block:: lua
+
+    require'lspconfig'.fortls.setup{
+        cmd = {
+            'fortls',
+            '--lowercase_intrisics',
+            '--hover_signature',
+            '--hover_language=fortran',
+            '--use_signature_help'
+        }
+    }
+
+.. important::
+    If you are just starting with ``neovim`` it is strongly recommended using
+    the `Suggested configuration`_ from `lspconfig`_ for keybingings and server
+    attaching. **Remember to attach the server during setup{}**
+
+.. _lspconfig: https://github.com/neovim/nvim-lspconfig
+.. _Suggested configuration: https://github.com/neovim/nvim-lspconfig#suggested-configuration
+
+
+.. _vim:
+
+`Vim <https://www.vim.org/>`__
+------------------------------
+
+Vim does not support LSP natively, so a 3rd party extensions need to be installed.
+A few options are available:
+
+`YouCompleteMe <https://ycm-core.github.io/YouCompleteMe/>`__
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+`YouCompleteMe <https://github.com/ycm-core/YouCompleteMe>`__
+is a popular Vim plugin and code-completion engine that also provides an LSP interface.
+You can therefore use it to register Language Servers like ``fortls``.
+
+For more information about configuring an arbitrary Language Server in YouCompleteMe,
+`see here <https://ycm-core.github.io/YouCompleteMe/#plugging-an-arbitrary-lsp-server>`__.
+
+.. code-block:: vim
+
+    " YouCompleteMe configuration options
+    let g:ycm_language_server =
+        \[
+        \   {
+        \       'name': 'fortls',
+        \       'cmdline': ['fortls', '--hover_language', 'fortran', '--notify_init', '--hover_signature', '--use_signature_help'],
+        \       'filetypes': ['fortran'],
+        \       'project_root_files': ['.fortls'],
+        \   },
+        \]
+    nmap <leader>yfw <Plug>(YCMFindSymbolInWorkspace)
+    nmap <leader>yfd <Plug>(YCMFindSymbolInDocument)
+
+
+`LanguageClient-neovim <https://github.com/autozimu/LanguageClient-neovim>`__
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 Firstly install the plugin `LanguageClient-neovim`_. Then edit your ``~/.vimrc`` settings file
 to set ``fortls`` for Fortran files
@@ -67,7 +143,7 @@ to set ``fortls`` for Fortran files
     " Required for operations modifying multiple buffers like rename. set hidden
     let g:LanguageClient_serverCommands = {
         " Add any default arguments you want fortls to have inside []
-        \ 'fortls': ['--hover_signature', '--use_signature_help'],
+        \ 'fortran': ['fortls', '--hover_signature', '--hover_language', 'fortran', '--use_signature_help'],
         \ }
 
     " note that if you are using Plug mapping you should not use `noremap` mappings.
@@ -79,8 +155,9 @@ to set ``fortls`` for Fortran files
 
 .. _LanguageClient-neovim: https://github.com/autozimu/LanguageClient-neovim
 
-EMACS
------
+
+`EMACS <https://www.gnu.org/software/emacs/>`__
+-----------------------------------------------
 
 Install the `lsp-mode`_ plugin. This should then allow for the variables
 `lsp-clients-fortls-args`_ and `lsp-clients-fortls-executable`_ to be defined in the ``~/.emacs`` configuration file.
@@ -96,12 +173,12 @@ Installing this `VS17 extension`_ should enable ``fortls`` features in Visual St
 
 .. _VS17 extension: https://github.com/michaelkonecny/vs-fortran-ls-client
 
-Kakoune
--------
+`Kakoune <https://kakoune.org/>`__
+----------------------------------
 
 Install `kak-lsp <https://github.com/kak-lsp/>`_.
 
-Edit the kak-lsp.toml config file to include:
+Edit the ``kak-lsp.toml`` config file to include:
 
 .. code-block:: sh
 
@@ -111,7 +188,7 @@ Edit the kak-lsp.toml config file to include:
   command = "fortls"
   args = ["--symbol_skip_mem", "--incremental_sync", "--autocomplete_no_prefix", "--lowercase_intrisics"]
 
-Edit your kakrc config to enable kak-lsp, adding fortran as a filetype:
+Edit your ``kakrc`` config to enable ``kak-lsp``, adding ``fortran`` as a filetype:
 
 .. code-block:: sh
 
@@ -120,5 +197,3 @@ Edit your kakrc config to enable kak-lsp, adding fortran as a filetype:
    hook global WinSetOption filetype=(fortran) %{
      lsp-enable-window
    }
-
-
diff --git a/docs/features.rst b/docs/features.rst
new file mode 100644
index 00000000..8f8252ca
--- /dev/null
+++ b/docs/features.rst
@@ -0,0 +1,146 @@
+Features
+===============
+
+- Project-wide and Document symbol detection and Renaming
+- Hover support, Signature help and Auto-completion
+- GoTo/Peek implementation and Find/Peek references
+- Preprocessor support
+- Documentation parsing `Doxygen <http://www.doxygen.org/>`__ and `FORD <https://github.com/Fortran-FOSS-Programmers/ford>`__ styles
+- Diagnostics
+- Code actions
+- Intrinsics modules
+
+
+Completion
+----------
+
+.. image:: ../assets/lsp/completion-ani.gif
+
+.. image:: ../assets/lsp/completion.png
+
+
+Hover
+-------
+
+.. image:: ../assets/lsp/hover.png
+
+.. image:: ../assets/lsp/hover2.png
+
+
+Symbols
+------------
+
+Project-wide and single Document symbol search
+
+.. image:: ../assets/lsp/symbols-workspace.png
+
+.. image:: ../assets/lsp/symbols-doc.png
+
+
+
+Signature Help
+----------------
+
+.. image:: ../assets/lsp/sig-help.gif
+
+
+Find References
+------------------
+
+.. figure:: ../assets/lsp/definition-goto.gif
+    :align: left
+
+    *Go To Definition of a function*
+
+
+.. figure:: ../assets/lsp/definition-peek.png
+    :align: left
+
+    *Peek into the Definition of a function*
+
+
+.. figure:: ../assets/lsp/references-peek.png
+    :align: left
+
+    *Peek into all the References of a function*
+
+
+Renaming
+------------
+
+.. figure:: ../assets/lsp/rename.gif
+    :align: left
+
+    *Rename a variable*
+
+
+Diagnostics
+-------------
+
+- Multiple definitions with the same variable name
+- Variable definition masks definition from parent scope
+- Missing subroutine/function arguments
+- Unknown user-defined type used in ``TYPE``/ ``CLASS`` definition (only if visible in project)
+- Unclosed blocks/scopes
+- Invalid scope nesting
+- Unknown modules in ``USE`` statement
+- Unimplemented deferred type-bound procedures
+- Use of non-imported variables/objects in interface blocks
+- Statement placement errors (``CONTAINS``, ``IMPLICIT``, ``IMPORT``)
+
+
+
+Code Actions
+---------------
+
+- Generate type-bound procedures and implementation templates for deferred procedures
+
+
+Intrinsics Modules
+------------------
+
+
+- ``ISO_FORTRAN_ENV``, ``IOS_C_BINDING`` GCC 11.2.0
+- ``IEEE_EXCEPTIONS``, ``IEEE_ARITHMETIC``, ``IEEE_FEATURES`` GCC 11.2.0
+- OpenMP ``OMP_LIB``, ``OMP_LIB_KINDS`` v5.0
+- OpenACC ``OPENACC``, ``OPENACC_KINDS`` v3.1
+
+
+All LSP Requests
+--------------------
+
+.. list-table:: tmp
+    :header-rows: 1
+
+    *   - Request
+        - Description
+    *   - ``workspace/symbol``
+        - Get workspace-wide symbols
+    *   - ``textDocument/documentSymbol``
+        - Get document symbols e.g. functions, subroutines, etc.
+    *   - ``textDocument/completion``
+        - Suggested tab-completion when typing
+    *   - ``textDocument/signatureHelp``
+        - Get signature information at a given cursor position
+    *   - ``textDocument/definition``
+        - GoTo definition/Peek definition
+    *   - ``textDocument/references``
+        - Find all/Peek references
+    *   - ``textDocument/documentHighlight``
+        - Same as ``textDocument/references``
+    *   - ``textDocument/hover``
+        - Show messages and signatures upon hover
+    *   - ``textDocument/implementation``
+        - GoTo implementation/Peek implementation
+    *   - ``textDocument/rename``
+        - Rename a symbol across the workspace
+    *   - ``textDocument/didOpen``
+        - Document synchronisation upon opening
+    *   - ``textDocument/didSave``
+        - Document synchronisation upon saving
+    *   - ``textDocument/didClose``
+        - Document synchronisation upon closing
+    *   - ``textDocument/didChange``
+        - Document synchronisation upon changes to the document
+    *   - ``textDocument/codeAction``
+        - **Experimental** Generate code
diff --git a/docs/html_extra/google3e426562ce42e98f.html b/docs/html_extra/google3e426562ce42e98f.html
new file mode 100644
index 00000000..707193d1
--- /dev/null
+++ b/docs/html_extra/google3e426562ce42e98f.html
@@ -0,0 +1,1 @@
+google-site-verification: google3e426562ce42e98f.html
diff --git a/docs/html_extra/robots.txt b/docs/html_extra/robots.txt
new file mode 100644
index 00000000..52e370c2
--- /dev/null
+++ b/docs/html_extra/robots.txt
@@ -0,0 +1,3 @@
+User-agent: *
+Disallow:
+Sitemap: https://gnikit.github.io/fortls/sitemap.xml
diff --git a/docs/index.rst b/docs/index.rst
index c09722cb..e5fb5f1e 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -1,29 +1,193 @@
-.. fortls documentation master file, created by
-   sphinx-quickstart on Mon Jan 10 11:32:27 2022.
-   You can adapt this file completely to your liking, but it should at least
-   contain the root `toctree` directive.
+:sd_hide_title:
 
-fortls -- Fortran Language Server
-==================================
+==============
+fortls
+==============
+
+
+.. div:: landing-title
+    :style: padding: 0.1rem 0.5rem 0.6rem 0; background-image: linear-gradient(315deg, #2753e3 0%, #734f96 74%); clip-path: polygon(0px 0px, 100% 0%, 100% 100%, 0% calc(100% - 1.5rem)); -webkit-clip-path: polygon(0px 0px, 100% 0%, 100% 100%, 0% calc(100% - 1.5rem));
+
+    .. grid::
+        :reverse:
+        :gutter: 2 3 3 3
+        :margin: 4 4 1 2
+
+        .. grid-item::
+            :columns: 12 6 6 6
+
+            .. image:: ../assets/logo2-animated.svg
+                :alt: fortls
+                :width: 100%
+
+        .. grid-item::
+            :columns: 12 6 6 6
+            :child-align: justify
+            :class: sd-text-white sd-fs-3
+
+            A Language Server for Fortran providing code completion, diagnostics, hovering and more.
+
+            .. button-ref:: quickstart
+                :ref-type: doc
+                :outline:
+                :color: white
+                :class: sd-px-4 sd-fs-5
+
+                Get Started
+
+
+.. grid:: 2
+    :gutter: 0
+    :class-container: sd-text-center sd-pt-4
+    :class-row: sd-align-minor-center
+
+    .. grid-item::
+        .. button-link:: https://github.com/sponsors/gnikit
+            :ref-type: ref
+            :outline:
+            :color: danger
+            :class: sd-px-2 sd-fs-4
+
+            Become a **Sponsor**
+            :octicon:`mark-github;2em;sd-text-black`
+            :octicon:`heart-fill;2em;sd-text-danger`
+
+    .. grid-item::
+        .. button-link:: https://www.paypal.com/paypalme/inikit
+            :ref-type: ref
+            :color: primary
+            :class: sd-px-2 sd-fs-5
+
+            Make a **Donation**
+            :fa:`fa-paypal`
+
+
+.. div::  sd-text-center
+
+    *A tool to supercharge Fortran development!*
+
+    .. tab-set::
+        :class: sd-align-major-center
+
+
+        .. tab-item:: Completion
+            :class-label: sd-rounded-2 sd-border-1 sd-my-2 sd-mx-2 sd-px-2 sd-py-1
+
+            .. image:: ../assets/lsp/completion-ani.gif
+
+        .. tab-item:: Hover
+            :class-label: sd-rounded-2 sd-border-1 sd-my-2 sd-mx-2 sd-px-2 sd-py-1
+
+            .. image:: ../assets/lsp/hover2.png
+
+        .. tab-item:: Rename
+            :class-label: sd-rounded-2 sd-border-1 sd-my-2 sd-mx-2 sd-px-2 sd-py-1
+
+            .. image:: ../assets/lsp/rename2.gif
+
+        .. tab-item:: Symbols
+            :class-label: sd-rounded-2 sd-border-1 sd-my-2 sd-mx-2 sd-px-2 sd-py-1
+
+            .. image:: ../assets/lsp/symbols-crop.png
+
+        .. tab-item:: References
+            :class-label: sd-rounded-2 sd-border-1 sd-my-2 sd-mx-2 sd-px-2 sd-py-1
+
+            .. image:: ../assets/lsp/definition-peek.png
+
+        .. tab-item:: Diagnostics
+            :class-label: sd-rounded-2 sd-border-1 sd-my-2 sd-mx-2 sd-px-2 sd-py-1
+
+            .. image:: ../assets/lsp/diagnostics1.png
+
+
+.. TODO: here go the sponsors
+
+.. toctree::
+    :hidden:
+
+    quickstart.rst
+
+.. toctree::
+    :maxdepth: 2
+    :caption: Components
+    :hidden:
+
+    features.rst
+    editor_integration.rst
+    options.rst
+    fortls_changes.md
 
 .. toctree::
-   :maxdepth: 2
-   :caption: Contents:
+    :maxdepth: 2
+    :caption: Get Involved
+    :hidden:
+
+    contributing.rst
+
+.. toctree::
+    :maxdepth: 2
+    :caption: Contact Us
+    :hidden:
+
+    contact.rst
+
+.. toctree::
+    :hidden:
+    :caption: Development
+
+    modules.rst
+
+.. grid:: 1 2 3 3
+    :margin: 4 4 0 0
+    :gutter: 1
+
+
+    .. grid-item-card:: :octicon:`desktop-download;5em;sd-text-primary`
+        :link-type: any
+        :link: Download
+        :class-body: sd-text-center
+
+        Download
+
+
+    .. grid-item-card:: :material-sharp:`import_contacts;5em;sd-text-primary`
+        :class-body: sd-text-center
+        :link: features
+        :link-type: doc
+
+        Features
+
+    .. grid-item-card:: :material-outlined:`settings;5em;sd-text-primary`
+        :link-type: doc
+        :link: options
+        :class-body: sd-text-center
+
+        Configuration Options
+
+    .. grid-item-card:: :octicon:`browser;5em;sd-text-primary`
+        :link-type: doc
+        :link: editor_integration
+        :class-body: sd-text-center
+
+        Editor Integration
+
+    .. grid-item-card:: :material-round:`mail;5em;sd-text-primary`
+        :link-type: doc
+        :link: contact
+        :class-body: sd-text-center
+
+        Contact Us
+
+    .. grid-item-card:: :octicon:`git-pull-request;5em;sd-text-primary`
+        :link-type: doc
+        :link: contributing
+        :class-body: sd-text-center
+
+        Contribute
 
-   README.md
-   options.rst
-   editor_integration.rst
-   fortls_changes.md
-   modules.rst
 
 ..
    Include native markdown into native rst
    .. include:: README.md
       :parser: myst_parser.sphinx_
-
-Indices and tables
-==================
-
-* :ref:`genindex`
-* :ref:`modindex`
-* :ref:`search`
diff --git a/docs/modules.rst b/docs/modules.rst
index bf41c9b0..ee901288 100644
--- a/docs/modules.rst
+++ b/docs/modules.rst
@@ -1,7 +1,16 @@
-Developers' documentations
+Documentation
 ==========================
 
 .. toctree::
    :maxdepth: 4
 
    fortls
+
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+
+.. * :ref:`search`
diff --git a/docs/options.rst b/docs/options.rst
index 153b7952..80f910e5 100644
--- a/docs/options.rst
+++ b/docs/options.rst
@@ -6,7 +6,7 @@ through a configuration file (by default named ``.fortls``).
 The options available from the command line and through the configuration file
 are identical and interchangeable.
 
-.. note:: Options defined in the configuration file have precedence over command line arguments.
+.. important:: Options defined in the configuration file have precedence over command line arguments.
 
 The following sections discuss the available settings in detail.
 
@@ -18,7 +18,7 @@ Configuration using the command line
 
 .. argparse::
    :module: fortls
-   :func: commandline_args
+   :func: cli
    :prog: fortls
    :nodefault:
 
@@ -108,7 +108,7 @@ incl_suffixes
 .F .f .F03 .f03 .F05 .f05 .F08 .f08 .F18 .f18 .F77 .f77 .F90 .f90 .F95 .f95 .FOR .for .FPP .fpp.
 Additional source file extensions can be defined in ``incl_suffixes``.
 
-.. note:: The default file extensions cannot be overwritten. ``incl_suffixes`` will only append to the default extensions. 
+.. note:: The default file extensions cannot be overwritten. ``incl_suffixes`` will only append to the default extensions.
 
 
 excl_suffixes
@@ -171,7 +171,7 @@ By default ``fortls`` will scan the project's directories for files with extensi
 files are external to the project, their locations can be specific via
 ``include_dirs``.
 
-.. note:: glob fnmatch style patterns  are allowed
+.. note:: glob fnmatch style patterns are allowed
 .. warning:: Source files detected in ``include_dirs`` will not be parsed for Fortran objects unless they are also included in ``source_dirs``.
 
 
@@ -187,7 +187,7 @@ pp_defs
       }
    }
 
-Additional **preprocessor definitions** from what are specified in files found in 
+Additional **preprocessor definitions** from what are specified in files found in
 ``include_dirs`` can be defined in ``pp_defs``.
 
 .. note:: Definitions in ``pp_defs`` will override definitions from ``include_dirs``
diff --git a/docs/quickstart.rst b/docs/quickstart.rst
new file mode 100644
index 00000000..7ed99085
--- /dev/null
+++ b/docs/quickstart.rst
@@ -0,0 +1,91 @@
+Get Started
+###########
+
+.. article-info::
+    :avatar: ../assets/f.svg
+    :avatar-link: https://github.com/gnikit
+    :author: `gnikit <https://github.com/gnikit>`__
+    :date: |today|
+    :read-time: 1 min read
+    :class-avatar: sd-animate-grow50-rot20
+
+
+``fortls`` is a tool known as a language server that interfaces with your code editor
+(VS Code, Vim, etc.) to provide features like code completion, code navigation, hover messages, and many more.
+
+Download
+********
+
+The project is available for download through the **PyPi** and **Anaconda** package managers
+
+.. tab-set::
+
+    .. tab-item:: PyPi
+
+        .. code-block:: sh
+
+            pip install fortls
+
+
+        For more information see `pypi/fortls`_
+
+        .. _pypi/fortls: https://pypi.python.org/pypi/fortls
+
+    .. tab-item:: Anaconda
+
+        .. code-block:: sh
+
+            conda install -c conda-forge fortls
+
+        For more installation instructions, see `conda-forge/fortls`_.
+
+        .. _conda-forge/fortls: https://github.com/conda-forge/fortls-feedstock#about-fortls
+
+
+    .. tab-item:: Source
+
+        Alternatively, one can install the development version from **GitHub** via
+
+        .. code-block:: sh
+
+            pip install --user --upgrade git+git://github.com/gnikit/fortls
+
+
+.. warning::
+    It is **NOT** possible having ``fortls`` and ``fortran-language-server``
+    simultaneously installed, since they use the same binary name. If you are having trouble
+    getting ``fortls`` to work try uninstalling ``fortran-language-server`` and reinstalling ``fortls``.
+
+
+Usage
+*****
+
+To make full use of ``fortls`` in your workflow you need to
+
+- integrate it into your code editor, see: :doc:`editor_integration`
+- (Optional) configure any additional settings to ``fortls``, see: :doc:`options`
+
+Integration
+===========
+
+Depending on the code editor used, different steps will have to be followed to integrate ``fortls``.
+Luckily, we support numerous code editors and have detailed instructions in the
+:doc:`editor_integration` section.
+
+.. card:: Example: VS Code
+
+    Setting up ``fortls`` with `VS Code`_ is as simple as installing
+    the `Modern Fortran`_ extension.
+
+.. _VS Code: https://code.visualstudio.com
+.. _Modern Fortran: https://marketplace.visualstudio.com/items?itemName=fortran-lang.linter-gfortran
+
+
+Configuration
+=============
+
+The Language Server by default is configured with reasonable settings however,
+depending on the project additional settings might need to be configured, such
+as source file paths, or additional preprocessor definitions.
+
+Instructions on how to do this and much more can be found in the :doc:`options` section.
diff --git a/fortls/__init__.py b/fortls/__init__.py
index fd559ff0..ede8a9a4 100644
--- a/fortls/__init__.py
+++ b/fortls/__init__.py
@@ -7,10 +7,10 @@
 from multiprocessing import freeze_support
 
 from .helper_functions import only_dirs, resolve_globs
-from .interface import commandline_args
+from .interface import cli
 from .jsonrpc import JSONRPC2Connection, ReadWriter, path_from_uri
 from .langserver import LangServer
-from .parse_fortran import fortran_file
+from .parse_fortran import FortranFile
 from .version import __version__
 
 __all__ = ["__version__"]
@@ -24,7 +24,7 @@ def error_exit(error_str: str):
 def main():
     #
     freeze_support()
-    args = commandline_args(__name__).parse_args()
+    args = cli(__name__).parse_args()
 
     if args.version:
         print(__version__)
@@ -86,18 +86,18 @@ def debug_server_general(args, settings):
                 "Specified 'debug_rootpath' does not exist or is not a directory"
             )
         print('\nTesting "initialize" request:')
-        print('  Root = "{0}"'.format(args.debug_rootpath))
+        print('  Root = "{}"'.format(args.debug_rootpath))
         s.serve_initialize({"params": {"rootPath": args.debug_rootpath}})
         if len(s.post_messages) == 0:
             print("  Successful!")
         else:
             print("  Successful with errors:")
             for message in s.post_messages:
-                print("    {0}".format(message[1]))
+                print("    {}".format(message[1]))
         # Print module directories
         print("\n  Source directories:")
         for source_dir in s.source_dirs:
-            print("    {0}".format(source_dir))
+            print("    {}".format(source_dir))
     #
     if args.debug_diagnostics:
         print('\nTesting "textDocument/publishDiagnostics" notification:')
@@ -117,7 +117,7 @@ def debug_server_general(args, settings):
                     sline = diag["range"]["start"]["line"]
                     message = diag["message"]
                     sev = sev_map[diag["severity"] - 1]
-                    print('  {0:5d}:{1}  "{2}"'.format(sline, sev, message))
+                    print('  {:5d}:{}  "{}"'.format(sline, sev, message))
     #
     if args.debug_symbols:
         print('\nTesting "textDocument/documentSymbol" request:')
@@ -192,7 +192,7 @@ def debug_server_general(args, settings):
             else:
                 for obj in completion_results:
                     print(
-                        "    {0}: {1} -> {2}".format(
+                        "    {}: {} -> {}".format(
                             obj["kind"], obj["label"], obj["detail"]
                         )
                     )
@@ -220,11 +220,11 @@ def debug_server_general(args, settings):
                 print(json.dumps(signature_results, indent=2))
             else:
                 active_param = signature_results.get("activeParameter", 0)
-                print("    Active param = {0}".format(active_param))
+                print("    Active param = {}".format(active_param))
                 active_signature = signature_results.get("activeSignature", 0)
-                print("    Active sig   = {0}".format(active_signature))
+                print("    Active sig   = {}".format(active_signature))
                 for i, signature in enumerate(signature_results["signatures"]):
-                    print("    {0}".format(signature["label"]))
+                    print("    {}".format(signature["label"]))
                     for j, obj in enumerate(signature["parameters"]):
                         if (i == active_signature) and (j == active_param):
                             active_mark = "*"
@@ -278,14 +278,14 @@ def debug_server_general(args, settings):
             if args.debug_full_result:
                 print(json.dumps(definition_results, indent=2))
             else:
-                print('    URI  = "{0}"'.format(definition_results["uri"]))
+                print('    URI  = "{}"'.format(definition_results["uri"]))
                 print(
-                    "    Line = {0}".format(
+                    "    Line = {}".format(
                         definition_results["range"]["start"]["line"] + 1
                     )
                 )
                 print(
-                    "    Char = {0}".format(
+                    "    Char = {}".format(
                         definition_results["range"]["start"]["character"] + 1
                     )
                 )
@@ -345,7 +345,7 @@ def debug_server_general(args, settings):
                 print("=======")
                 for result in ref_results:
                     print(
-                        "  {0}  ({1}, {2})".format(
+                        "  {}  ({}, {})".format(
                             result["uri"],
                             result["range"]["start"]["line"] + 1,
                             result["range"]["start"]["character"] + 1,
@@ -379,7 +379,7 @@ def debug_server_general(args, settings):
                 print("=======")
                 for uri, result in ref_results["changes"].items():
                     path = path_from_uri(uri)
-                    print('File: "{0}"'.format(path))
+                    print('File: "{}"'.format(path))
                     file_obj = s.workspace.get(path)
                     if file_obj is not None:
                         file_contents = file_obj.contents_split
@@ -388,11 +388,11 @@ def debug_server_general(args, settings):
                             end_line = change["range"]["end"]["line"]
                             start_col = change["range"]["start"]["character"]
                             end_col = change["range"]["end"]["character"]
-                            print("  {0}, {1}".format(start_line + 1, end_line + 1))
+                            print("  {}, {}".format(start_line + 1, end_line + 1))
                             new_contents = []
                             for i in range(start_line, end_line + 1):
                                 line = file_contents[i]
-                                print("  - {0}".format(line))
+                                print("  - {}".format(line))
                                 if i == start_line:
                                     new_contents.append(
                                         line[:start_col] + change["newText"]
@@ -400,10 +400,10 @@ def debug_server_general(args, settings):
                                 if i == end_line:
                                     new_contents[-1] += line[end_col:]
                             for line in new_contents:
-                                print("  + {0}".format(line))
+                                print("  + {}".format(line))
                             print()
                     else:
-                        print('Unknown file: "{0}"'.format(path))
+                        print('Unknown file: "{}"'.format(path))
                 print("=======")
     #
     if args.debug_actions:
@@ -434,12 +434,10 @@ def debug_server_general(args, settings):
         else:
             for result in action_results:
                 print(
-                    "Kind = '{0}', Title = '{1}'".format(
-                        result["kind"], result["title"]
-                    )
+                    "Kind = '{}', Title = '{}'".format(result["kind"], result["title"])
                 )
                 for editUri, editChange in result["edit"]["changes"].items():
-                    print("\nChange: URI = '{0}'".format(editUri))
+                    print("\nChange: URI = '{}'".format(editUri))
                     pp.pprint(editChange)
                 print()
     tmpout.close()
@@ -485,8 +483,8 @@ def debug_server_parser(args):
                 print(f"Error while parsing '{args.config}' settings file")
     #
     print("\nTesting parser")
-    print('  File = "{0}"'.format(args.debug_filepath))
-    file_obj = fortran_file(args.debug_filepath, pp_suffixes)
+    print('  File = "{}"'.format(args.debug_filepath))
+    file_obj = FortranFile(args.debug_filepath, pp_suffixes)
     err_str, _ = file_obj.load_from_disk()
     if err_str:
         error_exit(f"Reading file failed: {err_str}")
@@ -495,11 +493,11 @@ def debug_server_parser(args):
     file_ast = file_obj.parse(debug=True, pp_defs=pp_defs, include_dirs=include_dirs)
     print("\n=========\nObject Tree\n=========\n")
     for obj in file_ast.get_scopes():
-        print("{0}: {1}".format(obj.get_type(), obj.FQSN))
+        print("{}: {}".format(obj.get_type(), obj.FQSN))
         print_children(obj)
     print("\n=========\nExportable Objects\n=========\n")
     for _, obj in file_ast.global_dict.items():
-        print("{0}: {1}".format(obj.get_type(), obj.FQSN))
+        print("{}: {}".format(obj.get_type(), obj.FQSN))
 
 
 def check_request_params(args, loc_needed=True):
@@ -508,17 +506,17 @@ def check_request_params(args, loc_needed=True):
     file_exists = os.path.isfile(args.debug_filepath)
     if file_exists is False:
         error_exit("Specified 'debug_filepath' does not exist")
-    print('  File = "{0}"'.format(args.debug_filepath))
+    print('  File = "{}"'.format(args.debug_filepath))
     if loc_needed:
         if args.debug_line is None:
             error_exit("'debug_line' not specified for debug request")
-        print("  Line = {0}".format(args.debug_line))
+        print("  Line = {}".format(args.debug_line))
         if args.debug_char is None:
             error_exit("'debug_char' not specified for debug request")
-        print("  Char = {0}\n".format(args.debug_char))
+        print("  Char = {}\n".format(args.debug_char))
 
 
 def print_children(obj, indent=""):
     for child in obj.get_children():
-        print("  {0}{1}: {2}".format(indent, child.get_type(), child.FQSN))
+        print("  {}{}: {}".format(indent, child.get_type(), child.FQSN))
         print_children(child, indent + "  ")
diff --git a/fortls/ftypes.py b/fortls/ftypes.py
index 3e390cb7..474dbaef 100644
--- a/fortls/ftypes.py
+++ b/fortls/ftypes.py
@@ -8,17 +8,18 @@
 
 
 @dataclass
-class VAR_info:
+class VarInfo:
     """Holds information about a Fortran VARIABLE"""
 
     var_type: str  #: Type of variable e.g. ``INTEGER``, ``REAL``, etc.
     #: keywords associated with this variable e.g. SAVE, DIMENSION, etc.
     keywords: list[str]  #: Keywords associated with variable
     var_names: list[str]  #: Variable names
+    var_kind: str = field(default=None)  #: Kind of variable e.g. ``INTEGER*4`` etc.
 
 
 @dataclass
-class SELECT_info:
+class SelectInfo:
     """Holds information about a SELECT construct"""
 
     type: int  #: Type of SELECT e.g. normal, select type, select kind, select rank
@@ -27,16 +28,16 @@ class SELECT_info:
 
 
 @dataclass
-class CLASS_info:
+class ClassInfo:
     """Holds information about a Fortran CLASS"""
 
     name: str  #: Class name
     parent: str  #: Parent object of class e.g. ``TYPE, EXTENDS(scaled_vector) :: a``
-    keywords: str  #: Keywords associated with the class
+    keywords: list[str]  #: Keywords associated with the class
 
 
 @dataclass
-class USE_info:
+class UseInfo:
     """Holds information about a Fortran USE statement"""
 
     mod_name: str  #: Module name
@@ -47,7 +48,7 @@ class USE_info:
 
 
 @dataclass
-class GEN_info:
+class GenProcDefInfo:
     """Holds information about a GENERIC PROCEDURE DEFINITION"""
 
     bound_name: str  #: Procedure name
@@ -56,7 +57,7 @@ class GEN_info:
 
 
 @dataclass
-class SMOD_info:
+class SmodInfo:
     """Holds information about Fortran SUBMODULES"""
 
     name: str  #: Submodule name
@@ -64,7 +65,7 @@ class SMOD_info:
 
 
 @dataclass
-class INT_info:
+class InterInfo:
     """Holds information about a Fortran INTERFACE"""
 
     name: str  #: Interface name
@@ -72,7 +73,7 @@ class INT_info:
 
 
 @dataclass
-class VIS_info:
+class VisInfo:
     """Holds information about the VISIBILITY of a module's contents"""
 
     type: int  #: Visibility type 0: PUBLIC 1: PRIVATE TODO: convert to boolean
@@ -80,7 +81,7 @@ class VIS_info:
 
 
 @dataclass
-class INCLUDE_info:
+class IncludeInfo:
     """Holds information about a Fortran INCLUDE statement"""
 
     line_number: int  #: Line number of include
@@ -90,7 +91,7 @@ class INCLUDE_info:
 
 
 @dataclass
-class SUB_info:
+class SubInfo:
     """Holds information about a Fortran SUBROUTINE"""
 
     name: str  #: Procedure name
@@ -102,7 +103,7 @@ class SUB_info:
 
 
 @dataclass
-class RESULT_sig:
+class ResultSig:
     """Holds information about the RESULT section of a Fortran FUNCTION"""
 
     name: str = field(default=None)  #: Variable name of result
@@ -112,11 +113,11 @@ class RESULT_sig:
 
 
 @dataclass
-class FUN_sig(SUB_info):
+class FunSig(SubInfo):
     """Holds information about a Fortran FUNCTION"""
 
     #: Function's result with default ``result.name = name``
-    result: RESULT_sig = field(default_factory=RESULT_sig)
+    result: ResultSig = field(default_factory=ResultSig)
 
     def __post_init__(self):
         if not self.result.name:
diff --git a/fortls/helper_functions.py b/fortls/helper_functions.py
index 6ba6b6d6..16b80696 100644
--- a/fortls/helper_functions.py
+++ b/fortls/helper_functions.py
@@ -3,7 +3,7 @@
 import os
 from pathlib import Path
 
-from fortls.constants import KEYWORD_ID_DICT, KEYWORD_LIST, FRegex, log, sort_keywords
+from fortls.constants import KEYWORD_ID_DICT, KEYWORD_LIST, FRegex, sort_keywords
 from fortls.ftypes import Range
 
 
@@ -52,6 +52,22 @@ def detect_fixed_format(file_lines: list[str]) -> bool:
     -------
     bool
         True if file_lines are of Fixed Fortran style
+
+    Examples
+    --------
+
+    >>> detect_fixed_format([' free format'])
+    False
+
+    >>> detect_fixed_format([' INTEGER, PARAMETER :: N = 10'])
+    False
+
+    >>> detect_fixed_format(['C Fixed format'])
+    True
+
+    Lines wih ampersands are not fixed format
+    >>> detect_fixed_format(['trailing line & ! comment'])
+    False
     """
     for line in file_lines:
         if FRegex.FREE_FORMAT_TEST.match(line):
@@ -62,7 +78,7 @@ def detect_fixed_format(file_lines: list[str]) -> bool:
         # Trailing ampersand indicates free or intersection format
         if not FRegex.FIXED_COMMENT.match(line):
             line_end = line.split("!")[0].strip()
-            if len(line_end) > 0 and line_end[-1] == "&":
+            if len(line_end) > 0 and line_end.endswith("&"):
                 return False
     return True
 
@@ -107,10 +123,10 @@ def strip_strings(in_line: str, maintain_len: bool = False) -> str:
     """
 
     def repl_sq(m):
-        return "'{0}'".format(" " * (len(m.group()) - 2))
+        return "'{}'".format(" " * (len(m.group()) - 2))
 
     def repl_dq(m):
-        return '"{0}"'.format(" " * (len(m.group()) - 2))
+        return '"{}"'.format(" " * (len(m.group()) - 2))
 
     if maintain_len:
         out_line = FRegex.SQ_STRING.sub(repl_sq, in_line)
@@ -136,21 +152,20 @@ def separate_def_list(test_str: str) -> list[str] | None:
 
     Examples
     --------
-    >>> separate_def_list("var1, var2, var3")
-    ["var1", "var2", "var3"]
-
+    >>> separate_def_list('var1, var2, var3')
+    ['var1', 'var2', 'var3']
 
-    >>> separate_def_list("var, init_var(3) = [1,2,3], array(3,3)")
-    ["var", "init_var", "array"]
+    >>> separate_def_list('var, init_var(3) = [1,2,3], array(3,3)')
+    ['var', 'init_var(3) = [1,2,3]', 'array(3,3)']
     """
     stripped_str = strip_strings(test_str)
     paren_count = 0
-    def_list = []
+    def_list: list[str] = []
     curr_str = ""
     for char in stripped_str:
-        if (char == "(") or (char == "["):
+        if char in ("(", "["):
             paren_count += 1
-        elif (char == ")") or (char == "]"):
+        elif char in (")", "]"):
             paren_count -= 1
         elif (char == ",") and (paren_count == 0):
             curr_str = curr_str.strip()
@@ -208,17 +223,17 @@ def find_paren_match(string: str) -> int:
 
     Examples
     --------
-    >>> find_paren_match("a, b)")
+    >>> find_paren_match('a, b)')
     4
 
     Multiple parenthesis that are closed
 
-    >>> find_paren_match("a, (b, c), d)")
+    >>> find_paren_match('a, (b, c), d)')
     12
 
     If the outermost parenthesis is not closed function returns -1
 
-    >>> find_paren_match("a, (b, (c, d)")
+    >>> find_paren_match('a, (b, (c, d)')
     -1
     """
     paren_count = 1
@@ -233,7 +248,9 @@ def find_paren_match(string: str) -> int:
     return ind
 
 
-def get_line_prefix(pre_lines: list, curr_line: str, col: int, qs: bool = True) -> str:
+def get_line_prefix(
+    pre_lines: list[str], curr_line: str, col: int, qs: bool = True
+) -> str:
     """Get code line prefix from current line and preceding continuation lines
 
     Parameters
@@ -252,6 +269,11 @@ def get_line_prefix(pre_lines: list, curr_line: str, col: int, qs: bool = True)
     -------
     str
         part of the line including any relevant line continuations before ``col``
+
+    Examples
+    --------
+    >>> get_line_prefix([''], '#pragma once', 0) is None
+    True
     """
     if (curr_line is None) or (col > len(curr_line)) or (curr_line.startswith("#")):
         return None
@@ -275,7 +297,8 @@ def get_line_prefix(pre_lines: list, curr_line: str, col: int, qs: bool = True)
 
 
 def resolve_globs(glob_path: str, root_path: str = None) -> list[str]:
-    """Resolve paths (absolute and relative) and glob patterns
+    """Resolve paths (absolute and relative) and glob patterns while
+    nonexistent paths are ignored
 
     Parameters
     ----------
@@ -293,47 +316,70 @@ def resolve_globs(glob_path: str, root_path: str = None) -> list[str]:
     list[str]
         Expanded glob patterns with absolute paths.
         Absolute paths are used to resolve any potential ambiguity
+
+    Examples
+    --------
+
+    Relative to a root path
+    >>> import os, pathlib
+    >>> resolve_globs('test', os.getcwd()) == [str(pathlib.Path(os.getcwd()) / 'test')]
+    True
+
+    Absolute path resolution
+    >>> resolve_globs('test') == [str(pathlib.Path(os.getcwd()) / 'test')]
+    True
     """
     # Resolve absolute paths i.e. not in our root_path
     if os.path.isabs(glob_path) or not root_path:
         p = Path(glob_path).resolve()
-        root = p.root
+        root = p.anchor  # drive letter + root path
         rel = str(p.relative_to(root))  # contains glob pattern
         return [str(p.resolve()) for p in Path(root).glob(rel)]
     else:
         return [str(p.resolve()) for p in Path(root_path).resolve().glob(glob_path)]
 
 
-def only_dirs(paths: list[str], err_msg: list = []) -> list[str]:
+def only_dirs(paths: list[str]) -> list[str]:
     """From a list of strings returns only paths that are directories
 
     Parameters
     ----------
     paths : list[str]
         A list containing the files and directories
-    err_msg : list, optional
-        A list to append error messages if any, else use log channel, by default []
 
     Returns
     -------
     list[str]
         A list containing only valid directories
+
+    Raises
+    ------
+    FileNotFoundError
+        A list containing all the non existing directories
+
+    Examples
+    --------
+
+    >>> only_dirs(['./test/', './test/test_source/', './test/test_source/test.f90'])
+    ['./test/', './test/test_source/']
+
+    >>> only_dirs(['/fake/dir/a', '/fake/dir/b', '/fake/dir/c'])
+    Traceback (most recent call last):
+    FileNotFoundError: /fake/dir/a
+    /fake/dir/b
+    /fake/dir/c
     """
     dirs: list[str] = []
+    errs: list[str] = []
     for p in paths:
         if os.path.isdir(p):
             dirs.append(p)
         elif os.path.isfile(p):
             continue
         else:
-            msg: str = (
-                f"Directory '{p}' specified in Configuration settings file does not"
-                " exist"
-            )
-            if err_msg:
-                err_msg.append([2, msg])
-            else:
-                log.warning(msg)
+            errs.append(p)
+    if errs:
+        raise FileNotFoundError("\n".join(errs))
     return dirs
 
 
@@ -372,6 +418,24 @@ def get_keywords(keywords: list, keyword_info: dict = {}):
     return keyword_strings
 
 
+def parenthetic_contents(string: str):
+    """Generate parenthesized contents in string as pairs
+    (contents, start-position, level).
+
+    Examples
+    --------
+    >>> list(parenthetic_contents('character*(10*size(val(1), 2)) :: name'))
+    [('1', 22, 2), ('val(1), 2', 18, 1), ('10*size(val(1), 2)', 10, 0)]
+    """
+    stack = []
+    for i, c in enumerate(string):
+        if c == "(":
+            stack.append(i)
+        elif c == ")" and stack:
+            start = stack.pop()
+            yield (string[start + 1 : i], start, len(stack))
+
+
 def get_paren_substring(string: str) -> str | None:
     """Get the contents enclosed by the first pair of parenthesis
 
@@ -388,12 +452,12 @@ def get_paren_substring(string: str) -> str | None:
 
     Examples
     --------
-    >>> get_paren_substring("some line(a, b, (c, d))")
-    "a, b, (c, d)"
+    >>> get_paren_substring('some line(a, b, (c, d))')
+    'a, b, (c, d)'
 
     If the line has incomplete parenthesis however, ``None`` is returned
-    >>> get_paren_substring("some line(a, b")
-    None
+    >>> get_paren_substring('some line(a, b') is None
+    True
     """
     i1 = string.find("(")
     i2 = string.rfind(")")
@@ -419,15 +483,18 @@ def get_paren_level(line: str) -> tuple[str, list[Range]]:
 
     Examples
     --------
-    >>> get_paren_level("CALL sub1(arg1,arg2")
+    >>> get_paren_level('CALL sub1(arg1,arg2')
     ('arg1,arg2', [Range(start=10, end=19)])
 
     If the range is interrupted by parenthesis, another Range variable is used
     to mark the ``start`` and ``end`` of the argument
 
-    >>> get_paren_level("CALL sub1(arg1(i),arg2")
+    >>> get_paren_level('CALL sub1(arg1(i),arg2')
     ('arg1,arg2', [Range(start=10, end=14), Range(start=17, end=22)])
 
+    >>> get_paren_level('')
+    ('', [Range(start=0, end=0)])
+
     """
     if line == "":
         return "", [Range(0, 0)]
@@ -442,18 +509,18 @@ def get_paren_level(line: str) -> tuple[str, list[Range]]:
             if char == string_char:
                 in_string = False
             continue
-        if (char == "(") or (char == "["):
+        if char in ("(", "["):
             level -= 1
             if level == 0:
                 i1 = i
             elif level < 0:
                 sections.append(Range(i + 1, i1))
                 break
-        elif (char == ")") or (char == "]"):
+        elif char in (")", "]"):
             level += 1
             if level == 1:
                 sections.append(Range(i + 1, i1))
-        elif (char == "'") or (char == '"'):
+        elif char in ("'", '"'):
             in_string = True
             string_char = char
     if level == 0:
@@ -480,16 +547,19 @@ def get_var_stack(line: str) -> list[str]:
 
     Examples
     --------
-    >>> get_var_stack("myvar%foo%bar")
-    ["myvar", "foo", "bar"]
+    >>> get_var_stack('myvar%foo%bar')
+    ['myvar', 'foo', 'bar']
+
+    >>> get_var_stack('myarray(i)%foo%bar')
+    ['myarray', 'foo', 'bar']
 
-    >>> get_var_stack("myarray(i)%foo%bar")
-    ["myarray", "foo", "bar"]
+    In this case it will operate at the end of the string i.e. ``'this%foo'``
 
-    In this case it will operate at the end of the string i.e. ``"this%foo"``
+    >>> get_var_stack('CALL self%method(this%foo')
+    ['this', 'foo']
 
-    >>> get_var_stack("CALL self%method(this%foo")
-    ["this", "foo"]
+    >>> get_var_stack('')
+    ['']
     """
     if len(line) == 0:
         return [""]
diff --git a/fortls/interface.py b/fortls/interface.py
index 024174fc..eb66eeb7 100644
--- a/fortls/interface.py
+++ b/fortls/interface.py
@@ -10,7 +10,7 @@ def __call__(self, parser, namespace, values, option_string=None):
         setattr(namespace, self.dest, set(values))
 
 
-def commandline_args(name: str = "fortls") -> argparse.ArgumentParser:
+def cli(name: str = "fortls") -> argparse.ArgumentParser:
     """Parses the command line arguments to the Language Server
 
     Returns
@@ -114,6 +114,8 @@ def commandline_args(name: str = "fortls") -> argparse.ArgumentParser:
         "--incl_suffixes",
         type=str,
         nargs="*",
+        default=set(),
+        action=SetAction,
         metavar="SUFFIXES",
         help=(
             "Consider additional file extensions to the default (default: "
diff --git a/fortls/intrinsics.py b/fortls/intrinsics.py
index 2a903ce4..1de509df 100644
--- a/fortls/intrinsics.py
+++ b/fortls/intrinsics.py
@@ -3,16 +3,16 @@
 
 from fortls.helper_functions import map_keywords
 from fortls.objects import (
-    fortran_ast,
-    fortran_function,
-    fortran_module,
-    fortran_obj,
-    fortran_subroutine,
-    fortran_type,
-    fortran_var,
+    FortranAST,
+    FortranObj,
+    Function,
+    Module,
+    Subroutine,
+    Type,
+    Variable,
 )
 
-none_ast = fortran_ast()
+none_ast = FortranAST()
 lowercase_intrinsics = False
 
 
@@ -21,7 +21,7 @@ def set_lowercase_intrinsics():
     lowercase_intrinsics = True
 
 
-class fortran_intrinsic_obj(fortran_obj):
+class Intrinsic(FortranObj):
     def __init__(self, name, type, doc_str=None, args="", parent=None):
         self.name = name
         self.type = type
@@ -87,7 +87,7 @@ def create_int_object(name, json_obj, type):
         if lowercase_intrinsics:
             name = name.lower()
             args = args.lower()
-        return fortran_intrinsic_obj(name, type, doc_str=doc_str, args=args)
+        return Intrinsic(name, type, doc_str=doc_str, args=args)
 
     def create_object(json_obj, enc_obj=None):
         if enc_obj is not None:
@@ -105,14 +105,14 @@ def create_object(json_obj, enc_obj=None):
             name = name.lower()
             args = args.lower()
         if json_obj["type"] == 0:
-            mod_tmp = fortran_module(none_ast, 0, name)
+            mod_tmp = Module(none_ast, 0, name)
             if "use" in json_obj:
                 mod_tmp.add_use(json_obj["use"], 0)
             return mod_tmp
         elif json_obj["type"] == 1:
-            return fortran_subroutine(none_ast, 0, name, args=args)
+            return Subroutine(none_ast, 0, name, args=args)
         elif json_obj["type"] == 2:
-            return fortran_function(
+            return Function(
                 none_ast,
                 0,
                 name,
@@ -122,11 +122,9 @@ def create_object(json_obj, enc_obj=None):
                 # keyword_info=keyword_info,
             )
         elif json_obj["type"] == 3:
-            return fortran_var(
-                none_ast, 0, name, json_obj["desc"], keywords, keyword_info
-            )
+            return Variable(none_ast, 0, name, json_obj["desc"], keywords, keyword_info)
         elif json_obj["type"] == 4:
-            return fortran_type(none_ast, 0, name, keywords)
+            return Type(none_ast, 0, name, keywords)
         else:
             raise ValueError
 
diff --git a/fortls/langserver.py b/fortls/langserver.py
index 4b967b26..9d884fe9 100644
--- a/fortls/langserver.py
+++ b/fortls/langserver.py
@@ -1,4 +1,3 @@
-# TODO: enable jsonc C-style comments
 from __future__ import annotations
 
 import json
@@ -14,6 +13,7 @@
 from typing import Pattern
 from urllib.error import URLError
 
+import json5
 from packaging import version
 
 # Local modules
@@ -41,7 +41,7 @@
     set_keyword_ordering,
 )
 from fortls.intrinsics import (
-    fortran_intrinsic_obj,
+    Intrinsic,
     get_intrinsic_keywords,
     load_intrinsics,
     set_lowercase_intrinsics,
@@ -49,14 +49,14 @@
 from fortls.json_templates import change_json, symbol_json, uri_json
 from fortls.jsonrpc import JSONRPC2Connection, path_from_uri, path_to_uri
 from fortls.objects import (
+    FortranAST,
+    Variable,
     climb_type_tree,
     find_in_scope,
     find_in_workspace,
-    fortran_ast,
-    fortran_var,
     get_use_tree,
 )
-from fortls.parse_fortran import fortran_file, get_line_context
+from fortls.parse_fortran import FortranFile, get_line_context
 from fortls.regex_patterns import src_file_exts
 from fortls.version import __version__
 
@@ -70,7 +70,7 @@ def __init__(self, conn, settings: dict):
         self.conn: JSONRPC2Connection = conn
         self.running: bool = True
         self.root_path: str = None
-        self.workspace: dict[str, fortran_file] = {}
+        self.workspace: dict[str, FortranFile] = {}
         self.obj_tree: dict = {}
         self.link_version = 0
         self._version = version.parse(__version__)
@@ -136,6 +136,7 @@ def noop(request: dict):
             "textDocument/signatureHelp": self.serve_signature,
             "textDocument/definition": self.serve_definition,
             "textDocument/references": self.serve_references,
+            "textDocument/documentHighlight": self.serve_references,
             "textDocument/hover": self.serve_hover,
             "textDocument/implementation": self.serve_implementation,
             "textDocument/rename": self.serve_rename,
@@ -146,6 +147,7 @@ def noop(request: dict):
             "textDocument/codeAction": self.serve_codeActions,
             "initialized": noop,
             "workspace/didChangeWatchedFiles": noop,
+            "workspace/didChangeConfiguration": noop,
             "workspace/symbol": self.serve_workspace_symbol,
             "$/cancelRequest": noop,
             "$/setTrace": noop,
@@ -190,6 +192,7 @@ def serve_initialize(self, request: dict):
         self.source_dirs.add(self.root_path)
 
         self._load_config_file()
+        self._resolve_globs_in_paths()
         self._config_logger(request)
         self._load_intrinsics()
         self._add_source_dirs()
@@ -496,7 +499,7 @@ def build_comp(
         params: dict = request["params"]
         uri: str = params["textDocument"]["uri"]
         path: str = path_from_uri(uri)
-        file_obj: fortran_file = self.workspace.get(path)
+        file_obj: FortranFile = self.workspace.get(path)
         if file_obj is None:
             return None
         # Check line
@@ -673,7 +676,7 @@ def build_comp(
 
     def get_definition(
         self,
-        def_file: fortran_file,
+        def_file: FortranFile,
         def_line: int,
         def_char: int,
         hover_req: bool = False,
@@ -716,7 +719,7 @@ def get_definition(
             return None
         # Search in Preprocessor defined variables
         if def_name in def_file.pp_defs:
-            var = fortran_var(
+            var = Variable(
                 def_file.ast,
                 def_line + 1,
                 def_name,
@@ -778,7 +781,7 @@ def get_definition(
                 ):
                     var_type = f"{FORTRAN_LITERAL}STRING"
                 if var_type:
-                    return fortran_var(
+                    return Variable(
                         curr_scope.file_ast,
                         def_line + 1,
                         def_name,
@@ -897,7 +900,7 @@ def get_all_references(
         self,
         def_obj,
         type_mem: bool,
-        file_obj: fortran_file = None,
+        file_obj: FortranFile = None,
     ):
         # Search through all files
         def_name: str = def_obj.name.lower()
@@ -1124,7 +1127,7 @@ def serve_implementation(self, request: dict):
         if var_obj is None:
             return None
         # Intrinsics do not have implementations we can access
-        if isinstance(var_obj, fortran_intrinsic_obj):
+        if isinstance(var_obj, Intrinsic):
             return None
         # Construct implementation reference
         if var_obj.parent.get_type() == CLASS_TYPE_ID:
@@ -1147,7 +1150,7 @@ def serve_rename(self, request: dict):
         def_obj = self.get_definition(file_obj, def_line, def_char)
         if def_obj is None:
             return None
-        if isinstance(def_obj, fortran_intrinsic_obj):
+        if isinstance(def_obj, Intrinsic):
             self.post_message("Rename failed: Cannot rename intrinsics", Severity.warn)
             return None
         # Determine global accesibility and type membership
@@ -1335,11 +1338,11 @@ def update_workspace_file(
             file_obj = self.workspace.get(filepath)
             if read_file:
                 if file_obj is None:
-                    file_obj = fortran_file(filepath, self.pp_suffixes)
+                    file_obj = FortranFile(filepath, self.pp_suffixes)
                     # Create empty file if not yet saved to disk
                     if not os.path.isfile(filepath):
                         if allow_empty:
-                            file_obj.ast = fortran_ast(file_obj)
+                            file_obj.ast = FortranAST(file_obj)
                             self.workspace[filepath] = file_obj
                             return False, None
                         else:
@@ -1405,7 +1408,7 @@ def file_init(
         fortran_file | str
             A Fortran file object or a string containing the error message
         """
-        file_obj = fortran_file(filepath, pp_suffixes)
+        file_obj = FortranFile(filepath, pp_suffixes)
         err_str, _ = file_obj.load_from_disk()
         if err_str:
             return err_str
@@ -1491,17 +1494,17 @@ def _load_config_file(self) -> None:
 
         # Check for config file
         config_path = os.path.join(self.root_path, self.config)
+        # NOTE: it would be better if we could distinguish between a user-defined
+        # and a default config file. If not user-defined then we can check for
+        # default names, currently only .fortls. If None are found return None
+        # if user-defined config we would want to throw an error if the file
+        # cannot be found
         if not os.path.isfile(config_path):
             return None
 
         try:
             with open(config_path, "r") as jsonfile:
-                # Allow for jsonc C-style commnets
-                # jsondata = "".join(
-                #     line for line in jsonfile if not line.startswith("//")
-                # )
-                # config_dict = json.loads(jsondata)
-                config_dict = json.load(jsonfile)
+                config_dict = json5.load(jsonfile)
 
                 # Include and Exclude directories
                 self._load_config_file_dirs(config_dict)
@@ -1520,39 +1523,20 @@ def _load_config_file(self) -> None:
                     self.debug_log = True
 
         except FileNotFoundError:
-            self.post_messages(
-                [Severity.error, f"Error settings file '{self.config}' not found"]
-            )
+            self.post_message(f"Configuration file '{self.config}' not found")
 
-        except ValueError:
-            self.post_messages(
-                [Severity.error, f"Error while parsing '{self.config}' settings file"]
-            )
+        # Erroneous json file syntax
+        except ValueError as e:
+            msg = f'Error: "{e}" while reading "{self.config}" Configuration file'
+            self.post_message(msg)
 
     def _load_config_file_dirs(self, config_dict: dict) -> None:
-        # Exclude paths (directories & files)
-        # with glob resolution
-        for path in config_dict.get("excl_paths", []):
-            self.excl_paths.update(set(resolve_globs(path, self.root_path)))
-
-        # Source directory paths (directories)
-        # with glob resolution
-        source_dirs = config_dict.get("source_dirs", [])
-        for path in source_dirs:
-            self.source_dirs.update(
-                set(
-                    only_dirs(
-                        resolve_globs(path, self.root_path),
-                        self.post_messages,
-                    )
-                )
-            )
-        # Keep all directories present in source_dirs but not excl_paths
-        self.source_dirs = {i for i in self.source_dirs if i not in self.excl_paths}
-        self.incl_suffixes = config_dict.get("incl_suffixes", [])
+        self.excl_paths = set(config_dict.get("excl_paths", self.excl_paths))
+        self.source_dirs = set(config_dict.get("source_dirs", self.source_dirs))
+        self.incl_suffixes = set(config_dict.get("incl_suffixes", self.incl_suffixes))
         # Update the source file REGEX
         self.FORTRAN_SRC_EXT_REGEX = src_file_exts(self.incl_suffixes)
-        self.excl_suffixes = set(config_dict.get("excl_suffixes", []))
+        self.excl_suffixes = set(config_dict.get("excl_suffixes", self.excl_suffixes))
 
     def _load_config_file_general(self, config_dict: dict) -> None:
         # General options ------------------------------------------------------
@@ -1606,15 +1590,41 @@ def _load_config_file_general(self, config_dict: dict) -> None:
         )
 
     def _load_config_file_preproc(self, config_dict: dict) -> None:
-        self.pp_suffixes = config_dict.get("pp_suffixes", None)  # TODO: set def
-        self.pp_defs = config_dict.get("pp_defs", {})  # TODO: set other dif?
+        self.pp_suffixes = config_dict.get("pp_suffixes", None)
+        self.pp_defs = config_dict.get("pp_defs", {})
         if isinstance(self.pp_defs, list):
             self.pp_defs = {key: "" for key in self.pp_defs}
 
-        for path in config_dict.get("include_dirs", set()):
-            self.include_dirs.update(
-                only_dirs(resolve_globs(path, self.root_path), self.post_messages)
-            )
+        self.include_dirs = set(config_dict.get("include_dirs", self.include_dirs))
+
+    def _resolve_globs_in_paths(self) -> None:
+        """Resolves glob patterns in `excl_paths`, `source_dirs` and `include_dirs`.
+        Also performs the exclusion of `excl_paths` from `source_dirs`.
+        """
+        # Exclude paths (directories & files) with glob resolution
+        excl_paths = set()
+        for path in self.excl_paths:
+            excl_paths.update(set(resolve_globs(path, self.root_path)))
+        self.excl_paths = excl_paths.copy()
+
+        # Source directory paths (directories) with glob resolution
+        source_dirs = set()
+        for path in self.source_dirs:
+            # resolve_globs filters any nonexisting directories so FileNotFoundError
+            # found inside only_dirs can never be raised
+            source_dirs.update(set(only_dirs(resolve_globs(path, self.root_path))))
+        self.source_dirs = source_dirs.copy()
+
+        # Keep all directories present in source_dirs but not excl_paths
+        self.source_dirs = {i for i in self.source_dirs if i not in self.excl_paths}
+
+        # Preprocessor includes
+        include_dirs = set()
+        for path in self.include_dirs:
+            # resolve_globs filters any nonexisting directories so FileNotFoundError
+            # found inside only_dirs can never be raised
+            include_dirs.update(set(only_dirs(resolve_globs(path, self.root_path))))
+        self.include_dirs = include_dirs.copy()
 
     def _add_source_dirs(self) -> None:
         """Will recursively add all subdirectories that contain Fortran
@@ -1704,7 +1714,7 @@ def _load_intrinsics(self) -> None:
 
     def _create_ref_link(self, obj) -> dict:
         """Create a link reference to an object"""
-        obj_file: fortran_file = obj.file_ast.file
+        obj_file: FortranFile = obj.file_ast.file
         sline, (schar, echar) = obj_file.find_word_in_code_line(obj.sline - 1, obj.name)
         if schar < 0:
             schar = echar = 0
diff --git a/fortls/objects.py b/fortls/objects.py
index 7ace630b..215ec966 100644
--- a/fortls/objects.py
+++ b/fortls/objects.py
@@ -3,7 +3,7 @@
 import copy
 import os
 import re
-from dataclasses import replace
+from dataclasses import dataclass, replace
 from typing import Pattern
 
 from fortls.constants import (
@@ -26,15 +26,15 @@
     WHERE_TYPE_ID,
     FRegex,
 )
-from fortls.ftypes import INCLUDE_info, USE_info
+from fortls.ftypes import IncludeInfo, UseInfo
 from fortls.helper_functions import get_keywords, get_paren_substring, get_var_stack
 from fortls.json_templates import diagnostic_json, location_json, range_json
 from fortls.jsonrpc import path_to_uri
 
 
 def get_use_tree(
-    scope: fortran_scope,
-    use_dict: dict[str, USE_info],
+    scope: Scope,
+    use_dict: dict[str, UseInfo],
     obj_tree: dict,
     only_list: list[str] = None,
     rename_map: dict[str, str] = None,
@@ -96,12 +96,12 @@ def intersect_only(use_stmnt):
                                 use_dict_mod, rename_map=merged_rename
                             )
             else:
-                use_dict[use_stmnt.mod_name] = USE_info(use_stmnt.mod_name, set(), {})
+                use_dict[use_stmnt.mod_name] = UseInfo(use_stmnt.mod_name, set(), {})
             # Skip if we have already visited module with the same only list
             if old_len == len(use_dict_mod.only_list):
                 continue
         else:
-            use_dict[use_stmnt.mod_name] = USE_info(
+            use_dict[use_stmnt.mod_name] = UseInfo(
                 use_stmnt.mod_name, set(merged_use_list), merged_rename
             )
         # Descend USE tree
@@ -117,7 +117,7 @@ def intersect_only(use_stmnt):
 
 
 def find_in_scope(
-    scope: fortran_scope,
+    scope: Scope,
     var_name: str,
     obj_tree: dict,
     interface: bool = False,
@@ -125,7 +125,7 @@ def find_in_scope(
     var_line_number: int = None,
 ):
     def check_scope(
-        local_scope: fortran_scope,
+        local_scope: Scope,
         var_name_lower: str,
         filter_public: bool = False,
         var_line_number: int = None,
@@ -146,7 +146,7 @@ def check_scope(
                 # and start of scope i.e. function definition is equal to the request ln
                 # then we are need to skip this child
                 if (
-                    isinstance(local_scope, fortran_function)
+                    isinstance(local_scope, Function)
                     and local_scope.name.lower() == child.name.lower()
                     and var_line_number in (local_scope.sline, local_scope.eline)
                 ):
@@ -171,7 +171,7 @@ def check_scope(
             if strip_str == inc.path:
                 if inc.file is None:
                     return None
-                return fortran_include(inc.file.ast, inc.line_number, inc.path)
+                return Include(inc.file.ast, inc.line_number, inc.path)
 
     # Setup USE search
     use_dict = get_use_tree(scope, {}, obj_tree)
@@ -242,7 +242,7 @@ def add_children(mod_obj, query: str):
     return matching_symbols
 
 
-def climb_type_tree(var_stack, curr_scope: fortran_scope, obj_tree: dict):
+def climb_type_tree(var_stack, curr_scope: Scope, obj_tree: dict):
     """Walk up user-defined type sequence to determine final field type"""
     # Find base variable in current scope
     iVar = 0
@@ -291,7 +291,14 @@ def __init__(
             }
 
 
-class fortran_diagnostic:
+@dataclass
+class AssociateMap:
+    var: Variable
+    bind_name: str
+    link_name: str
+
+
+class Diagnostic:
     def __init__(
         self, sline: int, message: str, severity: int = 1, find_word: str = None
     ):
@@ -335,7 +342,7 @@ def build(self, file_obj):
 
 
 # Fortran object classes
-class fortran_obj:
+class FortranObj:
     def __init__(self):
         self.vis: int = 0
         self.def_vis: int = 0
@@ -459,12 +466,12 @@ def check_definition(self, obj_tree, known_types: dict = None, interface=False):
         return None, known_types
 
 
-class fortran_scope(fortran_obj):
+class Scope(FortranObj):
     def __init__(self, file_ast, line_number: int, name: str, keywords: list = None):
         super().__init__()
         if keywords is None:
             keywords = []
-        self.file_ast: fortran_ast = file_ast
+        self.file_ast: FortranAST = file_ast
         self.sline: int = line_number
         self.eline: int = line_number
         self.name: str = name
@@ -480,7 +487,7 @@ def __init__(self, file_ast, line_number: int, name: str, keywords: list = None)
         if file_ast.enc_scope_name is not None:
             self.FQSN = file_ast.enc_scope_name.lower() + "::" + self.name.lower()
 
-    def copy_from(self, copy_source: fortran_scope):
+    def copy_from(self, copy_source: Scope):
         # Pass the reference, we don't want shallow copy since that would still
         # result into 2 versions of attributes between copy_source and self
         for k, v in copy_source.__dict__.items():
@@ -589,7 +596,7 @@ def check_definitions(self, obj_tree):
             if (contains_line >= child.sline) and (
                 child.get_type(no_link=True) in after_contains_list
             ):
-                new_diag = fortran_diagnostic(
+                new_diag = Diagnostic(
                     line_number,
                     message="Subroutine/Function definition before CONTAINS statement",
                     severity=1,
@@ -603,7 +610,7 @@ def check_definitions(self, obj_tree):
             # Check other variables in current scope
             if child.FQSN in FQSN_dict:
                 if line_number > FQSN_dict[child.FQSN]:
-                    new_diag = fortran_diagnostic(
+                    new_diag = Diagnostic(
                         line_number,
                         message=f'Variable "{child.name}" declared twice in scope',
                         severity=1,
@@ -627,7 +634,7 @@ def check_definitions(self, obj_tree):
                         parent_var.FQSN == self.FQSN
                     ):
                         continue
-                    new_diag = fortran_diagnostic(
+                    new_diag = Diagnostic(
                         line_number,
                         message=(
                             f'Variable "{child.name}" masks variable in parent scope'
@@ -652,7 +659,7 @@ def check_use(self, obj_tree):
                 if (self.parent is None) or (
                     self.parent.get_type() != INTERFACE_TYPE_ID
                 ):
-                    new_diag = fortran_diagnostic(
+                    new_diag = Diagnostic(
                         use_stmnt.line_number - 1,
                         message="IMPORT statement outside of interface",
                         severity=1,
@@ -660,7 +667,7 @@ def check_use(self, obj_tree):
                     errors.append(new_diag)
                 continue
             if use_stmnt.mod_name not in obj_tree:
-                new_diag = fortran_diagnostic(
+                new_diag = Diagnostic(
                     use_stmnt.line_number - 1,
                     message=f'Module "{use_stmnt.mod_name}" not found in project',
                     severity=3,
@@ -668,7 +675,7 @@ def check_use(self, obj_tree):
                 )
                 errors.append(new_diag)
         if (self.implicit_line is not None) and (last_use_line >= self.implicit_line):
-            new_diag = fortran_diagnostic(
+            new_diag = Diagnostic(
                 self.implicit_line - 1,
                 message="USE statements after IMPLICIT statement",
                 severity=1,
@@ -700,7 +707,7 @@ def add_subroutine(self, interface_string, no_contains=False):
         return self.file_ast.path, edits
 
 
-class fortran_module(fortran_scope):
+class Module(Scope):
     def get_type(self, no_link=False):
         return MODULE_TYPE_ID
 
@@ -713,20 +720,20 @@ def check_valid_parent(self):
         return True
 
 
-class fortran_include(fortran_scope):
+class Include(Scope):
     def get_desc(self):
         return "INCLUDE"
 
 
-class fortran_program(fortran_module):
+class Program(Module):
     def get_desc(self):
         return "PROGRAM"
 
 
-class fortran_submodule(fortran_module):
+class Submodule(Module):
     def __init__(
         self,
-        file_ast: fortran_ast,
+        file_ast: FortranAST,
         line_number: int,
         name: str,
         ancestor_name: str = None,
@@ -781,11 +788,11 @@ def resolve_link(self, obj_tree):
                     if child.get_type() == BASE_TYPE_ID:
                         child_old = child
                         if prototype.get_type() == SUBROUTINE_TYPE_ID:
-                            child = fortran_subroutine(
+                            child = Subroutine(
                                 child_old.file_ast, child_old.sline, child_old.name
                             )
                         elif prototype.get_type() == FUNCTION_TYPE_ID:
-                            child = fortran_function(
+                            child = Function(
                                 child_old.file_ast, child_old.sline, child_old.name
                             )
                         child.copy_from(child_old)
@@ -803,10 +810,10 @@ def require_link(self):
         return True
 
 
-class fortran_subroutine(fortran_scope):
+class Subroutine(Scope):
     def __init__(
         self,
-        file_ast: fortran_ast,
+        file_ast: FortranAST,
         line_number: int,
         name: str,
         args: str = "",
@@ -827,7 +834,7 @@ def is_mod_scope(self):
     def is_callable(self):
         return True
 
-    def copy_interface(self, copy_source: fortran_subroutine) -> list[str]:
+    def copy_interface(self, copy_source: Subroutine) -> list[str]:
         # Copy arguments
         self.args = copy_source.args
         self.args_snip = copy_source.args_snip
@@ -996,7 +1003,7 @@ def check_valid_parent(self):
     def get_diagnostics(self):
         errors = []
         for missing_obj in self.missing_args:
-            new_diag = fortran_diagnostic(
+            new_diag = Diagnostic(
                 missing_obj.sline - 1,
                 f'Variable "{missing_obj.name}" with INTENT keyword not found in'
                 " argument list",
@@ -1011,7 +1018,7 @@ def get_diagnostics(self):
         for (i, arg_obj) in enumerate(self.arg_objs):
             if arg_obj is None:
                 arg_name = arg_list[i].strip()
-                new_diag = fortran_diagnostic(
+                new_diag = Diagnostic(
                     self.sline - 1,
                     f'No matching declaration found for argument "{arg_name}"',
                     severity=1,
@@ -1021,10 +1028,10 @@ def get_diagnostics(self):
         return errors
 
 
-class fortran_function(fortran_subroutine):
+class Function(Subroutine):
     def __init__(
         self,
-        file_ast: fortran_ast,
+        file_ast: FortranAST,
         line_number: int,
         name: str,
         args: str = "",
@@ -1042,12 +1049,12 @@ def __init__(
         self.mod_scope: bool = mod_flag
         self.result_name: str = result_name
         self.result_type: str = result_type
-        self.result_obj: fortran_var = None
+        self.result_obj: Variable = None
         # Set the implicit result() name to be the function name
         if self.result_name is None:
             self.result_name = self.name
 
-    def copy_interface(self, copy_source: fortran_function):
+    def copy_interface(self, copy_source: Function):
         # Call the parent class method
         child_names = super().copy_interface(copy_source)
         # Return specific options
@@ -1151,9 +1158,9 @@ def get_interface(self, name_replace=None, change_arg=-1, change_strings=None):
         return "\n".join(interface_array)
 
 
-class fortran_type(fortran_scope):
+class Type(Scope):
     def __init__(
-        self, file_ast: fortran_ast, line_number: int, name: str, keywords: list
+        self, file_ast: FortranAST, line_number: int, name: str, keywords: list
     ):
         super().__init__(file_ast, line_number, name, keywords)
         #
@@ -1233,7 +1240,7 @@ def get_diagnostics(self):
             if (not self.abstract) and (
                 in_child.keywords.count(KEYWORD_ID_DICT["deferred"]) > 0
             ):
-                new_diag = fortran_diagnostic(
+                new_diag = Diagnostic(
                     self.eline - 1,
                     f'Deferred procedure "{in_child.name}" not implemented',
                     severity=1,
@@ -1287,7 +1294,7 @@ def get_actions(self, sline, eline):
                     }
                 )
                 edits += interface_edits
-                new_diag = fortran_diagnostic(
+                new_diag = Diagnostic(
                     line_number,
                     f'Deferred procedure "{in_child.name}" not implemented',
                     severity=1,
@@ -1312,8 +1319,8 @@ def get_actions(self, sline, eline):
         return actions
 
 
-class fortran_block(fortran_scope):
-    def __init__(self, file_ast: fortran_ast, line_number: int, name: str):
+class Block(Scope):
+    def __init__(self, file_ast: FortranAST, line_number: int, name: str):
         super().__init__(file_ast, line_number, name)
 
     def get_type(self, no_link=False):
@@ -1329,8 +1336,8 @@ def req_named_end(self):
         return True
 
 
-class fortran_do(fortran_block):
-    def __init__(self, file_ast: fortran_ast, line_number: int, name: str):
+class Do(Block):
+    def __init__(self, file_ast: FortranAST, line_number: int, name: str):
         super().__init__(file_ast, line_number, name)
 
     def get_type(self, no_link=False):
@@ -1340,8 +1347,8 @@ def get_desc(self):
         return "DO"
 
 
-class fortran_where(fortran_block):
-    def __init__(self, file_ast: fortran_ast, line_number: int, name: str):
+class Where(Block):
+    def __init__(self, file_ast: FortranAST, line_number: int, name: str):
         super().__init__(file_ast, line_number, name)
 
     def get_type(self, no_link=False):
@@ -1351,8 +1358,8 @@ def get_desc(self):
         return "WHERE"
 
 
-class fortran_if(fortran_block):
-    def __init__(self, file_ast: fortran_ast, line_number: int, name: str):
+class If(Block):
+    def __init__(self, file_ast: FortranAST, line_number: int, name: str):
         super().__init__(file_ast, line_number, name)
 
     def get_type(self, no_link=False):
@@ -1362,10 +1369,10 @@ def get_desc(self):
         return "IF"
 
 
-class fortran_associate(fortran_block):
-    def __init__(self, file_ast: fortran_ast, line_number: int, name: str):
+class Associate(Block):
+    def __init__(self, file_ast: FortranAST, line_number: int, name: str):
         super().__init__(file_ast, line_number, name)
-        self.assoc_links = []
+        self.links: list[AssociateMap] = []  # holds the info to associate variables
 
     def get_type(self, no_link=False):
         return ASSOC_TYPE_ID
@@ -1373,32 +1380,64 @@ def get_type(self, no_link=False):
     def get_desc(self):
         return "ASSOCIATE"
 
-    def create_binding_variable(self, file_ast, line_number, bound_name, link_var):
-        new_var = fortran_var(file_ast, line_number, bound_name, "UNKNOWN", [])
-        self.assoc_links.append([new_var, bound_name, link_var])
+    def create_binding_variable(
+        self, file_ast: FortranAST, line_number: int, bind_name: str, link_name: str
+    ) -> Variable:
+        """Create a new variable to be linked upon resolution to the real variable
+        that contains the information of the mapping from the parent scope to the
+        ASSOCIATE block scope.
+
+        Parameters
+        ----------
+        file_ast : fortran_ast
+            AST file
+        line_number : int
+            Line number
+        bind_name : str
+            Name of the ASSOCIATE block variable
+        link_name : str
+            Name of the parent scope variable
+
+        Returns
+        -------
+        fortran_var
+            Variable object holding the ASSOCIATE block variable, pending resolution
+        """
+        new_var = Variable(file_ast, line_number, bind_name, "UNKNOWN", [])
+        self.links.append(AssociateMap(new_var, bind_name, link_name))
         return new_var
 
     def resolve_link(self, obj_tree):
-        for assoc_link in self.assoc_links:
-            var_stack = get_var_stack(assoc_link[2])
-            if len(var_stack) > 1:
+        # Loop through the list of the associated variables map and resolve the links
+        # find the AST node that that corresponds to the variable with link_name
+        for assoc in self.links:
+            # TODO: extract the dimensions component from the link_name
+            # re.sub(r'\(.*\)', '', link_name) removes the dimensions component
+            # keywords = re.match(r'(.*)\((.*)\)', link_name).groups()
+            # now pass the keywords through the dimension_parser and set the keywords
+            # in the associate object. Hover should now pick the local keywords
+            # over the linked_object keywords
+            assoc.link_name = re.sub(r"\(.*\)", "", assoc.link_name)
+            var_stack = get_var_stack(assoc.link_name)
+            is_member = len(var_stack) > 1
+            if is_member:
                 type_scope = climb_type_tree(var_stack, self, obj_tree)
                 if type_scope is None:
                     continue
                 var_obj = find_in_scope(type_scope, var_stack[-1], obj_tree)
                 if var_obj is not None:
-                    assoc_link[0].link_obj = var_obj
+                    assoc.var.link_obj = var_obj
             else:
-                var_obj = find_in_scope(self, assoc_link[2], obj_tree)
+                var_obj = find_in_scope(self, assoc.link_name, obj_tree)
                 if var_obj is not None:
-                    assoc_link[0].link_obj = var_obj
+                    assoc.var.link_obj = var_obj
 
     def require_link(self):
         return True
 
 
-class fortran_enum(fortran_block):
-    def __init__(self, file_ast: fortran_ast, line_number: int, name: str):
+class Enum(Block):
+    def __init__(self, file_ast: FortranAST, line_number: int, name: str):
         super().__init__(file_ast, line_number, name)
 
     def get_type(self, no_link=False):
@@ -1408,8 +1447,8 @@ def get_desc(self):
         return "ENUM"
 
 
-class fortran_select(fortran_block):
-    def __init__(self, file_ast: fortran_ast, line_number: int, name: str, select_info):
+class Select(Block):
+    def __init__(self, file_ast: FortranAST, line_number: int, name: str, select_info):
         super().__init__(file_ast, line_number, name)
         self.select_type = select_info.type
         self.binding_name = None
@@ -1457,18 +1496,18 @@ def create_binding_variable(self, file_ast, line_number, var_desc, case_type):
             bound_var = None
         # Create variable
         if binding_name is not None:
-            return fortran_var(
+            return Variable(
                 file_ast, line_number, binding_name, var_desc, [], link_obj=bound_var
             )
         elif (binding_name is None) and (bound_var is not None):
-            return fortran_var(file_ast, line_number, bound_var, var_desc, [])
+            return Variable(file_ast, line_number, bound_var, var_desc, [])
         return None
 
 
-class fortran_int(fortran_scope):
+class Interface(Scope):
     def __init__(
         self,
-        file_ast: fortran_ast,
+        file_ast: FortranAST,
         line_number: int,
         name: str,
         abstract: bool = False,
@@ -1506,10 +1545,10 @@ def require_link(self):
         return True
 
 
-class fortran_var(fortran_obj):
+class Variable(FortranObj):
     def __init__(
         self,
-        file_ast: fortran_ast,
+        file_ast: FortranAST,
         line_number: int,
         name: str,
         var_desc: str,
@@ -1520,7 +1559,7 @@ def __init__(
         super().__init__()
         if keyword_info is None:
             keyword_info = {}
-        self.file_ast: fortran_ast = file_ast
+        self.file_ast: FortranAST = file_ast
         self.sline: int = line_number
         self.eline: int = line_number
         self.name: str = name
@@ -1601,6 +1640,7 @@ def get_type_obj(self, obj_tree):
                         self.type_obj = type_obj
         return self.type_obj
 
+    # XXX: unused delete or use for associate blocks
     def set_dim(self, dim_str):
         if KEYWORD_ID_DICT["dimension"] not in self.keywords:
             self.keywords.append(KEYWORD_ID_DICT["dimension"])
@@ -1618,17 +1658,25 @@ def get_snippet(self, name_replace=None, drop_arg=-1):
 
     def get_hover(self, long=False, include_doc=True, drop_arg=-1):
         doc_str = self.get_documentation()
-        hover_str = ", ".join(
-            [self.desc] + get_keywords(self.keywords, self.keyword_info)
-        )
+        # In associated blocks we need to fetch the desc and keywords of the
+        # linked object
+        hover_str = ", ".join([self.get_desc()] + self.get_keywords())
         # TODO: at this stage we can mae this lowercase
         # Add parameter value in the output
         if self.is_parameter() and self.param_val:
             hover_str += f" :: {self.name} = {self.param_val}"
         if include_doc and (doc_str is not None):
-            hover_str += "\n {0}".format("\n ".join(doc_str.splitlines()))
+            hover_str += "\n {}".format("\n ".join(doc_str.splitlines()))
         return hover_str, True
 
+    def get_keywords(self):
+        # TODO: if local keywords are set they should take precedence over link_obj
+        # Alternatively, I could do a dictionary merge with local variables
+        # having precedence by default and use a flag to override?
+        if self.link_obj is not None:
+            return get_keywords(self.link_obj.keywords, self.link_obj.keyword_info)
+        return get_keywords(self.keywords, self.keyword_info)
+
     def is_optional(self):
         if self.keywords.count(KEYWORD_ID_DICT["optional"]) > 0:
             return True
@@ -1679,7 +1727,7 @@ def check_definition(self, obj_tree, known_types={}, interface=False):
             if type_info is not None:
                 if type_info[0] == 1:
                     if interface:
-                        out_diag = fortran_diagnostic(
+                        out_diag = Diagnostic(
                             self.sline - 1,
                             message=(
                                 f'Object "{desc_obj_name}" not imported in interface'
@@ -1688,7 +1736,7 @@ def check_definition(self, obj_tree, known_types={}, interface=False):
                             find_word=desc_obj_name,
                         )
                     else:
-                        out_diag = fortran_diagnostic(
+                        out_diag = Diagnostic(
                             self.sline - 1,
                             message=f'Object "{desc_obj_name}" not found in scope',
                             severity=1,
@@ -1704,10 +1752,10 @@ def check_definition(self, obj_tree, known_types={}, interface=False):
         return None, known_types
 
 
-class fortran_meth(fortran_var):
+class Method(Variable):  # i.e. TypeBound procedure
     def __init__(
         self,
-        file_ast: fortran_ast,
+        file_ast: FortranAST,
         line_number: int,
         name: str,
         var_desc: str,
@@ -1833,7 +1881,7 @@ def check_definition(self, obj_tree, known_types={}, interface=False):
         return None, known_types
 
 
-class fortran_ast:
+class FortranAST:
     def __init__(self, file_obj=None):
         self.file = file_obj
         self.path: str = None
@@ -1865,7 +1913,7 @@ def create_none_scope(self):
         """Create empty scope to hold non-module contained items"""
         if self.none_scope is not None:
             raise ValueError
-        self.none_scope = fortran_program(self, 1, "main")
+        self.none_scope = Program(self, 1, "main")
         self.add_scope(
             self.none_scope, re.compile(r"[ ]*END[ ]*PROGRAM", re.I), exportable=False
         )
@@ -1878,7 +1926,7 @@ def get_enc_scope_name(self):
 
     def add_scope(
         self,
-        new_scope: fortran_scope,
+        new_scope: Scope,
         END_SCOPE_REGEX: Pattern[str],
         exportable: bool = True,
         req_container: bool = False,
@@ -1927,7 +1975,7 @@ def end_scope(self, line_number: int, check: bool = True):
             self.END_SCOPE_REGEX = None
         self.enc_scope_name = self.get_enc_scope_name()
 
-    def add_variable(self, new_var: fortran_var):
+    def add_variable(self, new_var: Variable):
         if self.current_scope is None:
             self.create_none_scope()
             new_var.FQSN = self.none_scope.FQSN + "::" + new_var.name.lower()
@@ -1963,7 +2011,7 @@ def add_use(
         self.current_scope.add_use(mod_word, line_number, only_list, rename_map)
 
     def add_include(self, path: str, line_number: int):
-        self.include_statements.append(INCLUDE_info(line_number, path, None, []))
+        self.include_statements.append(IncludeInfo(line_number, path, None, []))
 
     def add_doc(self, doc_string: str, forward: bool = False):
         if doc_string == "":
@@ -2116,11 +2164,11 @@ def check_file(self, obj_tree):
                 message = f"Unexpected end of scope at line {error[0]}"
             else:
                 message = "Unexpected end statement: No open scopes"
-            errors.append(fortran_diagnostic(error[1] - 1, message=message, severity=1))
+            errors.append(Diagnostic(error[1] - 1, message=message, severity=1))
         for scope in tmp_list:
             if not scope.check_valid_parent():
                 errors.append(
-                    fortran_diagnostic(
+                    Diagnostic(
                         scope.sline - 1,
                         message=f'Invalid parent for "{scope.get_desc()}" declaration',
                         severity=1,
diff --git a/fortls/parse_fortran.py b/fortls/parse_fortran.py
index 03fc8d32..8bd2bce4 100644
--- a/fortls/parse_fortran.py
+++ b/fortls/parse_fortran.py
@@ -26,18 +26,18 @@
     log,
 )
 from fortls.ftypes import (
-    CLASS_info,
-    FUN_sig,
-    GEN_info,
-    INT_info,
+    ClassInfo,
+    FunSig,
+    GenProcDefInfo,
+    InterInfo,
     Range,
-    RESULT_sig,
-    SELECT_info,
-    SMOD_info,
-    SUB_info,
-    USE_info,
-    VAR_info,
-    VIS_info,
+    ResultSig,
+    SelectInfo,
+    SmodInfo,
+    SubInfo,
+    UseInfo,
+    VarInfo,
+    VisInfo,
 )
 from fortls.helper_functions import (
     detect_fixed_format,
@@ -51,24 +51,24 @@
     strip_strings,
 )
 from fortls.objects import (
-    fortran_associate,
-    fortran_ast,
-    fortran_block,
-    fortran_do,
-    fortran_enum,
-    fortran_function,
-    fortran_if,
-    fortran_int,
-    fortran_meth,
-    fortran_module,
-    fortran_program,
-    fortran_scope,
-    fortran_select,
-    fortran_submodule,
-    fortran_subroutine,
-    fortran_type,
-    fortran_var,
-    fortran_where,
+    Associate,
+    Block,
+    Do,
+    Enum,
+    FortranAST,
+    Function,
+    If,
+    Interface,
+    Method,
+    Module,
+    Program,
+    Scope,
+    Select,
+    Submodule,
+    Subroutine,
+    Type,
+    Variable,
+    Where,
 )
 
 
@@ -169,47 +169,58 @@ def parse_var_keywords(test_str: str) -> tuple[list[str], str]:
 
 def read_var_def(line: str, var_type: str = None, fun_only: bool = False):
     """Attempt to read variable definition line"""
+
+    def parse_kind(line: str):
+        match = FRegex.KIND_SPEC.match(line)
+        if not match:
+            return None, line
+        kind_str = match.group(1).replace(" ", "")
+        line = line[match.end(0) :]
+        if kind_str.find("(") >= 0:
+            match_char = find_paren_match(line)
+            if match_char < 0:  # this triggers while typing with autocomplete
+                raise ValueError("Incomplete kind specification")
+            kind_str += line[: match_char + 1].strip()
+            line = line[match_char + 1 :]
+        return kind_str, line
+
     if var_type is None:
         type_match = FRegex.VAR.match(line)
         if type_match is None:
             return None
-        else:
-            var_type = type_match.group(0).strip()
-            trailing_line = line[type_match.end(0) :]
+        var_type = type_match.group(0).strip()
+        trailing_line = line[type_match.end(0) :]
     else:
         trailing_line = line[len(var_type) :]
     var_type = var_type.upper()
     trailing_line = trailing_line.split("!")[0]
     if len(trailing_line) == 0:
         return None
-    #
-    kind_match = FRegex.KIND_SPEC.match(trailing_line)
-    if kind_match:
-        kind_str = kind_match.group(1).replace(" ", "")
-        var_type += kind_str
-        trailing_line = trailing_line[kind_match.end(0) :]
-        if kind_str.find("(") >= 0:
-            match_char = find_paren_match(trailing_line)
-            if match_char < 0:
-                return None  # Incomplete type spec
-            else:
-                kind_word = trailing_line[: match_char + 1].strip()
-                var_type += kind_word
-                trailing_line = trailing_line[match_char + 1 :]
-    else:
-        # Class and Type statements need a kind spec
-        if var_type in ("TYPE", "CLASS"):
-            return None
-        # Make sure next character is space or comma or colon
-        if not trailing_line[0] in (" ", ",", ":"):
-            return None
+
+    # Parse the global kind, if any, for the current line definition
+    # The global kind in some cases, like characters can be overriden by a locally
+    # defined kind
+    try:
+        kind_str, trailing_line = parse_kind(trailing_line)
+        var_type += kind_str  # XXX: see below
+    except ValueError:
+        return None
+    except TypeError:  # XXX: remove with explicit kind specification in VarInfo
+        pass
+
+    # Class and Type statements need a kind spec
+    if not kind_str and var_type in ("TYPE", "CLASS"):
+        return None
+    # Make sure next character is space or comma or colon
+    if not kind_str and not trailing_line[0] in (" ", ",", ":"):
+        return None
     #
     keywords, trailing_line = parse_var_keywords(trailing_line)
     # Check if this is a function definition
-    fun_def = read_fun_def(trailing_line, RESULT_sig(type=var_type, keywords=keywords))
-    if (fun_def is not None) or fun_only:
+    fun_def = read_fun_def(trailing_line, ResultSig(type=var_type, keywords=keywords))
+    if fun_def or fun_only:
         return fun_def
-    #
+    # Split the type and variable name
     line_split = trailing_line.split("::")
     if len(line_split) == 1:
         if len(keywords) > 0:
@@ -222,8 +233,8 @@ def read_var_def(line: str, var_type: str = None, fun_only: bool = False):
         var_words = separate_def_list(trailing_line.strip())
         if var_words is None:
             var_words = []
-    #
-    return "var", VAR_info(var_type, keywords, var_words)
+
+    return "var", VarInfo(var_type, keywords, var_words, kind_str)
 
 
 def get_procedure_modifiers(
@@ -264,8 +275,8 @@ def get_procedure_modifiers(
 
 
 def read_fun_def(
-    line: str, result: RESULT_sig = None, mod_flag: bool = False
-) -> tuple[Literal["fun"], FUN_sig] | None:
+    line: str, result: ResultSig = None, mod_flag: bool = False
+) -> tuple[Literal["fun"], FunSig] | None:
     """Attempt to read FUNCTION definition line
 
     To infer the `result` `type` and `name` the variable definition is called
@@ -308,15 +319,15 @@ def read_fun_def(
     trailing_line = trailing_line.strip()
     results_match = FRegex.RESULT.match(trailing_line)
     if result is None:
-        result = RESULT_sig()
+        result = ResultSig()
     if results_match:
         result.name = results_match.group(1).strip().lower()
-    return "fun", FUN_sig(name, args, keywords, mod_flag, result)
+    return "fun", FunSig(name, args, keywords, mod_flag, result)
 
 
 def read_sub_def(
     line: str, mod_flag: bool = False
-) -> tuple[Literal["sub"], SUB_info] | None:
+) -> tuple[Literal["sub"], SubInfo] | None:
     """Attempt to read a SUBROUTINE definition line
 
     Parameters
@@ -339,7 +350,7 @@ def read_sub_def(
     if name is None:
         return None
 
-    return "sub", SUB_info(name, args, keywords, mod_flag)
+    return "sub", SubInfo(name, args, keywords, mod_flag)
 
 
 def read_block_def(line: str) -> tuple[Literal["block"], str] | None:
@@ -431,7 +442,7 @@ def read_select_def(line: str):
             if select_default_match is None:
                 return None
             else:
-                return "select", SELECT_info(4, None, None)
+                return "select", SelectInfo(4, None, None)
         select_type = 3
         select_desc = select_type_match.group(1).upper()
         select_binding = select_type_match.group(2)
@@ -443,7 +454,7 @@ def read_select_def(line: str):
         elif select_word.lower().startswith("type"):
             select_type = 2
         select_binding = select_match.group(2)
-    return "select", SELECT_info(select_type, select_binding, select_desc)
+    return "select", SelectInfo(select_type, select_binding, select_desc)
 
 
 def read_type_def(line: str):
@@ -485,7 +496,7 @@ def read_type_def(line: str):
     else:
         return None
     #
-    return "typ", CLASS_info(name, parent, keywords)
+    return "typ", ClassInfo(name, parent, keywords)
 
 
 def read_enum_def(line: str):
@@ -528,11 +539,16 @@ def read_generic_def(line: str):
     if len(pro_out) == 0:
         return None
     #
-    return "gen", GEN_info(bound_name, pro_out, vis_flag)
+    return "gen", GenProcDefInfo(bound_name, pro_out, vis_flag)
 
 
 def read_mod_def(line: str):
-    """Attempt to read MODULE and MODULE PROCEDURE definition lines"""
+    """Attempt to read MODULE and MODULE PROCEDURE, MODULE FUNCTION definition lines"""
+    # Get all the keyword modifier mathces
+    keywords = re.findall(FRegex.SUB_MOD, line)
+    # remove modifiers from line
+    line = re.sub(FRegex.SUB_MOD, "", line)
+
     mod_match = FRegex.MOD.match(line)
     if mod_match is None:
         return None
@@ -547,15 +563,19 @@ def read_mod_def(line: str):
         return "int_pro", pro_names
     # Check for submodule definition
     trailing_line = line[mod_match.start(1) :]
+    # module procedure
     sub_res = read_sub_def(trailing_line, mod_flag=True)
     if sub_res is not None:
         return sub_res
+    # module function
     fun_res = read_var_def(trailing_line, fun_only=True)
     if fun_res is not None:
         fun_res[1].mod_flag = True
-        return fun_res[0], fun_res[1]
+        fun_res[1].keywords = keywords
+        return fun_res
     fun_res = read_fun_def(trailing_line, mod_flag=True)
     if fun_res is not None:
+        fun_res[1].keywords = keywords
         return fun_res
     return "mod", name
 
@@ -581,7 +601,7 @@ def read_submod_def(line: str):
     name_match = FRegex.WORD.search(trailing_line)
     if name_match:
         name = name_match.group(0).lower()
-    return "smod", SMOD_info(name, parent_name)
+    return "smod", SmodInfo(name, parent_name)
 
 
 def read_prog_def(line: str) -> tuple[Literal["prog"], str] | None:
@@ -592,7 +612,7 @@ def read_prog_def(line: str) -> tuple[Literal["prog"], str] | None:
     return "prog", prog_match.group(1)
 
 
-def read_int_def(line: str) -> tuple[Literal["int"], INT_info] | None:
+def read_int_def(line: str) -> tuple[Literal["int"], InterInfo] | None:
     """Attempt to read INTERFACE definition line"""
     int_match = FRegex.INT.match(line)
     if int_match is None:
@@ -601,13 +621,13 @@ def read_int_def(line: str) -> tuple[Literal["int"], INT_info] | None:
     int_name = int_match.group(2).lower()
     is_abstract = int_match.group(1) is not None
     if int_name == "":
-        return "int", INT_info(None, is_abstract)
+        return "int", InterInfo(None, is_abstract)
     if int_name == "assignment" or int_name == "operator":
-        return "int", INT_info(None, False)
-    return "int", INT_info(int_match.group(2), is_abstract)
+        return "int", InterInfo(None, False)
+    return "int", InterInfo(int_match.group(2), is_abstract)
 
 
-def read_use_stmt(line: str) -> tuple[Literal["use"], USE_info] | None:
+def read_use_stmt(line: str) -> tuple[Literal["use"], UseInfo] | None:
     """Attempt to read USE statement"""
     use_match = FRegex.USE.match(line)
     if use_match is None:
@@ -624,7 +644,7 @@ def read_use_stmt(line: str) -> tuple[Literal["use"], USE_info] | None:
             only_list.add(only_name)
             if len(only_split) == 2:
                 rename_map[only_name] = only_split[1].strip()
-    return "use", USE_info(use_mod, only_list, rename_map)
+    return "use", UseInfo(use_mod, only_list, rename_map)
 
 
 def read_imp_stmt(line: str) -> tuple[Literal["import"], list[str]] | None:
@@ -648,7 +668,7 @@ def read_inc_stmt(line: str) -> tuple[Literal["inc"], str] | None:
     return "inc", inc_path
 
 
-def read_vis_stmnt(line: str) -> tuple[Literal["vis"], VIS_info] | None:
+def read_vis_stmnt(line: str) -> tuple[Literal["vis"], VisInfo] | None:
     """Attempt to read PUBLIC/PRIVATE statement"""
     vis_match = FRegex.VIS.match(line)
     if vis_match is None:
@@ -659,7 +679,7 @@ def read_vis_stmnt(line: str) -> tuple[Literal["vis"], VIS_info] | None:
         vis_type = 1
     trailing_line = line[vis_match.end(0) :].split("!")[0]
     mod_words = FRegex.WORD.findall(trailing_line)
-    return "vis", VIS_info(vis_type, mod_words)
+    return "vis", VisInfo(vis_type, mod_words)
 
 
 def_tests = [
@@ -686,9 +706,7 @@ def read_vis_stmnt(line: str) -> tuple[Literal["vis"], VIS_info] | None:
 ]
 
 
-def find_external_type(
-    file_ast: fortran_ast, desc_string: str, name_stripped: str
-) -> bool:
+def find_external_type(file_ast: FortranAST, desc_string: str, name: str) -> bool:
     """Encountered a variable with EXTERNAL as its type
     Try and find an already defined variable with a
     NORMAL Fortran Type"""
@@ -697,7 +715,7 @@ def find_external_type(
     counter = 0
     # Definition without EXTERNAL has already been parsed
     for v in file_ast.variable_list:
-        if name_stripped == v.name:
+        if name == v.name:
             # If variable is already in external objs it has
             # been parsed correctly so exit
             if v in file_ast.external_objs:
@@ -713,9 +731,7 @@ def find_external_type(
         return False
 
 
-def find_external_attr(
-    file_ast: fortran_ast, name_stripped: str, new_var: fortran_var
-) -> bool:
+def find_external_attr(file_ast: FortranAST, name: str, new_var: Variable) -> bool:
     """Check if this NORMAL Fortran variable is in the external_objs with only
     ``EXTERNAL`` as its type. Used to detect seperated ``EXTERNAL`` declarations.
 
@@ -723,7 +739,7 @@ def find_external_attr(
     ----------
     file_ast : fortran_ast
         AST file
-    name_stripped : str
+    name : str
         Variable name, stripped
     new_var : fortran_var
         Fortran variable to check against
@@ -736,7 +752,7 @@ def find_external_attr(
     """
     counter = 0
     for v in file_ast.external_objs:
-        if v.name != name_stripped:
+        if v.name != name:
             continue
         if v.desc.upper() != "EXTERNAL":
             continue
@@ -757,10 +773,10 @@ def find_external_attr(
 
 
 def find_external(
-    file_ast: fortran_ast,
+    file_ast: FortranAST,
     desc_string: str,
-    name_stripped: str,
-    new_var: fortran_var,
+    name: str,
+    new_var: Variable,
 ) -> bool:
     """Find a procedure, function, subroutine, etc. that has been defined as
     ``EXTERNAL``. ``EXTERNAL``s are parsed as ``fortran_var``, since there is no
@@ -788,7 +804,7 @@ def find_external(
         AST
     desc_string : str
         Variable type e.g. ``REAL``, ``INTEGER``, ``EXTERNAL``
-    name_stripped : str
+    name : str
         Variable name
     new_var : fortran_var
         The line variable that we are attempting to match with an ``EXTERNAL``
@@ -800,15 +816,15 @@ def find_external(
         True if the variable is ``EXTERNAL`` and we manage to link it to the
         rest of its components, else False
     """
-    if find_external_type(file_ast, desc_string, name_stripped):
+    if find_external_type(file_ast, desc_string, name):
         return True
     elif desc_string.upper() != "EXTERNAL":
-        if find_external_attr(file_ast, name_stripped, new_var):
+        if find_external_attr(file_ast, name, new_var):
             return True
     return False
 
 
-class fortran_file:
+class FortranFile:
     def __init__(self, path: str = None, pp_suffixes: list = None):
         self.path: str = path
         self.contents_split: list[str] = []
@@ -817,7 +833,7 @@ def __init__(self, path: str = None, pp_suffixes: list = None):
         self.nLines: int = 0
         self.fixed: bool = False
         self.preproc: bool = False
-        self.ast: fortran_ast = None
+        self.ast: FortranAST = None
         self.hash: str = None
         if path:
             _, file_ext = os.path.splitext(os.path.basename(path))
@@ -827,9 +843,9 @@ def __init__(self, path: str = None, pp_suffixes: list = None):
                 self.preproc = file_ext == file_ext.upper()
         self.COMMENT_LINE_MATCH, self.DOC_COMMENT_MATCH = self.get_comment_regexs()
 
-    def copy(self) -> fortran_file:
+    def copy(self) -> FortranFile:
         """Copy content to new file object (does not copy objects)"""
-        copy_obj = fortran_file(self.path)
+        copy_obj = FortranFile(self.path)
         copy_obj.preproc = self.preproc
         copy_obj.fixed = self.fixed
         copy_obj.contents_pp = self.contents_pp
@@ -1191,7 +1207,7 @@ def parse(
         debug: bool = False,
         pp_defs: dict = None,
         include_dirs: set = None,
-    ) -> fortran_ast:
+    ) -> FortranAST:
         """Parse Fortran file contents of a fortran_file object and build an
         Abstract Syntax Tree (AST)
 
@@ -1221,7 +1237,7 @@ def parse(
             )
 
         # This is not necessarily the same as self.ast
-        file_ast = fortran_ast(self)
+        file_ast = FortranAST(self)
         if self.preproc:
             log.debug("=== PreProc Pass ===\n")
             pp_skips, pp_defines = self.preprocess(
@@ -1335,75 +1351,76 @@ def parse(
             if obj_type == "var":
                 if obj_info.var_names is None:
                     continue
-                desc_string = obj_info.var_type
                 link_name: str = None
                 procedure_def = False
-                if desc_string[:3] == "PRO":
+                if obj_info.var_type[:3] == "PRO":
                     if file_ast.current_scope.get_type() == INTERFACE_TYPE_ID:
                         for var_name in obj_info.var_names:
                             file_ast.add_int_member(var_name)
                         log.debug("%s !!! INTERFACE-PRO - Ln:%d", line.strip(), line_no)
                         continue
                     procedure_def = True
-                    link_name = get_paren_substring(desc_string)
+                    link_name = get_paren_substring(obj_info.var_type)
                 for var_name in obj_info.var_names:
+                    desc = obj_info.var_type
                     link_name: str = None
                     if var_name.find("=>") > -1:
                         name_split = var_name.split("=>")
-                        name_raw = name_split[0]
+                        name = name_split[0]
                         link_name = name_split[1].split("(")[0].strip()
                         if link_name.lower() == "null":
                             link_name = None
                     else:
-                        name_raw = var_name.split("=")[0]
+                        name = var_name.split("=")[0]
                     # Add dimension if specified
-                    key_tmp = obj_info.keywords[:]
-                    iparen = name_raw.find("(")
-                    if iparen == 0:
+                    # TODO: turn into function and add support for co-arrays i.e. [*]
+                    # Copy global keywords to the individual variable
+                    var_keywords: list[str] = obj_info.keywords[:]
+                    # The name starts with (
+                    if name.find("(") == 0:
                         continue
-                    elif iparen > 0:
-                        if name_raw[iparen - 1] == "*":
-                            iparen -= 1
-                            if desc_string.find("(") < 0:
-                                desc_string += f"*({get_paren_substring(name_raw)})"
-                        else:
-                            key_tmp.append(
-                                f"dimension({get_paren_substring(name_raw)})"
-                            )
-                        name_raw = name_raw[:iparen]
-                    name_stripped = name_raw.strip()
-                    keywords, keyword_info = map_keywords(key_tmp)
+                    name, dims = self.parse_imp_dim(name)
+                    name, char_len = self.parse_imp_char(name)
+                    if dims:
+                        var_keywords.append(dims)
+                    if char_len:
+                        desc += char_len
+
+                    name = name.strip()
+                    keywords, keyword_info = map_keywords(var_keywords)
+
                     if procedure_def:
-                        new_var = fortran_meth(
+                        new_var = Method(
                             file_ast,
                             line_no,
-                            name_stripped,
-                            desc_string,
+                            name,
+                            desc,
                             keywords,
                             keyword_info=keyword_info,
                             link_obj=link_name,
                         )
                     else:
-                        new_var = fortran_var(
+                        new_var = Variable(
                             file_ast,
                             line_no,
-                            name_stripped,
-                            desc_string,
+                            name,
+                            desc,
                             keywords,
                             keyword_info=keyword_info,
+                            # kind=obj_info.var_kind,
                             link_obj=link_name,
                         )
                         # If the object is fortran_var and a parameter include
                         #  the value in hover
                         if new_var.is_parameter():
-                            _, col = find_word_in_line(line, name_stripped)
+                            _, col = find_word_in_line(line, name)
                             match = FRegex.PARAMETER_VAL.match(line[col:])
                             if match:
                                 var = match.group(1).strip()
                                 new_var.set_parameter_val(var)
 
                         # Check if the "variable" is external and if so cycle
-                        if find_external(file_ast, desc_string, name_stripped, new_var):
+                        if find_external(file_ast, desc, name, new_var):
                             continue
 
                     # if not merge_external:
@@ -1411,25 +1428,25 @@ def parse(
                 log.debug("%s !!! VARIABLE - Ln:%d", line, line_no)
 
             elif obj_type == "mod":
-                new_mod = fortran_module(file_ast, line_no, obj_info)
+                new_mod = Module(file_ast, line_no, obj_info)
                 file_ast.add_scope(new_mod, FRegex.END_MOD)
                 log.debug("%s !!! MODULE - Ln:%d", line, line_no)
 
             elif obj_type == "smod":
-                new_smod = fortran_submodule(
+                new_smod = Submodule(
                     file_ast, line_no, obj_info.name, ancestor_name=obj_info.parent
                 )
                 file_ast.add_scope(new_smod, FRegex.END_SMOD)
                 log.debug("%s !!! SUBMODULE - Ln:%d", line, line_no)
 
             elif obj_type == "prog":
-                new_prog = fortran_program(file_ast, line_no, obj_info)
+                new_prog = Program(file_ast, line_no, obj_info)
                 file_ast.add_scope(new_prog, FRegex.END_PROG)
                 log.debug("%s !!! PROGRAM - Ln:%d", line, line_no)
 
             elif obj_type == "sub":
                 keywords, _ = map_keywords(obj_info.keywords)
-                new_sub = fortran_subroutine(
+                new_sub = Subroutine(
                     file_ast,
                     line_no,
                     obj_info.name,
@@ -1442,7 +1459,7 @@ def parse(
 
             elif obj_type == "fun":
                 keywords, _ = map_keywords(obj_info.keywords)
-                new_fun = fortran_function(
+                new_fun = Function(
                     file_ast,
                     line_no,
                     obj_info.name,
@@ -1457,7 +1474,7 @@ def parse(
                 # result() variable that is the function name
                 if obj_info.result.type:
                     keywords, keyword_info = map_keywords(obj_info.result.keywords)
-                    new_obj = fortran_var(
+                    new_obj = Variable(
                         file_ast,
                         line_no,
                         name=obj_info.result.name,
@@ -1473,7 +1490,7 @@ def parse(
                 if name is None:
                     counters["block"] += 1
                     name = f"#BLOCK{counters['block']}"
-                new_block = fortran_block(file_ast, line_no, name)
+                new_block = Block(file_ast, line_no, name)
                 file_ast.add_scope(new_block, FRegex.END_BLOCK, req_container=True)
                 log.debug("%s !!! BLOCK - Ln:%d", line, line_no)
 
@@ -1482,7 +1499,7 @@ def parse(
                 name = f"#DO{counters['do']}"
                 if obj_info != "":
                     block_id_stack.append(obj_info)
-                new_do = fortran_do(file_ast, line_no, name)
+                new_do = Do(file_ast, line_no, name)
                 file_ast.add_scope(new_do, FRegex.END_DO, req_container=True)
                 log.debug("%s !!! DO - Ln:%d", line, line_no)
 
@@ -1491,14 +1508,14 @@ def parse(
                 if not obj_info:
                     counters["do"] += 1
                     name = f"#WHERE{counters['do']}"
-                    new_do = fortran_where(file_ast, line_no, name)
+                    new_do = Where(file_ast, line_no, name)
                     file_ast.add_scope(new_do, FRegex.END_WHERE, req_container=True)
                 log.debug("%s !!! WHERE - Ln:%d", line, line_no)
 
             elif obj_type == "assoc":
                 counters["block"] += 1
                 name = f"#ASSOC{counters['block']}"
-                new_assoc = fortran_associate(file_ast, line_no, name)
+                new_assoc = Associate(file_ast, line_no, name)
                 file_ast.add_scope(new_assoc, FRegex.END_ASSOCIATE, req_container=True)
                 for bound_var in obj_info:
                     try:
@@ -1518,14 +1535,14 @@ def parse(
             elif obj_type == "if":
                 counters["if"] += 1
                 name = f"#IF{counters['if']}"
-                new_if = fortran_if(file_ast, line_no, name)
+                new_if = If(file_ast, line_no, name)
                 file_ast.add_scope(new_if, FRegex.END_IF, req_container=True)
                 log.debug("%s !!! IF - Ln:%d", line, line_no)
 
             elif obj_type == "select":
                 counters["select"] += 1
                 name = f"#SELECT{counters['select']}"
-                new_select = fortran_select(file_ast, line_no, name, obj_info)
+                new_select = Select(file_ast, line_no, name, obj_info)
                 file_ast.add_scope(new_select, FRegex.END_SELECT, req_container=True)
                 new_var = new_select.create_binding_variable(
                     file_ast,
@@ -1539,7 +1556,7 @@ def parse(
 
             elif obj_type == "typ":
                 keywords, _ = map_keywords(obj_info.keywords)
-                new_type = fortran_type(file_ast, line_no, obj_info.name, keywords)
+                new_type = Type(file_ast, line_no, obj_info.name, keywords)
                 if obj_info.parent is not None:
                     new_type.set_inherit(obj_info.parent)
                 file_ast.add_scope(new_type, FRegex.END_TYPED, req_container=True)
@@ -1548,7 +1565,7 @@ def parse(
             elif obj_type == "enum":
                 counters["block"] += 1
                 name = f"#ENUM{counters['block']}"
-                new_enum = fortran_enum(file_ast, line_no, name)
+                new_enum = Enum(file_ast, line_no, name)
                 file_ast.add_scope(new_enum, FRegex.END_ENUMD, req_container=True)
                 log.debug("%s !!! ENUM - Ln:%d", line, line_no)
 
@@ -1557,14 +1574,12 @@ def parse(
                 if name is None:
                     counters["interface"] += 1
                     name = f"#GEN_INT{counters['interface']}"
-                new_int = fortran_int(
-                    file_ast, line_no, name, abstract=obj_info.abstract
-                )
+                new_int = Interface(file_ast, line_no, name, abstract=obj_info.abstract)
                 file_ast.add_scope(new_int, FRegex.END_INT, req_container=True)
                 log.debug("%s !!! INTERFACE - Ln:%d", line, line_no)
 
             elif obj_type == "gen":
-                new_int = fortran_int(
+                new_int = Interface(
                     file_ast, line_no, obj_info.bound_name, abstract=False
                 )
                 new_int.set_visibility(obj_info.vis_flag)
@@ -1582,7 +1597,7 @@ def parse(
                         log.debug("%s !!! INTERFACE-PRO - Ln:%d", line, line_no)
 
                     elif file_ast.current_scope.get_type() == SUBMODULE_TYPE_ID:
-                        new_impl = fortran_scope(file_ast, line_no, obj_info[0])
+                        new_impl = Scope(file_ast, line_no, obj_info[0])
                         file_ast.add_scope(new_impl, FRegex.END_PRO)
                         log.debug("%s !!! INTERFACE-IMPL - Ln:%d", line, line_no)
 
@@ -1635,8 +1650,58 @@ def parse(
                     log.debug(f"{error['range']}: {error['message']}")
         return file_ast
 
+    def parse_imp_dim(self, line: str):
+        """Parse the implicit dimension of an array e.g.
+        var(3,4), var_name(size(val,1)*10)
+
+        Parameters
+        ----------
+        line : str
+            line containing variable name
+
+        Returns
+        -------
+        tuple[str, str]
+            truncated line, dimension string
+        """
+        m = re.compile(r"[ ]*\w+[ ]*(\()", re.I).match(line)
+        if not m:
+            return line, None
+        i = find_paren_match(line[m.end(1) :])
+        if i < 0:
+            return line, None  # triggers for autocomplete
+        dims = line[m.start(1) : m.end(1) + i + 1]
+        line = line[: m.start(1)] + line[m.end(1) + i + 1 :]
+        return line, f"dimension{dims}"
+
+    def parse_imp_char(self, line: str):
+        """Parse the implicit character length from a variable e.g.
+        var_name*10 or var_name*(10), var_name*(size(val, 1))
+
+        Parameters
+        ----------
+        line : str
+            line containing potential variable
+
+        Returns
+        -------
+        tuple[str, str]
+            truncated line, character length
+        """
+        match = re.compile(r"(\w+)[ ]*\*[ ]*(\d+|\()", re.I).match(line)
+        if not match:
+            return line, None
+        if match.group(2) == "(":
+            i = find_paren_match(line[match.end(2) :])
+            if i < 0:
+                return line, None  # triggers for autocomplete
+            char_len = line[match.start(2) : match.end(2) + i + 1]
+        elif match.group(2).isdigit():
+            char_len = match.group(2)
+        return match.group(1), f"*{char_len}"
+
     def parse_end_scope_word(
-        self, line: str, ln: int, file_ast: fortran_ast, match: re.Match
+        self, line: str, ln: int, file_ast: FortranAST, match: re.Match
     ) -> bool:
         """Parses END keyword marking the end of scopes
 
@@ -1684,7 +1749,7 @@ def parse_do_fixed_format(
         self,
         line: str,
         ln: int,
-        file_ast: fortran_ast,
+        file_ast: FortranAST,
         line_label: str,
         block_id_stack: list[str],
     ):
@@ -1702,7 +1767,7 @@ def parse_do_fixed_format(
                 return True
         return False
 
-    def parse_implicit(self, line: str, ln: int, file_ast: fortran_ast) -> bool:
+    def parse_implicit(self, line: str, ln: int, file_ast: FortranAST) -> bool:
         """Parse implicit statements from a line
 
         Parameters
@@ -1734,7 +1799,7 @@ def parse_implicit(self, line: str, ln: int, file_ast: fortran_ast) -> bool:
         log.debug("%s !!! IMPLICIT - Ln:%d", line, ln)
         return True
 
-    def parse_contains(self, line: str, ln: int, file_ast: fortran_ast) -> bool:
+    def parse_contains(self, line: str, ln: int, file_ast: FortranAST) -> bool:
         """Parse contain statements
 
         Parameters
@@ -1767,7 +1832,7 @@ def parse_contains(self, line: str, ln: int, file_ast: fortran_ast) -> bool:
         log.debug("%s !!! CONTAINS - Ln:%d", line, ln)
         return True
 
-    def parse_docs(self, line: str, ln: int, file_ast: fortran_ast, docs: list[str]):
+    def parse_docs(self, line: str, ln: int, file_ast: FortranAST, docs: list[str]):
         """Parse documentation stings of style Doxygen or FORD.
         Multiline docstrings are detected if the first comment starts with `!>`
         docstring continuations are detected with either `!>`, `!<` or `!!`
@@ -1789,7 +1854,7 @@ def format(docs: list[str]) -> str:
                 return f"!! {docs[0]}"
             return "!! " + "\n!! ".join(docs)
 
-        def add_line_comment(file_ast: fortran_ast, docs: list[str]):
+        def add_line_comment(file_ast: FortranAST, docs: list[str]):
             # Handle dangling comments from previous line
             if docs:
                 file_ast.add_doc(format(docs))
@@ -2082,7 +2147,7 @@ def replace_vars(line: str):
                     break
             if include_path is not None:
                 try:
-                    include_file = fortran_file(include_path)
+                    include_file = FortranFile(include_path)
                     err_string, _ = include_file.load_from_disk()
                     if err_string is None:
                         log.debug(f'\n!!! Parsing include file "{include_path}"')
diff --git a/fortls/regex_patterns.py b/fortls/regex_patterns.py
index ea28afee..be150100 100644
--- a/fortls/regex_patterns.py
+++ b/fortls/regex_patterns.py
@@ -16,39 +16,38 @@ class FortranRegularExpressions:
     CONTAINS: Pattern = compile(r"[ ]*(CONTAINS)[ ]*$", I)
     IMPLICIT: Pattern = compile(r"[ ]*IMPLICIT[ ]+([a-z]*)", I)
     SUB_MOD: Pattern = compile(r"[ ]*\b(PURE|IMPURE|ELEMENTAL|RECURSIVE)\b", I)
-    SUB: Pattern = compile(r"[ ]*SUBROUTINE[ ]+([a-z0-9_]+)", I)
+    SUB: Pattern = compile(r"[ ]*SUBROUTINE[ ]+(\w+)", I)
     END_SUB: Pattern = compile(r"SUBROUTINE", I)
-    FUN: Pattern = compile(r"[ ]*FUNCTION[ ]+([a-z0-9_]+)", I)
-    RESULT: Pattern = compile(r"RESULT[ ]*\(([a-z0-9_]*)\)", I)
+    FUN: Pattern = compile(r"[ ]*FUNCTION[ ]+(\w+)", I)
+    RESULT: Pattern = compile(r"RESULT[ ]*\((\w*)\)", I)
     END_FUN: Pattern = compile(r"FUNCTION", I)
-    MOD: Pattern = compile(r"[ ]*MODULE[ ]+([a-z0-9_]+)", I)
+    MOD: Pattern = compile(r"[ ]*MODULE[ ]+(\w+)", I)
     END_MOD: Pattern = compile(r"MODULE", I)
     SUBMOD: Pattern = compile(r"[ ]*SUBMODULE[ ]*\(", I)
     END_SMOD: Pattern = compile(r"SUBMODULE", I)
     END_PRO: Pattern = compile(r"(MODULE)?[ ]*PROCEDURE", I)
-    BLOCK: Pattern = compile(r"[ ]*([a-z_][a-z0-9_]*[ ]*:[ ]*)?BLOCK(?![a-z0-9_])", I)
+    BLOCK: Pattern = compile(r"[ ]*([a-z_]\w*[ ]*:[ ]*)?BLOCK(?!\w)", I)
     END_BLOCK: Pattern = compile(r"BLOCK", I)
-    DO: Pattern = compile(r"[ ]*(?:[a-z_][a-z0-9_]*[ ]*:[ ]*)?DO([ ]+[0-9]*|$)", I)
+    DO: Pattern = compile(r"[ ]*(?:[a-z_]\w*[ ]*:[ ]*)?DO([ ]+[0-9]*|$)", I)
     END_DO: Pattern = compile(r"DO", I)
     WHERE: Pattern = compile(r"[ ]*WHERE[ ]*\(", I)
     END_WHERE: Pattern = compile(r"WHERE", I)
-    IF: Pattern = compile(r"[ ]*(?:[a-z_][a-z0-9_]*[ ]*:[ ]*)?IF[ ]*\(", I)
+    IF: Pattern = compile(r"[ ]*(?:[a-z_]\w*[ ]*:[ ]*)?IF[ ]*\(", I)
     THEN: Pattern = compile(r"\)[ ]*THEN$", I)
     END_IF: Pattern = compile(r"IF", I)
     ASSOCIATE: Pattern = compile(r"[ ]*ASSOCIATE[ ]*\(", I)
     END_ASSOCIATE: Pattern = compile(r"ASSOCIATE", I)
     END_FIXED: Pattern = compile(r"[ ]*([0-9]*)[ ]*CONTINUE", I)
     SELECT: Pattern = compile(
-        r"[ ]*(?:[a-z_][a-z0-9_]*[ ]*:[ ]*)?SELECT[ ]*"
-        r"(CASE|TYPE)[ ]*\(([a-z0-9_=> ]*)",
+        r"[ ]*(?:[a-z_]\w*[ ]*:[ ]*)?SELECT[ ]*" r"(CASE|TYPE)[ ]*\(([\w=> ]*)",
         I,
     )
-    SELECT_TYPE: Pattern = compile(r"[ ]*(TYPE|CLASS)[ ]+IS[ ]*\(([a-z0-9_ ]*)", I)
+    SELECT_TYPE: Pattern = compile(r"[ ]*(TYPE|CLASS)[ ]+IS[ ]*\(([\w ]*)", I)
     SELECT_DEFAULT: Pattern = compile(r"[ ]*CLASS[ ]+DEFAULT", I)
     END_SELECT: Pattern = compile(r"SELECT", I)
-    PROG: Pattern = compile(r"[ ]*PROGRAM[ ]+([a-z0-9_]+)", I)
+    PROG: Pattern = compile(r"[ ]*PROGRAM[ ]+(\w+)", I)
     END_PROG: Pattern = compile(r"PROGRAM", I)
-    INT: Pattern = compile(r"[ ]*(ABSTRACT)?[ ]*INTERFACE[ ]*([a-z0-9_]*)", I)
+    INT: Pattern = compile(r"[ ]*(ABSTRACT)?[ ]*INTERFACE[ ]*(\w*)", I)
     END_INT: Pattern = compile(r"INTERFACE", I)
     END_WORD: Pattern = compile(
         r"[ ]*END[ ]*(DO|WHERE|IF|BLOCK|ASSOCIATE|SELECT"
@@ -57,7 +56,7 @@ class FortranRegularExpressions:
         I,
     )
     TYPE_DEF: Pattern = compile(r"[ ]*(TYPE)[, :]+", I)
-    EXTENDS: Pattern = compile(r"EXTENDS[ ]*\(([a-z0-9_]*)\)", I)
+    EXTENDS: Pattern = compile(r"EXTENDS[ ]*\((\w*)\)", I)
     GENERIC_PRO: Pattern = compile(
         r"[ ]*(GENERIC)[, ]*(PRIVATE|PUBLIC)?[ ]*::[ ]*[a-z]", I
     )
@@ -71,7 +70,7 @@ class FortranRegularExpressions:
         r"|EXTERNAL|CLASS|TYPE)",  # external :: variable is handled by this
         I,
     )
-    KIND_SPEC: Pattern = compile(r"[ ]*([*]?\([ ]*[a-z0-9_*:]|\*[ ]*[0-9:]*)", I)
+    KIND_SPEC: Pattern = compile(r"[ ]*([*]?\([ ]*[\w*:]|\*[ ]*[0-9:]*)", I)
     KEYWORD_LIST: Pattern = compile(
         r"[ ]*,[ ]*(PUBLIC|PRIVATE|ALLOCATABLE|"
         r"POINTER|TARGET|DIMENSION[ ]*\(|"
@@ -82,22 +81,22 @@ class FortranRegularExpressions:
     )
     PARAMETER_VAL: Pattern = compile(r"\w*[\s\&]*=[\s\&]*([\w\.\*\-\+\\\'\"]*)", I)
     TATTR_LIST: Pattern = compile(
-        r"[ ]*,[ ]*(PUBLIC|PRIVATE|ABSTRACT|EXTENDS\([a-z0-9_]*\))", I
+        r"[ ]*,[ ]*(PUBLIC|PRIVATE|ABSTRACT|EXTENDS\(\w*\))", I
     )
     VIS: Pattern = compile(r"[ ]*\b(PUBLIC|PRIVATE)\b", I)
-    WORD: Pattern = compile(r"[a-z_][a-z0-9_]*", I)
+    WORD: Pattern = compile(r"[a-z_]\w*", I)
     NUMBER: Pattern = compile(
         r"[\+\-]?(\b\d+\.?\d*|\.\d+)(_\w+|d[\+\-]?\d+|e[\+\-]?\d+(_\w+)?)?(?!\w)",
         I,
     )
     LOGICAL: Pattern = compile(r".true.|.false.", I)
-    SUB_PAREN: Pattern = compile(r"\([a-z0-9_, ]*\)", I)
-    # KIND_SPEC_MATCH: Pattern = compile(r"\([a-z0-9_, =*]*\)", I)
+    SUB_PAREN: Pattern = compile(r"\([\w, ]*\)", I)
+    # KIND_SPEC_MATCH: Pattern = compile(r"\([\w, =*]*\)", I)
 
     SQ_STRING: Pattern = compile(r"\'[^\']*\'", I)
     DQ_STRING: Pattern = compile(r"\"[^\"]*\"", I)
     LINE_LABEL: Pattern = compile(r"[ ]*([0-9]+)[ ]+", I)
-    NON_DEF: Pattern = compile(r"[ ]*(CALL[ ]+[a-z_]|[a-z_][a-z0-9_%]*[ ]*=)", I)
+    NON_DEF: Pattern = compile(r"[ ]*(CALL[ ]+[a-z_]|[a-z_][\w%]*[ ]*=)", I)
     # Fixed format matching rules
     FIXED_COMMENT: Pattern = compile(r"([!cd*])", I)
     FIXED_CONT: Pattern = compile(r"( {5}[\S])")
@@ -110,14 +109,14 @@ class FortranRegularExpressions:
     FREE_OPENMP: Pattern = compile(r"[ ]*!\$OMP", I)
     FREE_FORMAT_TEST: Pattern = compile(r"[ ]{1,4}[a-z]", I)
     # Preprocessor matching rules
-    DEFINED: Pattern = compile(r"defined[ ]*\(?[ ]*([a-z_][a-z0-9_]*)[ ]*\)?", I)
+    DEFINED: Pattern = compile(r"defined[ ]*\(?[ ]*([a-z_]\w*)[ ]*\)?", I)
     PP_REGEX: Pattern = compile(r"#(if |ifdef|ifndef|else|elif|endif)")
     PP_DEF: Pattern = compile(r"#(define|undef)[ ]*([\w]+)(\((\w+(,[ ]*)?)+\))?", I)
-    PP_DEF_TEST: Pattern = compile(r"(![ ]*)?defined[ ]*\([ ]*([a-z0-9_]*)[ ]*\)$", I)
-    PP_INCLUDE: Pattern = compile(r"#include[ ]*([\"a-z0-9_\.]*)", I)
+    PP_DEF_TEST: Pattern = compile(r"(![ ]*)?defined[ ]*\([ ]*(\w*)[ ]*\)$", I)
+    PP_INCLUDE: Pattern = compile(r"#include[ ]*([\"\w\.]*)", I)
     PP_ANY: Pattern = compile(r"(^#:?\w+)")
     # Context matching rules
-    CALL: Pattern = compile(r"[ ]*CALL[ ]+[a-z0-9_%]*$", I)
+    CALL: Pattern = compile(r"[ ]*CALL[ ]+[\w%]*$", I)
     INT_STMNT: Pattern = compile(r"^[ ]*[a-z]*$", I)
     TYPE_STMNT: Pattern = compile(r"[ ]*(TYPE|CLASS)[ ]*(IS)?[ ]*$", I)
     PROCEDURE_STMNT: Pattern = compile(r"[ ]*(PROCEDURE)[ ]*$", I)
diff --git a/pyproject.toml b/pyproject.toml
index 4aa5b93d..69446d19 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -9,3 +9,11 @@ build-backend = "setuptools.build_meta"
 
 [tool.setuptools_scm]
 write_to = "fortls/_version.py"
+
+[tool.isort]
+profile = "black"
+
+[tool.pytest.ini_options]
+minversion = "7.0"
+addopts = "-v --cov=fortls --cov-report=html --cov-report=xml --cov-context=test"
+testpaths = ["fortls", "test"]
diff --git a/setup.cfg b/setup.cfg
index 03b6d826..6639d49e 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,6 +1,6 @@
 [metadata]
 name = fortls
-url = https://github.com/gnikit/fortls
+url = https://gnikit.github.io/fortls
 author = Giannis Nikiteas
 author_email = giannis.nikiteas@gmail.com
 description = fortls - Fortran Language Server
@@ -19,6 +19,7 @@ classifiers =
     Programming Language :: Python :: 3.8
     Programming Language :: Python :: 3.9
     Programming Language :: Python :: 3.10
+    Programming Language :: Python :: 3.11
     Programming Language :: Fortran
     Operating System :: Microsoft :: Windows
     Operating System :: POSIX
@@ -31,14 +32,17 @@ keywords =
     lsp
     fortls
 project_urls =
+    Donate = https://github.com/sponsors/gnikit
     Documentation = https://gnikit.github.io/fortls
-    Repository = https://github.com/gnikit/fortls
+    Changes = https://github.com/gnikit/fortls/blob/master/CHANGELOG.md
     Tracker = https://github.com/gnikit/fortls/issues
+    Source Code = https://github.com/gnikit/fortls
 
 [options]
 packages = find:
 python_requires = >= 3.7
 install_requires =
+    json5
     packaging
     importlib-metadata; python_version < "3.8"
     typing-extensions; python_version < "3.8"
@@ -56,12 +60,17 @@ dev =
     pytest-cov >= 2.12.1
     black
     isort
+    pre-commit
 docs =
     sphinx >= 4.0.0
     sphinx_rtd_theme
     sphinx-argparse
     sphinx-autodoc-typehints
+    sphinx_design
+    sphinx-copybutton
+    furo
     myst-parser
+    sphinx-sitemap
 
 [flake8]
 max-line-length = 88


Test patch:
diff --git a/test/test_interface.py b/test/test_interface.py
index 0d205d8d..0e550e2d 100644
--- a/test/test_interface.py
+++ b/test/test_interface.py
@@ -4,9 +4,9 @@
 
 sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
 
-from fortls.interface import commandline_args  # noqa: E402
+from fortls.interface import cli  # noqa: E402
 
-parser = commandline_args("fortls")
+parser = cli("fortls")
 
 
 def test_command_line_general_options():
@@ -28,10 +28,10 @@ def test_command_line_file_parsing_options():
         "--source_dirs tmp ./local /usr/include/** --incl_suffixes .FF .fpc .h f20"
         " --excl_suffixes _tmp.f90 _h5hut_tests.F90 --excl_paths exclude tests".split()
     )
-    assert args.source_dirs == set(["tmp", "./local", "/usr/include/**"])
-    assert args.incl_suffixes == [".FF", ".fpc", ".h", "f20"]
-    assert args.excl_suffixes == set(["_tmp.f90", "_h5hut_tests.F90"])
-    assert args.excl_paths == set(["exclude", "tests"])
+    assert args.source_dirs == {"tmp", "./local", "/usr/include/**"}
+    assert args.incl_suffixes == {".FF", ".fpc", ".h", "f20"}
+    assert args.excl_suffixes == {"_tmp.f90", "_h5hut_tests.F90"}
+    assert args.excl_paths == {"exclude", "tests"}
 
 
 def test_command_line_autocomplete_options():
@@ -69,7 +69,7 @@ def test_command_line_preprocessor_options():
         ' {"HAVE_PETSC":"","HAVE_ZOLTAN":"","Mat":"type(tMat)"}'.split()
     )
     assert args.pp_suffixes == [".h", ".fh"]
-    assert args.include_dirs == set(["/usr/include/**", "./local/incl"])
+    assert args.include_dirs == {"/usr/include/**", "./local/incl"}
     assert args.pp_defs == {"HAVE_PETSC": "", "HAVE_ZOLTAN": "", "Mat": "type(tMat)"}
 
 
@@ -87,7 +87,7 @@ def unittest_server_init():
     from fortls.langserver import LangServer
 
     root = (Path(__file__).parent / "test_source").resolve()
-    parser = commandline_args("fortls")
+    parser = cli("fortls")
     args = parser.parse_args("-c f90_config.json".split())
 
     server = LangServer(None, vars(args))
@@ -109,12 +109,10 @@ def test_config_file_general_options():
 def test_config_file_dir_parsing_options():
     server, r = unittest_server_init()
     # File parsing
-    assert server.source_dirs == set(
-        [f'{r/"subdir"}', f'{r/"pp"}', f'{r/"pp"/"include"}']
-    )
-    assert server.incl_suffixes == [".FF", ".fpc", ".h", "f20"]
-    assert server.excl_suffixes == set(["_tmp.f90", "_h5hut_tests.F90"])
-    assert server.excl_paths == set([f'{r/"excldir"}', f'{r/"hover"}'])
+    assert server.source_dirs == {"pp/**", "subdir"}
+    assert server.incl_suffixes == {".FF", ".fpc", ".h", "f20"}
+    assert server.excl_suffixes == {"_tmp.f90", "_h5hut_tests.F90"}
+    assert server.excl_paths == {"excldir", "hover/**"}
 
 
 def test_config_file_autocomplete_options():
@@ -146,7 +144,7 @@ def test_config_file_preprocessor_options():
     server, root = unittest_server_init()
     # Preprocessor options
     assert server.pp_suffixes == [".h", ".fh"]
-    assert server.include_dirs == set([f'{root/"include"}'])
+    assert server.include_dirs == {"./include/**"}
     assert server.pp_defs == {
         "HAVE_PETSC": "",
         "HAVE_ZOLTAN": "",
@@ -167,11 +165,12 @@ def test_config_file_codeactions_options():
 
 
 def test_version_update_pypi():
+    from packaging import version
+
     from fortls.jsonrpc import JSONRPC2Connection, ReadWriter
     from fortls.langserver import LangServer
-    from packaging import version
 
-    parser = commandline_args("fortls")
+    parser = cli("fortls")
     args = parser.parse_args("-c f90_config.json".split())
     args = vars(args)
     args["disable_autoupdate"] = False
diff --git a/test/test_server.py b/test/test_server.py
index 2a482e08..639ef427 100644
--- a/test/test_server.py
+++ b/test/test_server.py
@@ -177,6 +177,7 @@ def check_return(result_array):
         objs = (
             ["test", 6, 7],
             ["test_abstract", 2, 0],
+            ["test_associate_block", 2, 0],
             ["test_free", 2, 0],
             ["test_gen_type", 5, 1],
             ["test_generic", 2, 0],
diff --git a/test/test_server_diagnostics.py b/test/test_server_diagnostics.py
index caa738f4..d6da6da4 100644
--- a/test/test_server_diagnostics.py
+++ b/test/test_server_diagnostics.py
@@ -262,6 +262,14 @@ def test_contains():
             "message": "CONTAINS statement without enclosing scope",
             "severity": 1,
         },
+        {
+            "range": {
+                "start": {"line": 8, "character": 0},
+                "end": {"line": 8, "character": 0},
+            },
+            "message": "Subroutine/Function definition before CONTAINS statement",
+            "severity": 1,
+        },
     ]
 
 
@@ -277,10 +285,122 @@ def test_visibility():
     assert results[1]["diagnostics"] == [
         {
             "range": {
-                "start": {"line": 3, "character": 0},
-                "end": {"line": 3, "character": 0},
+                "start": {"line": 5, "character": 0},
+                "end": {"line": 5, "character": 0},
             },
             "message": "Visibility statement without enclosing scope",
             "severity": 1,
+        },
+        {
+            "range": {
+                "start": {"line": 1, "character": 8},
+                "end": {"line": 1, "character": 26},
+            },
+            "message": 'Module "nonexisting_module" not found in project',
+            "severity": 3,
+        },
+        {
+            "range": {
+                "start": {"line": 3, "character": 8},
+                "end": {"line": 3, "character": 11},
+            },
+            "message": 'Module "mod" not found in project',
+            "severity": 3,
+        },
+        {
+            "range": {
+                "start": {"line": 2, "character": 4},
+                "end": {"line": 2, "character": 12},
+            },
+            "message": "USE statements after IMPLICIT statement",
+            "severity": 1,
+        },
+    ]
+
+
+def test_import():
+    string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)})
+    # Test module procedure in submodules importing scopes
+    file_path = str(test_dir / "diag" / "test_import.f90")
+    string += write_rpc_notification(
+        "textDocument/didOpen", {"textDocument": {"uri": file_path}}
+    )
+    errcode, results = run_request(string)
+    assert errcode == 0
+    assert results[1]["diagnostics"] == [
+        {
+            "range": {
+                "start": {"line": 1, "character": 0},
+                "end": {"line": 1, "character": 0},
+            },
+            "message": "IMPORT statement outside of interface",
+            "severity": 1,
         }
     ]
+
+
+def test_variable():
+    string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)})
+    # Test module procedure in submodules importing scopes
+    file_path = str(test_dir / "diag" / "test_variable.f90")
+    string += write_rpc_notification(
+        "textDocument/didOpen", {"textDocument": {"uri": file_path}}
+    )
+    errcode, results = run_request(string)
+    assert errcode == 0
+    assert results[1]["diagnostics"] == [
+        {
+            "range": {
+                "start": {"line": 4, "character": 19},
+                "end": {"line": 4, "character": 22},
+            },
+            "message": 'Variable "val" masks variable in parent scope',
+            "severity": 2,
+            "relatedInformation": [
+                {
+                    "location": {
+                        "uri": path_to_uri(str(file_path)),
+                        "range": {
+                            "start": {"line": 1, "character": 0},
+                            "end": {"line": 1, "character": 0},
+                        },
+                    },
+                    "message": "First declaration",
+                }
+            ],
+        }
+    ]
+
+
+def test_function():
+    string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)})
+    # Test module procedure in submodules importing scopes
+    file_path = str(test_dir / "diag" / "test_function.f90")
+    string += write_rpc_notification(
+        "textDocument/didOpen", {"textDocument": {"uri": file_path}}
+    )
+    errcode, results = run_request(string)
+    assert errcode == 0
+    assert results[1]["diagnostics"] == [
+        {
+            "range": {
+                "start": {"line": 3, "character": 31},
+                "end": {"line": 3, "character": 34},
+            },
+            "message": 'Variable "bar" with INTENT keyword not found in argument list',
+            "severity": 1,
+        }
+    ]
+
+
+def test_submodule_scopes():
+    """Test that submodule procedures and functions with modifier keywords are correctly
+    parsed and their scopes correctly closed."""
+    string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir / "diag")})
+    file_path = str(test_dir / "diag" / "test_scope_overreach.f90")
+    string += write_rpc_notification(
+        "textDocument/didOpen", {"textDocument": {"uri": file_path}}
+    )
+    errcode, results = run_request(string, ["-n", "1"])
+    assert errcode == 0
+    assert results[1]["diagnostics"] == []
diff --git a/test/test_server_documentation.py b/test/test_server_documentation.py
index 0682d715..634f6d99 100644
--- a/test/test_server_documentation.py
+++ b/test/test_server_documentation.py
@@ -88,6 +88,17 @@ def test_doc_type_bound_procedure_sub_implementation():
     check_return(results[1], ((1, "!! Doc 4"), (4, " !! Doc 5")))
 
 
+def test_doc_variable():
+    string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)})
+    file_path = test_dir / "subdir" / "test_free.f90"
+    # n !! Doc 5
+    # Test that a variable can carry over documentation
+    string += hover_request(file_path, 37, 26)
+    errcode, results = run_request(string)
+    assert errcode == 0
+    check_return(results[1], ((1, " !! Doc 5"),))
+
+
 def test_doc_overwrite_type_bound_procedure_fun():
     string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)})
     file_path = test_dir / "subdir" / "test_free.f90"
diff --git a/test/test_server_hover.py b/test/test_server_hover.py
index 7b9b956f..f1a573c9 100644
--- a/test/test_server_hover.py
+++ b/test/test_server_hover.py
@@ -23,7 +23,7 @@ def test_hover_abstract_int_procedure():
     string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)})
     file_path = test_dir / "subdir" / "test_abstract.f90"
     string += hover_req(file_path, 7, 30)
-    errcode, results = run_request(string, fortls_args=["--sort_keywords"])
+    errcode, results = run_request(string, fortls_args=["--sort_keywords", "-n1"])
     assert errcode == 0
     ref_results = [
         """SUBROUTINE test(a, b)
@@ -315,3 +315,130 @@ def test_hover_interface_as_argument():
  REAL :: arg3""",
     )
     validate_hover(results, ref_results)
+
+
+def test_hover_block():
+    string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir / "hover")})
+    file_path = test_dir / "hover" / "associate_block.f90"
+    string += hover_req(file_path, 4, 17)
+    string += hover_req(file_path, 4, 20)
+    # string += hover_req(file_path, 10, 11)    # slice of array
+    errcode, results = run_request(string, fortls_args=["--sort_keywords", "-n", "1"])
+    assert errcode == 0
+    ref_results = ["REAL, DIMENSION(5)", "REAL"]
+    validate_hover(results, ref_results)
+
+
+def test_hover_submodule_procedure():
+    """Test that submodule procedures and functions with modifier keywords
+    are correctly displayed when hovering.
+    """
+    string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir / "diag")})
+    file_path = test_dir / "diag" / "test_scope_overreach.f90"
+    string += hover_req(file_path, 18, 37)
+    string += hover_req(file_path, 23, 37)
+    errcode, results = run_request(string, fortls_args=["-n", "1"])
+    assert errcode == 0
+    ref_results = [
+        """PURE RECURSIVE FUNCTION foo_sp(x) RESULT(fi)
+ REAL(sp), INTENT(IN) :: x
+ REAL(sp) :: fi""",
+        """PURE RECURSIVE FUNCTION foo_dp(x) RESULT(fi)
+ REAL(dp), INTENT(IN) :: x
+ REAL(dp) :: fi""",
+    ]
+    validate_hover(results, ref_results)
+
+
+def test_var_type_kinds():
+    string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir / "parse")})
+    file_path = test_dir / "parse" / "test_kinds_and_dims.f90"
+    string += hover_req(file_path, 2, 24)
+    string += hover_req(file_path, 2, 27)
+    string += hover_req(file_path, 3, 15)
+    string += hover_req(file_path, 3, 19)
+    string += hover_req(file_path, 4, 20)
+    string += hover_req(file_path, 4, 25)
+    string += hover_req(file_path, 5, 23)
+    string += hover_req(file_path, 6, 25)
+    errcode, results = run_request(string, fortls_args=["-n", "1"])
+    assert errcode == 0
+    ref_results = [
+        "INTEGER(kind=4)",
+        "INTEGER(kind=4), DIMENSION(3,4)",
+        "INTEGER*8",
+        "INTEGER*8, DIMENSION(3,4)",
+        "INTEGER(8)",
+        "INTEGER(8), DIMENSION(3,4)",
+        "REAL(kind=r15)",
+        "REAL(kind(0.d0))",
+    ]
+    validate_hover(results, ref_results)
+
+
+def test_kind_function_result():
+    string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir / "parse")})
+    file_path = test_dir / "parse" / "test_kinds_and_dims.f90"
+    string += hover_req(file_path, 9, 18)
+    string += hover_req(file_path, 14, 25)
+    errcode, results = run_request(string, fortls_args=["-n", "1"])
+    assert errcode == 0
+    ref_results = [
+        """FUNCTION foo(val) RESULT(r)
+ REAL(8), INTENT(IN) :: val
+ REAL*8 :: r""",
+        """FUNCTION phi(val) RESULT(r)
+ REAL(8), INTENT(IN) :: val
+ REAL(kind=8) :: r""",
+    ]
+    validate_hover(results, ref_results)
+
+
+def test_var_type_asterisk():
+    string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir / "parse")})
+    file_path = test_dir / "parse" / "test_kinds_and_dims.f90"
+    string += hover_req(file_path, 2 + 19, 18)
+    string += hover_req(file_path, 2 + 19, 21)
+    string += hover_req(file_path, 2 + 19, 29)
+    string += hover_req(file_path, 3 + 19, 21)
+    string += hover_req(file_path, 4 + 19, 17)
+    string += hover_req(file_path, 5 + 19, 23)
+    errcode, results = run_request(string, fortls_args=["-n", "1"])
+    assert errcode == 0
+    ref_results = [
+        "CHARACTER*17",
+        "CHARACTER*17, DIMENSION(3,4)",
+        "CHARACTER*17, DIMENSION(9)",
+        "CHARACTER*(6+3)",
+        "CHARACTER*10, DIMENSION(3,4)",
+        "CHARACTER*(LEN(B)), DIMENSION(3,4)",
+    ]
+    validate_hover(results, ref_results)
+
+
+def test_var_name_asterisk():
+    string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir / "parse")})
+    file_path = test_dir / "parse" / "test_kinds_and_dims.f90"
+    string += hover_req(file_path, 26, 15)
+    string += hover_req(file_path, 26, 22)
+    string += hover_req(file_path, 26, 34)
+    string += hover_req(file_path, 27, 15)
+    string += hover_req(file_path, 28, 15)
+    string += hover_req(file_path, 29, 15)
+    string += hover_req(file_path, 31, 24)
+    string += hover_req(file_path, 32, 32)
+    # string += hover_req(file_path, 33, 32)  # FIXME: this is not displayed correctly
+    errcode, results = run_request(string, fortls_args=["-n", "1"])
+    assert errcode == 0
+    ref_results = [
+        "CHARACTER*17",
+        "CHARACTER*17, DIMENSION(3,4)",
+        "CHARACTER*17, DIMENSION(9)",
+        "CHARACTER*(6+3)",
+        "CHARACTER*(LEN(A))",
+        "CHARACTER*10, DIMENSION(*)",
+        "CHARACTER(LEN=200)",
+        "CHARACTER(KIND=4, LEN=200), DIMENSION(3,4)",
+        # "CHARACTER(KIND=4, LEN=100), DIMENSION(3,4)",
+    ]
+    validate_hover(results, ref_results)
diff --git a/test/test_server_messages.py b/test/test_server_messages.py
new file mode 100644
index 00000000..ad26b333
--- /dev/null
+++ b/test/test_server_messages.py
@@ -0,0 +1,30 @@
+from setup_tests import run_request, test_dir, write_rpc_request
+
+
+def check_msg(ref, res):
+    assert ref["type"] == res["type"]
+    assert ref["message"] == res["message"]
+
+
+# def test_config_file_non_existent():
+#     string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)})
+#     errcode, results = run_request(string, ["-c", "fake.json"])
+#
+#     ref = {"type": 1, "message": "Configuration file 'fake.json' not found"}
+#     assert errcode == 0
+#     check_msg(ref, results[0])
+
+
+def test_config_file_non_existent_options():
+    string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)})
+    errcode, results = run_request(string, ["-c", "wrong_syntax.json"])
+
+    ref = {
+        "type": 1,
+        "message": (
+            'Error: "<string>:2 Unexpected "," at column 18" while reading'
+            ' "wrong_syntax.json" Configuration file'
+        ),
+    }
+    assert errcode == 0
+    check_msg(ref, results[0])
diff --git a/test/test_source/.fortls b/test/test_source/.fortls
index 06f03f00..868923f9 100644
--- a/test/test_source/.fortls
+++ b/test/test_source/.fortls
@@ -1,12 +1,15 @@
 {
+  // Directories to be scanned for source files
   "source_dirs": [
     "**/"
   ],
+  // These are regular expressions, files and paths that can be ignored
   "excl_paths": [
     "excldir/**",
     "./diag/",
     "docs",
-    "rename"
+    "rename",
+    "parse"
   ]
 
-}
\ No newline at end of file
+}
diff --git a/test/test_source/diag/test_contains.f90 b/test/test_source/diag/test_contains.f90
index 27aacf6e..0922ef38 100644
--- a/test/test_source/diag/test_contains.f90
+++ b/test/test_source/diag/test_contains.f90
@@ -3,4 +3,10 @@ program test_contains
     contains
     contains
 end program test_contains
-contains
\ No newline at end of file
+contains
+
+module test_contains2
+    subroutine foo()    ! Err: before contains
+    end subroutine
+    contains
+end module test_contains2
diff --git a/test/test_source/diag/test_external.f90 b/test/test_source/diag/test_external.f90
index f7d91abe..1634920a 100644
--- a/test/test_source/diag/test_external.f90
+++ b/test/test_source/diag/test_external.f90
@@ -8,4 +8,4 @@ program test_external
     EXTERNAL     VAR_B   ! throw error
     REAL         VAR_A   ! throw error
     EXTERNAL     VAR_C
-end program test_external
\ No newline at end of file
+end program test_external
diff --git a/test/test_source/diag/test_function.f90 b/test/test_source/diag/test_function.f90
new file mode 100644
index 00000000..97a1417a
--- /dev/null
+++ b/test/test_source/diag/test_function.f90
@@ -0,0 +1,6 @@
+module test_functions
+    contains
+    subroutine foo(val)
+        integer, intent(in) :: bar
+    end subroutine
+end module test_functions
diff --git a/test/test_source/diag/test_implicit_none.f90 b/test/test_source/diag/test_implicit_none.f90
index d45309ed..0c9af40e 100644
--- a/test/test_source/diag/test_implicit_none.f90
+++ b/test/test_source/diag/test_implicit_none.f90
@@ -1,5 +1,5 @@
 program test_imp
     implicit none
-    
+
 end program test_imp
 implicit none
diff --git a/test/test_source/diag/test_import.f90 b/test/test_source/diag/test_import.f90
new file mode 100644
index 00000000..332648b8
--- /dev/null
+++ b/test/test_source/diag/test_import.f90
@@ -0,0 +1,3 @@
+program test_diagnostic_import
+    import some
+end program test_diagnostic_import
diff --git a/test/test_source/diag/test_lines.f90 b/test/test_source/diag/test_lines.f90
index b7838893..01711bc0 100644
--- a/test/test_source/diag/test_lines.f90
+++ b/test/test_source/diag/test_lines.f90
@@ -2,4 +2,4 @@ program test_lines
     implicit none
     character(len=123) :: val = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam sodales imperdiet dolor, sit amet venenatis magna dictum id."
     ! Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam sodales imperdiet dolor, sit amet venenatis magna dictum id.
-end program test_lines
\ No newline at end of file
+end program test_lines
diff --git a/test/test_source/diag/test_scope_end_name_var.f90 b/test/test_source/diag/test_scope_end_name_var.f90
index de5790cb..d5692bec 100644
--- a/test/test_source/diag/test_scope_end_name_var.f90
+++ b/test/test_source/diag/test_scope_end_name_var.f90
@@ -4,4 +4,4 @@ program scope_end_named_var
     if (.true.) then
         end = 10
     end if
-end program scope_end_named_var
\ No newline at end of file
+end program scope_end_named_var
diff --git a/test/test_source/diag/test_scope_overreach.f90 b/test/test_source/diag/test_scope_overreach.f90
new file mode 100644
index 00000000..3604e6b5
--- /dev/null
+++ b/test/test_source/diag/test_scope_overreach.f90
@@ -0,0 +1,28 @@
+module m
+    interface
+        module subroutine sub(arg)
+            integer :: arg
+        end subroutine
+    end interface
+end module m
+
+submodule (m) n
+
+    use, intrinsic :: iso_fortran_env, only: int8, int16, int32, int64
+    implicit none
+
+    integer, parameter :: sp = selected_real_kind(6)
+    integer, parameter :: dp = selected_real_kind(15)
+
+contains
+
+    pure recursive module function foo_sp(x) result(fi)
+        real(sp), intent(in) :: x
+        real(sp) :: fi
+    end function foo_sp
+
+    pure recursive module function foo_dp(x) result(fi)
+        real(dp), intent(in) :: x
+        real(dp) :: fi
+    end function foo_dp
+end submodule n
diff --git a/test/test_source/diag/test_semicolon.f90 b/test/test_source/diag/test_semicolon.f90
index 146f624d..72318bcf 100644
--- a/test/test_source/diag/test_semicolon.f90
+++ b/test/test_source/diag/test_semicolon.f90
@@ -16,4 +16,4 @@ program test_semicolon
     print*, "one";
     print*, str2
     print*, a; print*, p; ! a; comment; that;contains; semi-colons
-end program test_semicolon
\ No newline at end of file
+end program test_semicolon
diff --git a/test/test_source/diag/test_variable.f90 b/test/test_source/diag/test_variable.f90
new file mode 100644
index 00000000..3460188f
--- /dev/null
+++ b/test/test_source/diag/test_variable.f90
@@ -0,0 +1,7 @@
+program test_variable
+    integer :: val
+    contains
+    subroutine foo()
+        integer :: val  ! Warn: shadows parent
+    end subroutine
+end program test_variable
diff --git a/test/test_source/diag/test_visibility.f90 b/test/test_source/diag/test_visibility.f90
index 03f2064f..a3407d25 100644
--- a/test/test_source/diag/test_visibility.f90
+++ b/test/test_source/diag/test_visibility.f90
@@ -1,4 +1,6 @@
 program test_visibility
+    use nonexisting_module  ! Info: missing module
     implicit none
+    use mod
 end program test_visibility
-public
\ No newline at end of file
+public
diff --git a/test/test_source/docs/test_doxygen.f90 b/test/test_source/docs/test_doxygen.f90
index aa242491..3d96cc49 100644
--- a/test/test_source/docs/test_doxygen.f90
+++ b/test/test_source/docs/test_doxygen.f90
@@ -1,6 +1,6 @@
 module test_doxygen
     implicit none
-    
+
 contains
 
     !> @brief inserts a value into an ordered array
@@ -32,4 +32,4 @@ pure function calc_angle(v, w) result (theta)
         real :: theta
     end function calc_angle
 
-end module test_doxygen
\ No newline at end of file
+end module test_doxygen
diff --git a/test/test_source/docs/test_ford.f90 b/test/test_source/docs/test_ford.f90
index b5424d41..452a7e4a 100644
--- a/test/test_source/docs/test_ford.f90
+++ b/test/test_source/docs/test_ford.f90
@@ -19,4 +19,4 @@ subroutine feed_pets(cats, dogs, food, angry)
 
         return
     end subroutine feed_pets
-end module test_fortd
\ No newline at end of file
+end module test_fortd
diff --git a/test/test_source/excldir/sub1/tmp.f90 b/test/test_source/excldir/sub1/tmp.f90
index e3bb39ba..9a2422a5 100644
--- a/test/test_source/excldir/sub1/tmp.f90
+++ b/test/test_source/excldir/sub1/tmp.f90
@@ -9,7 +9,7 @@ module oumods
        FINAL :: del_ex_type
        PROCEDURE :: sub => ex_sub
      END TYPE ex_type
-     
+
 contains
   subroutine zI12(t,c,alpha,beta,r)
     complex(c_double_complex) c,r,    x,y,z
@@ -19,4 +19,4 @@ subroutine zI12(t,c,alpha,beta,r)
           & /2._c_double )) / (c / exp((z + cmplx(0._c_double,3.14159265358979324_c_double,kind(1._c_double)))/2._c_double))
      r = beta*r+alpha*((t*y - x)/c)
   end subroutine
-end module
\ No newline at end of file
+end module
diff --git a/test/test_source/hover/associate_block.f90 b/test/test_source/hover/associate_block.f90
new file mode 100644
index 00000000..30994308
--- /dev/null
+++ b/test/test_source/hover/associate_block.f90
@@ -0,0 +1,13 @@
+PROGRAM test_associate_block
+    IMPLICIT NONE
+    REAL :: A(5), B(5,5), C, III = 1
+    ASSOCIATE (X => A, Y => C)
+        PRINT*, X, Y, III
+    END ASSOCIATE
+    ASSOCIATE (X => 1)
+        PRINT*, X
+    END ASSOCIATE
+    ASSOCIATE (ARRAY => B(:,1))
+        ARRAY (3) = ARRAY (1) + ARRAY (2)
+    END ASSOCIATE
+END PROGRAM test_associate_block
diff --git a/test/test_source/hover/parameters.f90 b/test/test_source/hover/parameters.f90
index 7ede3436..e60f5cbc 100644
--- a/test/test_source/hover/parameters.f90
+++ b/test/test_source/hover/parameters.f90
@@ -9,4 +9,4 @@ program params
     logical(kind=8), parameter :: long_bool = .true.
     character(len=5), parameter :: sq_str = '12345'
     character(len=5), parameter :: dq_str = "12345"
-end program params
\ No newline at end of file
+end program params
diff --git a/test/test_source/hover/pointers.f90 b/test/test_source/hover/pointers.f90
index 9beef061..f19f4748 100644
--- a/test/test_source/hover/pointers.f90
+++ b/test/test_source/hover/pointers.f90
@@ -1,3 +1,3 @@
 program pointers
     INTEGER, POINTER :: val1
-end program
\ No newline at end of file
+end program
diff --git a/test/test_source/hover/recursive.f90 b/test/test_source/hover/recursive.f90
index 50730816..fa9171ff 100644
--- a/test/test_source/hover/recursive.f90
+++ b/test/test_source/hover/recursive.f90
@@ -20,4 +20,4 @@ recursive subroutine recursive_assign_descending(node, vector, current_loc)
         end if
         return
     end subroutine recursive_assign_descending
-end module tree
\ No newline at end of file
+end module tree
diff --git a/test/test_source/hover/spaced_keywords.f90 b/test/test_source/hover/spaced_keywords.f90
index c7b60aa6..3342dee4 100644
--- a/test/test_source/hover/spaced_keywords.f90
+++ b/test/test_source/hover/spaced_keywords.f90
@@ -1,4 +1,4 @@
 subroutine spaced_keywords(arg1,  arg2)
     real, dimension (:, :), intent (in) :: arg1
     real, dimension ( size(arg1, 1), maxval([size(arg1, 2), size(arg1, 1)]) ), intent (out) ::  arg2
-end subroutine spaced_keywords
\ No newline at end of file
+end subroutine spaced_keywords
diff --git a/test/test_source/parse/test_incomplete_dims.f90 b/test/test_source/parse/test_incomplete_dims.f90
new file mode 100644
index 00000000..15c94483
--- /dev/null
+++ b/test/test_source/parse/test_incomplete_dims.f90
@@ -0,0 +1,9 @@
+
+! Tests that the parser will not break, when parsing incomplete variables
+! constructs. This is particularly important for autocompletion.
+program test_incomplete_dims
+    implicit none
+    integer   :: dim_val(1, 2
+    character :: char_val*(10
+    integer   :: (
+end program test_incomplete_dims
diff --git a/test/test_source/parse/test_kinds_and_dims.f90 b/test/test_source/parse/test_kinds_and_dims.f90
new file mode 100644
index 00000000..fe6fc2bb
--- /dev/null
+++ b/test/test_source/parse/test_kinds_and_dims.f90
@@ -0,0 +1,42 @@
+subroutine normal_kinds()
+    integer, parameter :: r15 = selected_real_kind(15)
+    integer(kind=4) :: a, b(3,4)
+    integer*8 aa, bb(3,4)
+    integer(8) :: aaa, bbb(3,4)
+    real(kind=r15) :: r
+    real(kind(0.d0)) :: rr
+end subroutine normal_kinds
+
+real*8 function foo(val) result(r)
+    real(8), intent(in) :: val
+    r = val
+end function foo
+
+real(kind=8) function phi(val) result(r)
+    real(8), intent(in) :: val
+    r = val
+end function phi
+
+subroutine character_len_parsing(input)
+    ! global variable_type * length variable_name1, variable_name2,...
+    CHARACTER*17 A, B(3,4), V(9)
+    CHARACTER*(6+3) C
+    CHARACTER*10D(3,4)
+    CHARACTER*(LEN(B))DD(3,4)
+    ! local variable_type variable_name1 * length, variable_name2 * length,...
+    CHARACTER AA*17, BB(3,4)*17, VV(9)*17
+    CHARACTER CC*(6+3)
+    CHARACTER AAA*(LEN(A))
+    CHARACTER INPUT(*)*10
+    ! explicit len and kind for characters
+    CHARACTER(LEN=200) F
+    CHARACTER(KIND=4, LEN=200) FF(3,4)
+    CHARACTER(KIND=4, LEN=200) AAAA(3,4)*100
+
+    ! override global length with local length
+    CHARACTER*10 BBB(3,4)*(LEN(B))      ! has the length of len(b)
+    CHARACTER*10CCC(3,4)*(LEN(B))       ! no-space
+    CHARACTER(KIND=4) BBBB(3,4)*(LEN(B))      ! cannot have *10(kind=4) or vice versa
+
+    INTEGER((4)) INT_KIND_IMP   ! FIXME: (()) trips up the regex
+end subroutine character_len_parsing
diff --git a/test/test_source/pp/include/petscpc.h b/test/test_source/pp/include/petscpc.h
index b57881b3..d6f9899b 100644
--- a/test/test_source/pp/include/petscpc.h
+++ b/test/test_source/pp/include/petscpc.h
@@ -5,9 +5,9 @@
 
 #define PC type(tPC)
 #define PCType character*(80)
-#define ewrite(priority, format) if (priority <= 3) write((priority), format) 
+#define ewrite(priority, format) if (priority <= 3) write((priority), format)
 #define ewrite2(priority, format) \
-    if (priority <= 3) write((priority), format) 
+    if (priority <= 3) write((priority), format)
 #define varVar \
         55
 #endif
diff --git a/test/test_source/pp/preproc.F90 b/test/test_source/pp/preproc.F90
index 5ed849c1..265bff68 100644
--- a/test/test_source/pp/preproc.F90
+++ b/test/test_source/pp/preproc.F90
@@ -11,4 +11,4 @@ program preprocessor
     print*, SUCCESS
 
 #endif
-end program preprocessor
\ No newline at end of file
+end program preprocessor
diff --git a/test/test_source/pp/preproc_keywords.F90 b/test/test_source/pp/preproc_keywords.F90
index c216b51e..1748456f 100644
--- a/test/test_source/pp/preproc_keywords.F90
+++ b/test/test_source/pp/preproc_keywords.F90
@@ -3,8 +3,8 @@ program test_preproc_keywords
 #ifdef HAVE_CONTIGUOUS
 , CONTIGUOUS &
 #endif
-, POINTER :: & 
+, POINTER :: &
 var1(:), &
 var2(:)
 
-end program test_preproc_keywords
\ No newline at end of file
+end program test_preproc_keywords
diff --git a/test/test_source/rename/test_rename_intrinsic.f90 b/test/test_source/rename/test_rename_intrinsic.f90
index 4870b0dd..77e69b34 100644
--- a/test/test_source/rename/test_rename_intrinsic.f90
+++ b/test/test_source/rename/test_rename_intrinsic.f90
@@ -22,4 +22,4 @@ program driver
     integer :: sz
     call size(val, sz)  ! This is fortran_sub and should be renamed
     print*, size(val)   ! This is an intrinsic, should be skipped in renaming
-end program driver
\ No newline at end of file
+end program driver
diff --git a/test/test_source/subdir/test_rename.F90 b/test/test_source/subdir/test_rename.F90
index 8868e34f..52bc0911 100644
--- a/test/test_source/subdir/test_rename.F90
+++ b/test/test_source/subdir/test_rename.F90
@@ -12,5 +12,5 @@ subroutine test_rename_sub()
 implicit none
 !
 localname = 4
-renamed_var2 = 4    
+renamed_var2 = 4
 end subroutine test_rename_sub
diff --git a/test/test_source/subdir/test_vis.f90 b/test/test_source/subdir/test_vis.f90
index 4e50ec84..40144d2c 100644
--- a/test/test_source/subdir/test_vis.f90
+++ b/test/test_source/subdir/test_vis.f90
@@ -11,4 +11,4 @@ module test_vis_mod
 contains
 subroutine some_sub
 end subroutine some_sub
-end module test_vis_mod
\ No newline at end of file
+end module test_vis_mod
diff --git a/test/test_source/tmp.py b/test/test_source/tmp.py
deleted file mode 100644
index 5e3bc411..00000000
--- a/test/test_source/tmp.py
+++ /dev/null
@@ -1,18 +0,0 @@
-import os
-
-from fparser.common.readfortran import FortranFileReader
-from fparser.two.parser import ParserFactory
-from fparser.two.utils import Base, walk
-
-os.chdir("/home/gn/Code/Python/fortls/test/test_source")
-
-reader = FortranFileReader("test.f90", include_dirs=["subdir"], ignore_comments=False)
-parser = ParserFactory().create(std="f2008")
-parse_tree = parser(reader)
-
-for i, node in enumerate(walk(parse_tree)):
-    if node is None:
-        continue
-    if not isinstance(node, Base):
-        continue
-    print(i, type(node), ": ", node)
diff --git a/test/test_source/wrong_syntax.json b/test/test_source/wrong_syntax.json
new file mode 100644
index 00000000..83234de9
--- /dev/null
+++ b/test/test_source/wrong_syntax.json
@@ -0,0 +1,3 @@
+{
+    "source_dirs", "s"
+}