dimfort 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dimfort-0.1.0/.github/workflows/release.yml +100 -0
- dimfort-0.1.0/.gitignore +243 -0
- dimfort-0.1.0/CHANGELOG.md +92 -0
- dimfort-0.1.0/CONTRIBUTING.md +89 -0
- dimfort-0.1.0/LICENSE +21 -0
- dimfort-0.1.0/PKG-INFO +164 -0
- dimfort-0.1.0/README.md +113 -0
- dimfort-0.1.0/docs/annotations.md +226 -0
- dimfort-0.1.0/docs/index.md +10 -0
- dimfort-0.1.0/docs/lsp.md +92 -0
- dimfort-0.1.0/docs/release.md +57 -0
- dimfort-0.1.0/docs/usage.md +99 -0
- dimfort-0.1.0/pyproject.toml +63 -0
- dimfort-0.1.0/scripts/make_branding.py +230 -0
- dimfort-0.1.0/social_preview.png +0 -0
- dimfort-0.1.0/src/dimfort/__init__.py +1 -0
- dimfort-0.1.0/src/dimfort/__main__.py +4 -0
- dimfort-0.1.0/src/dimfort/cli.py +224 -0
- dimfort-0.1.0/src/dimfort/config.py +162 -0
- dimfort-0.1.0/src/dimfort/core/__init__.py +6 -0
- dimfort-0.1.0/src/dimfort/core/_source_io.py +62 -0
- dimfort-0.1.0/src/dimfort/core/annotations.py +426 -0
- dimfort-0.1.0/src/dimfort/core/attach.py +240 -0
- dimfort-0.1.0/src/dimfort/core/default_units.toml +42 -0
- dimfort-0.1.0/src/dimfort/core/diagnostics.py +26 -0
- dimfort-0.1.0/src/dimfort/core/multifile.py +563 -0
- dimfort-0.1.0/src/dimfort/core/symbols.py +225 -0
- dimfort-0.1.0/src/dimfort/core/ts_checker.py +1468 -0
- dimfort-0.1.0/src/dimfort/core/ts_parser.py +385 -0
- dimfort-0.1.0/src/dimfort/core/unit_config.py +175 -0
- dimfort-0.1.0/src/dimfort/core/units.py +292 -0
- dimfort-0.1.0/src/dimfort/core/workspace_index.py +553 -0
- dimfort-0.1.0/src/dimfort/lsp/__init__.py +0 -0
- dimfort-0.1.0/src/dimfort/lsp/handlers.py +1 -0
- dimfort-0.1.0/src/dimfort/lsp/server.py +1967 -0
- dimfort-0.1.0/src/dimfort/lsp/ts_helpers.py +304 -0
- dimfort-0.1.0/tests/__init__.py +0 -0
- dimfort-0.1.0/tests/fixtures/hello.f90 +4 -0
- dimfort-0.1.0/tests/fixtures/multifile/geo.f90 +19 -0
- dimfort-0.1.0/tests/fixtures/multifile/main.f90 +25 -0
- dimfort-0.1.0/tests/fixtures/multifile_scope/file_a.f90 +12 -0
- dimfort-0.1.0/tests/fixtures/multifile_scope/file_b.f90 +13 -0
- dimfort-0.1.0/tests/fixtures/smoke_ast_phase1.f90 +43 -0
- dimfort-0.1.0/tests/fixtures/smoke_ast_phase3.f90 +49 -0
- dimfort-0.1.0/tests/fixtures/smoke_basic.f90 +31 -0
- dimfort-0.1.0/tests/fixtures/smoke_check.f90 +15 -0
- dimfort-0.1.0/tests/fixtures/smoke_derived_types.f90 +24 -0
- dimfort-0.1.0/tests/fixtures/smoke_functions.f90 +45 -0
- dimfort-0.1.0/tests/fixtures/smoke_intrinsics.f90 +17 -0
- dimfort-0.1.0/tests/fixtures/smoke_rational_pow.f90 +14 -0
- dimfort-0.1.0/tests/integration/__init__.py +0 -0
- dimfort-0.1.0/tests/integration/test_cli_check.py +93 -0
- dimfort-0.1.0/tests/integration/test_overrides.py +55 -0
- dimfort-0.1.0/tests/integration/test_smoke_basic.py +52 -0
- dimfort-0.1.0/tests/unit/test_annotations.py +160 -0
- dimfort-0.1.0/tests/unit/test_attach.py +239 -0
- dimfort-0.1.0/tests/unit/test_cli.py +23 -0
- dimfort-0.1.0/tests/unit/test_config.py +199 -0
- dimfort-0.1.0/tests/unit/test_declarations.py +216 -0
- dimfort-0.1.0/tests/unit/test_format.py +36 -0
- dimfort-0.1.0/tests/unit/test_lsp_hover_scoping.py +66 -0
- dimfort-0.1.0/tests/unit/test_lsp_module_navigation.py +214 -0
- dimfort-0.1.0/tests/unit/test_lsp_server.py +87 -0
- dimfort-0.1.0/tests/unit/test_lsp_workset_cap.py +69 -0
- dimfort-0.1.0/tests/unit/test_source_io.py +51 -0
- dimfort-0.1.0/tests/unit/test_ts_checker.py +246 -0
- dimfort-0.1.0/tests/unit/test_ts_helpers.py +127 -0
- dimfort-0.1.0/tests/unit/test_ts_parser.py +304 -0
- dimfort-0.1.0/tests/unit/test_unit_config.py +49 -0
- dimfort-0.1.0/tests/unit/test_units.py +84 -0
- dimfort-0.1.0/tests/unit/test_var_units_scoping.py +170 -0
- dimfort-0.1.0/tests/unit/test_workspace_index.py +369 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
|
|
3
|
+
# A single tag push (v*.*.*) gates the full release pipeline:
|
|
4
|
+
# 1. test: run pytest + ruff across Linux/macOS/Windows on
|
|
5
|
+
# Python 3.11 (our minimum supported version).
|
|
6
|
+
# 2. build: build sdist + wheel, check with twine.
|
|
7
|
+
# 3. publish-pypi: upload to PyPI via trusted publishing.
|
|
8
|
+
# 4. github-release: create a GitHub Release with the dist artefacts.
|
|
9
|
+
#
|
|
10
|
+
# Each downstream job `needs:` the prior, so a single failing test
|
|
11
|
+
# blocks publish — no broken release can land.
|
|
12
|
+
#
|
|
13
|
+
# `workflow_dispatch` lets you re-run the test matrix on demand
|
|
14
|
+
# (e.g. before tagging) without pushing a tag.
|
|
15
|
+
|
|
16
|
+
on:
|
|
17
|
+
push:
|
|
18
|
+
tags:
|
|
19
|
+
- "v*.*.*"
|
|
20
|
+
workflow_dispatch:
|
|
21
|
+
|
|
22
|
+
jobs:
|
|
23
|
+
test:
|
|
24
|
+
runs-on: ${{ matrix.os }}
|
|
25
|
+
strategy:
|
|
26
|
+
fail-fast: false
|
|
27
|
+
matrix:
|
|
28
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
29
|
+
steps:
|
|
30
|
+
- uses: actions/checkout@v5
|
|
31
|
+
- uses: actions/setup-python@v6
|
|
32
|
+
with:
|
|
33
|
+
python-version: "3.11"
|
|
34
|
+
- name: Install
|
|
35
|
+
run: |
|
|
36
|
+
python -m pip install --upgrade pip
|
|
37
|
+
pip install -e ".[dev,lsp]"
|
|
38
|
+
- name: Lint
|
|
39
|
+
run: ruff check .
|
|
40
|
+
- name: Test
|
|
41
|
+
run: pytest
|
|
42
|
+
|
|
43
|
+
build:
|
|
44
|
+
needs: test
|
|
45
|
+
# Don't build/publish on manual dispatch — that's just a test re-run.
|
|
46
|
+
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
|
|
47
|
+
runs-on: ubuntu-latest
|
|
48
|
+
steps:
|
|
49
|
+
- uses: actions/checkout@v5
|
|
50
|
+
- uses: actions/setup-python@v6
|
|
51
|
+
with:
|
|
52
|
+
python-version: "3.12"
|
|
53
|
+
- name: Install build tooling
|
|
54
|
+
run: |
|
|
55
|
+
python -m pip install --upgrade pip
|
|
56
|
+
pip install build twine
|
|
57
|
+
- name: Build sdist and wheel
|
|
58
|
+
run: python -m build
|
|
59
|
+
- name: Check artefacts
|
|
60
|
+
run: twine check dist/*
|
|
61
|
+
- name: Upload artefacts
|
|
62
|
+
uses: actions/upload-artifact@v4
|
|
63
|
+
with:
|
|
64
|
+
name: dist
|
|
65
|
+
path: dist/
|
|
66
|
+
|
|
67
|
+
publish-pypi:
|
|
68
|
+
needs: build
|
|
69
|
+
runs-on: ubuntu-latest
|
|
70
|
+
# Trusted publishing — no API token in secrets. Configure the
|
|
71
|
+
# corresponding "pending publisher" on https://pypi.org/manage/account/publishing/
|
|
72
|
+
# before the first release.
|
|
73
|
+
environment:
|
|
74
|
+
name: pypi
|
|
75
|
+
url: https://pypi.org/project/dimfort/
|
|
76
|
+
permissions:
|
|
77
|
+
id-token: write
|
|
78
|
+
steps:
|
|
79
|
+
- uses: actions/download-artifact@v4
|
|
80
|
+
with:
|
|
81
|
+
name: dist
|
|
82
|
+
path: dist/
|
|
83
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
84
|
+
|
|
85
|
+
github-release:
|
|
86
|
+
needs: build
|
|
87
|
+
runs-on: ubuntu-latest
|
|
88
|
+
permissions:
|
|
89
|
+
contents: write
|
|
90
|
+
steps:
|
|
91
|
+
- uses: actions/checkout@v5
|
|
92
|
+
- uses: actions/download-artifact@v4
|
|
93
|
+
with:
|
|
94
|
+
name: dist
|
|
95
|
+
path: dist/
|
|
96
|
+
- name: Create GitHub Release
|
|
97
|
+
uses: softprops/action-gh-release@v2
|
|
98
|
+
with:
|
|
99
|
+
files: dist/*
|
|
100
|
+
generate_release_notes: true
|
dimfort-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py.cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
# Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# UV
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
# uv.lock
|
|
102
|
+
|
|
103
|
+
# poetry
|
|
104
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
105
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
106
|
+
# commonly ignored for libraries.
|
|
107
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
108
|
+
# poetry.lock
|
|
109
|
+
# poetry.toml
|
|
110
|
+
|
|
111
|
+
# pdm
|
|
112
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
113
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
114
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
115
|
+
# pdm.lock
|
|
116
|
+
# pdm.toml
|
|
117
|
+
.pdm-python
|
|
118
|
+
.pdm-build/
|
|
119
|
+
|
|
120
|
+
# pixi
|
|
121
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
122
|
+
# pixi.lock
|
|
123
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
124
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
125
|
+
.pixi
|
|
126
|
+
|
|
127
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
128
|
+
__pypackages__/
|
|
129
|
+
|
|
130
|
+
# Celery stuff
|
|
131
|
+
celerybeat-schedule
|
|
132
|
+
celerybeat.pid
|
|
133
|
+
|
|
134
|
+
# Redis
|
|
135
|
+
*.rdb
|
|
136
|
+
*.aof
|
|
137
|
+
*.pid
|
|
138
|
+
|
|
139
|
+
# RabbitMQ
|
|
140
|
+
mnesia/
|
|
141
|
+
rabbitmq/
|
|
142
|
+
rabbitmq-data/
|
|
143
|
+
|
|
144
|
+
# ActiveMQ
|
|
145
|
+
activemq-data/
|
|
146
|
+
|
|
147
|
+
# SageMath parsed files
|
|
148
|
+
*.sage.py
|
|
149
|
+
|
|
150
|
+
# Environments
|
|
151
|
+
.env
|
|
152
|
+
.envrc
|
|
153
|
+
.venv
|
|
154
|
+
env/
|
|
155
|
+
venv/
|
|
156
|
+
ENV/
|
|
157
|
+
env.bak/
|
|
158
|
+
venv.bak/
|
|
159
|
+
|
|
160
|
+
# Spyder project settings
|
|
161
|
+
.spyderproject
|
|
162
|
+
.spyproject
|
|
163
|
+
|
|
164
|
+
# Rope project settings
|
|
165
|
+
.ropeproject
|
|
166
|
+
|
|
167
|
+
# mkdocs documentation
|
|
168
|
+
/site
|
|
169
|
+
|
|
170
|
+
# mypy
|
|
171
|
+
.mypy_cache/
|
|
172
|
+
.dmypy.json
|
|
173
|
+
dmypy.json
|
|
174
|
+
|
|
175
|
+
# Pyre type checker
|
|
176
|
+
.pyre/
|
|
177
|
+
|
|
178
|
+
# pytype static type analyzer
|
|
179
|
+
.pytype/
|
|
180
|
+
|
|
181
|
+
# Cython debug symbols
|
|
182
|
+
cython_debug/
|
|
183
|
+
|
|
184
|
+
# PyCharm
|
|
185
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
186
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
187
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
188
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
189
|
+
# .idea/
|
|
190
|
+
|
|
191
|
+
# Abstra
|
|
192
|
+
# Abstra is an AI-powered process automation framework.
|
|
193
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
194
|
+
# Learn more at https://abstra.io/docs
|
|
195
|
+
.abstra/
|
|
196
|
+
|
|
197
|
+
# Visual Studio Code
|
|
198
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
199
|
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
200
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
201
|
+
# you could uncomment the following to ignore the entire vscode folder
|
|
202
|
+
# .vscode/
|
|
203
|
+
# Temporary file for partial code execution
|
|
204
|
+
tempCodeRunnerFile.py
|
|
205
|
+
|
|
206
|
+
# Ruff stuff:
|
|
207
|
+
.ruff_cache/
|
|
208
|
+
|
|
209
|
+
# PyPI configuration file
|
|
210
|
+
.pypirc
|
|
211
|
+
|
|
212
|
+
# Marimo
|
|
213
|
+
marimo/_static/
|
|
214
|
+
marimo/_lsp/
|
|
215
|
+
__marimo__/
|
|
216
|
+
|
|
217
|
+
# Streamlit
|
|
218
|
+
.streamlit/secrets.toml
|
|
219
|
+
|
|
220
|
+
# --- DimFort-specific ---
|
|
221
|
+
|
|
222
|
+
# Per-project analysis cache (safe to delete; `dimfort cache clean` does the same)
|
|
223
|
+
.dimfort/
|
|
224
|
+
|
|
225
|
+
# OS files
|
|
226
|
+
.DS_Store
|
|
227
|
+
Thumbs.db
|
|
228
|
+
*~
|
|
229
|
+
|
|
230
|
+
# Editor / IDE
|
|
231
|
+
.idea/
|
|
232
|
+
*.swp
|
|
233
|
+
*.swo
|
|
234
|
+
|
|
235
|
+
# Local-only notes or scratch
|
|
236
|
+
NOTES.local.md
|
|
237
|
+
scratch/
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
# Emacs auto-save / backup
|
|
241
|
+
\#*\#
|
|
242
|
+
.\#*
|
|
243
|
+
*~
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to DimFort are documented here. Format inspired by [Keep a Changelog](https://keepachangelog.com/).
|
|
4
|
+
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
## [0.1.0] — 2026-05-19
|
|
8
|
+
|
|
9
|
+
First public release. Pre-alpha; expect breaking changes between
|
|
10
|
+
`0.1.x` versions as the tool matures against real-world Fortran
|
|
11
|
+
codebases.
|
|
12
|
+
|
|
13
|
+
### Highlights
|
|
14
|
+
|
|
15
|
+
- **CLI**: `dimfort check FILE/DIR [...]` with per-file H-/U-summary;
|
|
16
|
+
`dimfort lsp` over stdio.
|
|
17
|
+
- **Annotation pipeline**: scoped per `(subroutine|function, name)` so
|
|
18
|
+
same-named parameters across two routines in a file don't alias.
|
|
19
|
+
- **Checker**: full H001-H004 (assignment, arithmetic, intrinsics,
|
|
20
|
+
user-defined calls, derived-type fields, rational `**` exponents)
|
|
21
|
+
across multi-file worksets.
|
|
22
|
+
- **Workspace orchestration**: `use`-chain resolution plus a
|
|
23
|
+
workspace-wide top-level-procedure index for F77-vintage external
|
|
24
|
+
procedures.
|
|
25
|
+
- **LSP server**: live diagnostics, hover (scope-aware bare
|
|
26
|
+
identifier, derived-type member chains, call signatures, module-
|
|
27
|
+
summary on `use foo`), inlay hints, go-to-definition for variables,
|
|
28
|
+
callables, and module names, code lens, completion inside
|
|
29
|
+
`@unit{...}`, "Add unit annotation" code action, the
|
|
30
|
+
`dimfort.checkWorkspace` command, didClose republish,
|
|
31
|
+
`workspace/inlayHint/refresh` push, tab-switch-safe republish, and
|
|
32
|
+
a `/tmp/dimfort-lsp.crash` excepthook for silent-crash diagnostics.
|
|
33
|
+
- **Editor companions** (separate repos): VSCode, Neovim ≥ 0.11,
|
|
34
|
+
Emacs (eglot + lsp-mode).
|
|
35
|
+
- **Project config**: `.dimfort.toml` with `[project] src_paths`,
|
|
36
|
+
`[workset] external_modules` / `max_size`, `[parser] cpp_defines`
|
|
37
|
+
/ `include_paths`, `[units] file`.
|
|
38
|
+
- **Test coverage**: 228 unit + integration tests, ruff-clean.
|
|
39
|
+
|
|
40
|
+
### 2026-05-19 — Scope-aware annotations, external-procedure index, tab-switch republish
|
|
41
|
+
|
|
42
|
+
- **Per-scope `@unit{}` annotations** (`attach.py`, `annotations.py`, `ts_checker.py`): annotations are now keyed by `(scope_lc, name)` where `scope_lc` is the lower-cased enclosing subroutine/function (or `None` at module/file level). Two routines in one file declaring same-named params with different units no longer alias. Flat `var_units` view retained as a back-compat first-seen surface for callers that don't carry scope info. `_make_scoped_lookup` no longer falls back to flat lookup when in scope-aware mode — this closed a real false-positive path where unannotated wrapper params (e.g. NetCDF `put_var(..., v)`) were absorbing the unit of unrelated same-named variables in the workset. Diagnostic count on LMDZ trial dropped from 20 to 12 H-findings, all real (8 spurious, retracted in `LMDZ_FINDINGS.md`).
|
|
43
|
+
- **Scope-aware hover** (`lsp/server.py`): bare-identifier hover consults the per-scope table and reports the *enclosing routine's* annotation, not the first-seen across the workset.
|
|
44
|
+
- **Module-name hover and goto-def** (`lsp/server.py`, `lsp/ts_helpers.py`): hover on the module-name token of a `use foo` statement renders a summary of the module's exports — variables with units, contained procedures with signatures, `(N/M annotated)` count when there's a gap. Goto-def on the same token jumps to the `module foo` header.
|
|
45
|
+
- **Workspace external-procedure index** (`core/workspace_index.py`, `core/multifile.py`, `lsp/server.py`): a workspace-wide name map from top-level `SUBROUTINE`/`FUNCTION` to defining file, populated at LSP startup (~4.5 s on full LMDZ). Resolves F77-vintage external procedures (called without a `USE` clause), so goto-def, hover signatures, and H004 all follow such calls. `resolve_workset`'s BFS now expands via `CALL` edges too; topo sort honours them; the per-file workset cap pins direct deps (modules used + procedures called) so shallow callees can't be sliced out.
|
|
46
|
+
- **Tab-switch-safe re-publish** (`lsp/server.py`): the single global `_last_result` was overwritten on every `didOpen`/`didSave`/`didChange`. Navigating caller↔callee opened the callee's tab, flipping the workset to its downward-only deps. Switching back was silent (no LSP event), so subsequent goto-def/hover/inlay on the caller failed with "not in trees". New `_ensure_uri_loaded` re-publishes synchronously when the requested URI isn't in the current workset.
|
|
47
|
+
- **H004 message includes argument name** (`ts_checker.py`): `"argument 5 (pbaru) unit mismatch: …"` instead of `"argument 5 unit mismatch: …"`. Index kept too — formal names can repeat across `INTENT(INOUT)` slots or in overloads, so position remains the unambiguous identifier and the name is the friendly hint.
|
|
48
|
+
- **Silent-crash trace hook** (`lsp/server.py`, opt-out via `DIMFORT_CRASH_LOG=""`): `sys.excepthook` + `threading.excepthook` + pygls/asyncio logger handlers mirror Python tracebacks into `/tmp/dimfort-lsp.crash`. Doesn't catch native segfaults / SIGKILLs, but makes future Python-level crashes immediately actionable.
|
|
49
|
+
- **Tree-handler serialisation lock** (`lsp/server.py`): defensive lock around `_hover`, `_definition`, and `_inlay_hint` so they can't traverse the same tree-sitter Tree from different threads. Today's bug turned out to be elsewhere, but the lock stays as cheap insurance against tree-sitter's C library not being thread-safe.
|
|
50
|
+
|
|
51
|
+
### 2026-05-17 — CLI directory mode, LSP didClose persistence, U005 usage hint
|
|
52
|
+
|
|
53
|
+
- **CLI**: `dimfort check` accepts directory arguments and walks them
|
|
54
|
+
recursively for Fortran sources. New `--summary` flag prints a
|
|
55
|
+
per-file H-/U-diagnostic count breakdown after the diagnostic stream.
|
|
56
|
+
`FORTRAN_EXTS` and `discover_fortran_files` extracted to
|
|
57
|
+
`core/_source_io.py` so the LSP and CLI share one definition.
|
|
58
|
+
- **LSP**: `didClose` no longer publishes an empty diagnostic list for
|
|
59
|
+
the closed file — it now republishes the most recent workspace-check
|
|
60
|
+
diagnostics for that path, so the Problems panel keeps showing real
|
|
61
|
+
issues after the user closes a tab.
|
|
62
|
+
- **Checker**: U005 ("variable used in unit-checked expression but
|
|
63
|
+
has no `@unit{}` annotation") now appends `(e.g. used at line N)`
|
|
64
|
+
pointing at the earliest usage site, so the user can jump from the
|
|
65
|
+
unannotated declaration to a concrete consumer.
|
|
66
|
+
- **Branding**: `scripts/make_branding.py` renders a 1280×640
|
|
67
|
+
`social_preview.png` at the repo root. Design palette mirrors the
|
|
68
|
+
VSCompanion icon (translucent Clarendon F watermark, rounded
|
|
69
|
+
frame, `[m·s⁻²]` glyph).
|
|
70
|
+
|
|
71
|
+
### Branch `ast-tree-sitter` (2026-05-16) — LFortran retired, tree-sitter takes over
|
|
72
|
+
|
|
73
|
+
Parser swap: LFortran subprocess → in-process tree-sitter Fortran grammar. The diagnostic pipeline, the LSP enrichments, and the on-disk caching are all re-implemented; CLI and config simplified accordingly.
|
|
74
|
+
|
|
75
|
+
- **Phase 0** (`df8a793`) — new `core.ts_parser`: parse_text / parse_file / walk, plus a CPP shim with line-map remap for `.F90` files. 18 unit tests pin the `&`-continuation drift case and the CPP shim's define/include/missing-include paths.
|
|
76
|
+
- **Phase 1** (`a823a73`) — declaration scanner ported. `core/annotations.py` walks tree-sitter `variable_declaration` and `derived_type_definition` nodes instead of the regex matcher; recovers names from `sized_declarator` / `init_declarator` wrappers. Net −155 / +174 lines; +1 test pinning the new "recover declarations after a syntax error" capability.
|
|
77
|
+
- **Phase 2** (`75459fd`) — full checker port. New `core/ts_checker.py` mirrors `core.ast_checker` 1:1 against tree-sitter nodes: `_resolve` for expressions, H001-H004 emitters, intrinsic dispatch, derived-type chain resolution, `**` exponent handling including negatives. `core/ast_multifile.py` switched to drive the new checker; 8 new unit tests at `tests/unit/test_ts_checker.py`.
|
|
78
|
+
- **Phase 3** (`d9d7c1c`) — LSP enrichments rewritten on tree-sitter. New `lsp/ts_helpers.py` (position containment, targeted walks, "is this the callee?" / "is this inside a declaration?" predicates). Hover, inlay hints, go-to-definition, and code-lens handlers all rewired; identifier-to-unit resolution shared with the diagnostic pipeline so there's a single source of truth. The most elaborate hover renderers (multi-variable expression / assignment hovers) intentionally skipped — they degrade to "no hover at that position" and can be reinstated later. Net +284 / −640.
|
|
79
|
+
- **Phase 4** (this commit) — LFortran path retired entirely. Deleted `core/lfortran.py`, `core/ast_checker.py`, `core/checker.py`, `core/ast_multifile.py`, `cache.py`, `core/parser.py`. New `core/symbols.py` holds the parser-agnostic data (FuncSig, intrinsic tables, ModuleExports, apply_use_clauses). `core/multifile.py` rewritten as a clean tree-sitter orchestrator (was the ASR orchestrator). CLI: `--backend`, `--lfortran`, `--no-cache`, `--cache-dir` flags removed; `cache` subcommand removed. Config: `[lfortran]` and `[checker]` sections silently ignored for backward compatibility but no longer exposed as fields. LSP: backend dispatch deleted, cache wiring deleted. Test count went from 287 → 183 — the deleted tests covered the deleted code.
|
|
80
|
+
|
|
81
|
+
### Branch `ast-only` (previous, preserved on `ast_and_asr`)
|
|
82
|
+
|
|
83
|
+
- **Phase 0 (spike, 2026-05-15)** — minimal AST-only checker landing as `core.ast_checker.check`. Walks LFortran's AST (no ASR involvement, no `lfortran -c`) and emits H001 + H002 for `Name` / `Num` / `BinOp(+,-,*,/)` / `Assignment` node combinations. Demonstrated end-to-end on `tests/fixtures/smoke_check.f90`: H001 fires on the dimensionally-wrong assignment, not on the clean one. Design notes in `docs/ast-only-design.md`; rest of the H/U series, cross-file `use`-chain resolution, intrinsics, derived types, casts, and array sections are TBD across Phases 1–5.
|
|
84
|
+
- **Phase 1 (single-file H/U series, 2026-05-15)** — `core.ast_checker` extended to cover the full single-file H-series: H003 (dimensionless-intrinsic violation), H004 (call argument mismatch), plus `Pow` with constant exponent (integer or rational via `Fraction.limit_denominator`), `UnaryMinus`, `Real` literal, and the six intrinsic categories (`DIMENSIONLESS`, `TRANSFORMING`, `TRANSPARENT`, `SAME_UNIT_ARG`, `PRODUCT`, `REDUCTION`) re-used verbatim from `core.checker` — no duplication of intrinsic tables. `collect_function_signatures(ast, var_units)` walks the AST for `Function` / `Subroutine` definitions and builds the same `FuncSig` table the ASR-side checker produces; `check()` accepts a `signatures=` kwarg so Phase 2 can pass a workset-wide map. New fixture `tests/fixtures/smoke_ast_phase1.f90` and integration tests `test_ast_phase1.py` (5 tests). Added `test_ast_parity.py` (3 fixtures) asserting the AST checker's H-series multiset matches the ASR checker's on `smoke_check.f90` / `smoke_intrinsics.f90` / `smoke_functions.f90` — the parity guard that catches regression once Phase 2+ extends scope further.
|
|
85
|
+
- **Phase 2 (cross-file use-chains, 2026-05-15)** — `core.ast_multifile.check_files_ast` orchestrates a full workset using AST only (no `lfortran -c`, no ASR). `ast_checker.collect_module_exports(ast, var_units)` walks `Module` nodes and produces a `ModuleExports` record per module (vars + signatures); `ast_checker.apply_use_clauses(uses, exports, ...)` splices the imported symbols into a consumer file's scope, honouring `only:` lists and `local => remote` renames. Missing modules surface as U007. New integration tests `test_ast_phase2.py` (4 tests) cover the cross-file H001/H004 path, workset-wide H-series parity with the ASR pipeline, order-independence, and the U007 emission. All 231/231 tests still pass.
|
|
86
|
+
- **Phase 3 (derived types + arrays, 2026-05-15)** — `ast_checker` now resolves derived-type access chains (`a%b%c`), array elements (`a(i)`), array slices (`a(:)`, `a(1:n)`). Adds `collect_var_types(ast)` and `collect_type_field_types(ast)` to build the per-file type maps from `Declaration` and `DerivedType` nodes; the resolver walks `Name.member` chains against those maps to reach the `field_units` table. `FuncCallOrArray` whose name matches a known variable now returns that variable's unit — closing the "is `a(1)` a function call or array indexing?" ambiguity LFortran's AST inherits. Fix to `Pow` and the transforming-intrinsics codepath to use `Unit.pow(exp)` instead of `Unit ** exp` (the latter falls through to `Fraction.__rpow__` and crashes on `float`). Extended parity test set to 5 fixtures including `smoke_derived_types.f90` and `smoke_rational_pow.f90` — all pass. New fixture `smoke_ast_phase3.f90` + 3 Phase 3-specific tests. Full suite: 236/236.
|
|
87
|
+
- **Phase 3 hardening (2026-05-15)** — LMDZ trial on `dyn3d_common/` (117 files) surfaced two bugs in the Phase 2/3 multifile orchestrator: missing U-series emissions (U001 scan errors, U002 unit-parse failures, the U006/U-conflict/U010 set from `_attachment_diags`) and a cross-file bare-name leak through `merged_var_units`. Fixed by reusing `multifile._attachment_diags`, emitting U001/U002 in the per-file pass, and scoping each file's check from its own `attachment.var_units` (cross-file imports still arrive explicitly via `apply_use_clauses`). LMDZ impact on `dyn3d_common`: false-positive H001s dropped from 47 to 6; previously-suppressed H004s now surface (11). New regression fixture `tests/fixtures/multifile_scope/` + `test_ast_scope.py`.
|
|
88
|
+
- **Phase 4 (backend selection, 2026-05-15)** — `[checker] backend = "ast" \| "asr"` lands in `dimfort.config.DimfortConfig`. CLI gains `--backend ast\|asr` on the `check` subcommand. LSP server reads `backend` from `initializationOptions` (falling through to config, then default `"asr"`). VSCompanion repo's `ast-only` branch adds `dimfort.backend` (enum) to the settings schema and forwards it. Backend is logged in the init notification (`backend=…`). 5 new config tests + 3 new CLI integration tests. Default stays `"asr"`; Phase 5 will flip it once the AST path has soaked.
|
|
89
|
+
- **Phase 4.6 (`.intfb.h` stubs + cpp_defines, 2026-05-16)** — `[lfortran] include_paths` and `[lfortran] cpp_defines` in `DimfortConfig` thread `-I` and `-D` through to LFortran. Unblocks LMDZ's ecRad headers (after stubbing them empty) and the `#ifdef ISO`-wrapped isotope-branch modules. `lf.dump_tree` decodes stdout/stderr with UTF-8 → Latin-1 fallback so French-comment files don't crash the workspace check. Adds the "DimFort: Check Whole Workspace" LSP command with phase-tagged ($/progress) per-file reporting ("loading 412/2435", "indexing modules", "checking"). LMDZ trial: 2435 files → 16 unloadable + ~13 cascade U007s (all LFortran 0.63 bugs).
|
|
90
|
+
- **Phase 5 (default backend → AST, 2026-05-16)** — `cli.py`, `lsp/server.py`, and VSCompanion `package.json` all now default to `backend = "ast"`. ASR remains selectable via `--backend asr` (CLI), `[checker] backend = "asr"` (config), or the `dimfort.backend` VSCode setting. Fixes a long-standing round-trip bug in `ast_multifile`: it converted parsed `Unit` objects back to text via `format_unit()` before handing to `ast_checker.check`, which then re-parsed — but `format_unit` emits Unicode (`m/s²`, `kg×m/s²`) that the parser doesn't accept. `ast_checker.check` now accepts `Unit` objects directly for both `var_units` and `field_units`; the multifile path passes them through without round-tripping. Caught when the existing CLI integration tests (which previously ran via ASR by default) started failing — they exercise H001 on a single-file workset where this round-trip had been silently dropping the only annotation.
|
|
91
|
+
- **Phase 6a (parallel loading, 2026-05-16)** — `check_files_ast`'s Phase A now uses a `ThreadPoolExecutor` (default workers = `cpu_count() - 1`). Subprocess.run releases the GIL while LFortran is running, so threads parallelise without the pickling overhead a process pool would impose. Progress callback fires in completion order under a small lock. LMDZ benchmark (2435 files, 8 cores): 223s → 170s (1.3×). Modest gain — GIL contention during large-AST JSON parsing now dominates the residual.
|
|
92
|
+
- **Phase 6b (AST cache, 2026-05-16)** — New `cache.load_single_tree_cached(path, mode='ast', …)` mirrors `load_trees_cached` but caches one tree at a time. Stored under `<cache>/<sha1>.ast.json`, keyed on content sha256 mixed with `include_paths` + `cpp_defines` (so config changes invalidate cleanly). `ast_multifile.check_files_ast` now accepts a `cache_dir=` kwarg and threads it into `_load_one`; the LSP passes `_cache_dir` (already resolved at initialize). LSP buffer overrides bypass the cache for that file only — sibling files still benefit. 3 new unit tests covering round-trip, include-path invalidation, and cpp-define invalidation. Warm-run LMDZ workspace check now dominates JSON-load cost rather than LFortran, dropping wall time to a fraction of the cold run.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Contributing to DimFort
|
|
2
|
+
|
|
3
|
+
Thanks for considering a contribution. DimFort is pre-alpha; the
|
|
4
|
+
contribution surface and expectations below will evolve as the
|
|
5
|
+
project stabilises.
|
|
6
|
+
|
|
7
|
+
## Reporting issues
|
|
8
|
+
|
|
9
|
+
Open an issue on GitHub with:
|
|
10
|
+
|
|
11
|
+
- Minimum reproducible Fortran source (or a pointer to the
|
|
12
|
+
relevant file).
|
|
13
|
+
- The DimFort command you ran (or the editor + LSP context).
|
|
14
|
+
- Expected versus observed behaviour.
|
|
15
|
+
- Output of `dimfort --version` and your Python version.
|
|
16
|
+
|
|
17
|
+
If the failure is a false-positive diagnostic, include the
|
|
18
|
+
annotation in question and any cross-file imports involved — unit
|
|
19
|
+
consistency depends on the whole `use`-chain neighbourhood, not
|
|
20
|
+
just one file.
|
|
21
|
+
|
|
22
|
+
## Development setup
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
git clone https://github.com/ArrialVictor/DimFort.git
|
|
26
|
+
cd DimFort
|
|
27
|
+
python -m venv .venv
|
|
28
|
+
source .venv/bin/activate
|
|
29
|
+
pip install -e ".[dev,lsp]"
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Minimum Python version is 3.11. The Fortran parser
|
|
33
|
+
([`tree-sitter-fortran`](https://pypi.org/project/tree-sitter-fortran/))
|
|
34
|
+
is a runtime dependency installed automatically.
|
|
35
|
+
|
|
36
|
+
## Running tests
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pytest # 228 tests across unit + integration
|
|
40
|
+
pytest tests/unit -q # unit only
|
|
41
|
+
pytest -k <expression> # filter by name
|
|
42
|
+
ruff check . # lint
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
A patch that breaks tests or trips ruff will not be merged.
|
|
46
|
+
|
|
47
|
+
## Code style
|
|
48
|
+
|
|
49
|
+
- Follow the existing module organisation. See
|
|
50
|
+
[ARCHITECTURE.md](https://github.com/ArrialVictor/DimFort/blob/main/ARCHITECTURE.md)
|
|
51
|
+
(when present) and the docstrings at the top of each `core/`
|
|
52
|
+
module for the layout rationale.
|
|
53
|
+
- Comments should explain **why**, not what. Reading the code
|
|
54
|
+
tells you what; the comment is for the non-obvious constraint,
|
|
55
|
+
invariant, or trade-off.
|
|
56
|
+
- Public API additions need a docstring.
|
|
57
|
+
- New diagnostic codes are registered in `core/symbols.py`'s
|
|
58
|
+
`CODES` dict.
|
|
59
|
+
- Performance work is welcome; please include before/after numbers
|
|
60
|
+
on the LMDZ trial workset (see `project_workspace_perf.md` for
|
|
61
|
+
the benchmark recipe) so we know we're moving the needle.
|
|
62
|
+
|
|
63
|
+
## Commits and pull requests
|
|
64
|
+
|
|
65
|
+
- Imperative subject under 70 characters
|
|
66
|
+
(`checker: do X`, not `did X`).
|
|
67
|
+
- Body explains motivation more than implementation. Mention
|
|
68
|
+
affected files, perf impact, test coverage, behavioural changes.
|
|
69
|
+
- One coherent change per commit. Use multiple commits in a PR if
|
|
70
|
+
the work has logically distinct phases.
|
|
71
|
+
- PRs should be rebased on the latest `main` before review.
|
|
72
|
+
|
|
73
|
+
## Companions
|
|
74
|
+
|
|
75
|
+
The editor integrations live in their own repositories and have
|
|
76
|
+
their own contribution flows:
|
|
77
|
+
|
|
78
|
+
- [DimFort-VSCompanion](https://github.com/ArrialVictor/DimFort-VSCompanion)
|
|
79
|
+
- [DimFort-NvimCompanion](https://github.com/ArrialVictor/DimFort-NvimCompanion)
|
|
80
|
+
- [DimFort-EmacsCompanion](https://github.com/ArrialVictor/DimFort-EmacsCompanion)
|
|
81
|
+
|
|
82
|
+
A change that affects the LSP protocol surface needs a matching
|
|
83
|
+
companion change; mention it in the DimFort PR so reviewers can
|
|
84
|
+
coordinate.
|
|
85
|
+
|
|
86
|
+
## License
|
|
87
|
+
|
|
88
|
+
By contributing, you agree your changes are licensed under the
|
|
89
|
+
project's MIT [LICENSE](LICENSE).
|
dimfort-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Victor Arrial
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|