tcl-ls 0.2.1__py3-none-any.whl

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.
Files changed (113) hide show
  1. tcl_ls-0.2.1.dist-info/METADATA +172 -0
  2. tcl_ls-0.2.1.dist-info/RECORD +113 -0
  3. tcl_ls-0.2.1.dist-info/WHEEL +4 -0
  4. tcl_ls-0.2.1.dist-info/entry_points.txt +4 -0
  5. tcl_ls-0.2.1.dist-info/licenses/LICENSE +21 -0
  6. tcl_lsp/__init__.py +7 -0
  7. tcl_lsp/__main__.py +11 -0
  8. tcl_lsp/analysis/__init__.py +49 -0
  9. tcl_lsp/analysis/arity.py +158 -0
  10. tcl_lsp/analysis/builtins.py +484 -0
  11. tcl_lsp/analysis/control_flow.py +207 -0
  12. tcl_lsp/analysis/diagnostics/__init__.py +57 -0
  13. tcl_lsp/analysis/diagnostics/ambiguous_command.py +25 -0
  14. tcl_lsp/analysis/diagnostics/ambiguous_variable.py +27 -0
  15. tcl_lsp/analysis/diagnostics/base.py +74 -0
  16. tcl_lsp/analysis/diagnostics/duplicate_proc.py +24 -0
  17. tcl_lsp/analysis/diagnostics/helpers.py +267 -0
  18. tcl_lsp/analysis/diagnostics/invalid_arguments.py +51 -0
  19. tcl_lsp/analysis/diagnostics/invalid_regex.py +151 -0
  20. tcl_lsp/analysis/diagnostics/missing_option_value.py +29 -0
  21. tcl_lsp/analysis/diagnostics/unknown_option.py +29 -0
  22. tcl_lsp/analysis/diagnostics/unknown_subcommand.py +28 -0
  23. tcl_lsp/analysis/diagnostics/unreachable_code.py +350 -0
  24. tcl_lsp/analysis/diagnostics/unresolved_command.py +25 -0
  25. tcl_lsp/analysis/diagnostics/unresolved_package.py +27 -0
  26. tcl_lsp/analysis/diagnostics/unresolved_variable.py +27 -0
  27. tcl_lsp/analysis/diagnostics/wrong_argument_count.py +90 -0
  28. tcl_lsp/analysis/embedded_languages.py +528 -0
  29. tcl_lsp/analysis/facts/__init__.py +3 -0
  30. tcl_lsp/analysis/facts/collector.py +3143 -0
  31. tcl_lsp/analysis/facts/lowering.py +1394 -0
  32. tcl_lsp/analysis/facts/parsing.py +302 -0
  33. tcl_lsp/analysis/facts/utils.py +300 -0
  34. tcl_lsp/analysis/flow/__init__.py +25 -0
  35. tcl_lsp/analysis/flow/exprs.py +388 -0
  36. tcl_lsp/analysis/flow/variables.py +561 -0
  37. tcl_lsp/analysis/index.py +361 -0
  38. tcl_lsp/analysis/metadata_commands.py +1698 -0
  39. tcl_lsp/analysis/metadata_effects.py +296 -0
  40. tcl_lsp/analysis/model.py +264 -0
  41. tcl_lsp/analysis/resolver.py +1196 -0
  42. tcl_lsp/analysis/signature_matching.py +287 -0
  43. tcl_lsp/cache.py +42 -0
  44. tcl_lsp/checker/__init__.py +10 -0
  45. tcl_lsp/checker/cli.py +116 -0
  46. tcl_lsp/checker/model.py +31 -0
  47. tcl_lsp/checker/reporting.py +425 -0
  48. tcl_lsp/checker/service.py +555 -0
  49. tcl_lsp/common.py +111 -0
  50. tcl_lsp/lsp/__init__.py +3 -0
  51. tcl_lsp/lsp/document_changes.py +138 -0
  52. tcl_lsp/lsp/features/__init__.py +1 -0
  53. tcl_lsp/lsp/features/completion.py +941 -0
  54. tcl_lsp/lsp/features/cursor_context.py +217 -0
  55. tcl_lsp/lsp/features/document_links.py +81 -0
  56. tcl_lsp/lsp/features/folding.py +107 -0
  57. tcl_lsp/lsp/features/highlights.py +76 -0
  58. tcl_lsp/lsp/features/hover.py +27 -0
  59. tcl_lsp/lsp/features/navigation.py +310 -0
  60. tcl_lsp/lsp/features/rename.py +348 -0
  61. tcl_lsp/lsp/features/signature_help.py +199 -0
  62. tcl_lsp/lsp/features/symbols.py +124 -0
  63. tcl_lsp/lsp/features/workspace_symbols.py +125 -0
  64. tcl_lsp/lsp/semantic_tokens.py +407 -0
  65. tcl_lsp/lsp/server.py +1255 -0
  66. tcl_lsp/lsp/state.py +57 -0
  67. tcl_lsp/lsp/workspace_rebuild.py +576 -0
  68. tcl_lsp/meta/__init__.py +63 -0
  69. tcl_lsp/meta/meta.meta.tcl +304 -0
  70. tcl_lsp/meta/plugins/host.tcl +140 -0
  71. tcl_lsp/meta/tcl8.6/msgcat.meta.tcl +24 -0
  72. tcl_lsp/meta/tcl8.6/tcl.meta.tcl +1878 -0
  73. tcl_lsp/meta/tcl8.6/tcloo.meta.tcl +74 -0
  74. tcl_lsp/meta/tcl8.6/tcltest.meta.tcl +132 -0
  75. tcl_lsp/meta/tcl8.6/tk.meta.tcl +114 -0
  76. tcl_lsp/meta/tcllib/asn.meta.tcl +75 -0
  77. tcl_lsp/meta/tcllib/clay.meta.tcl +55 -0
  78. tcl_lsp/meta/tcllib/cmdline.meta.tcl +22 -0
  79. tcl_lsp/meta/tcllib/doctools/text.meta.tcl +30 -0
  80. tcl_lsp/meta/tcllib/fileutil.meta.tcl +30 -0
  81. tcl_lsp/meta/tcllib/json/write.meta.tcl +27 -0
  82. tcl_lsp/meta/tcllib/json.meta.tcl +9 -0
  83. tcl_lsp/meta/tcllib/log.meta.tcl +12 -0
  84. tcl_lsp/meta/tcllib/logger.meta.tcl +35 -0
  85. tcl_lsp/meta/tcllib/oo/meta.meta.tcl +6 -0
  86. tcl_lsp/meta/tcllib/struct/set.meta.tcl +56 -0
  87. tcl_lsp/meta/tcllib/tepam.meta.tcl +7 -0
  88. tcl_lsp/meta/tcllib/tepam.tcl +68 -0
  89. tcl_lsp/meta/tcllib/testutilities.meta.tcl +34 -0
  90. tcl_lsp/meta/tcllib/textutil/adjust.meta.tcl +27 -0
  91. tcl_lsp/meta/tcllib/textutil/repeat.meta.tcl +9 -0
  92. tcl_lsp/meta/tcllib/textutil/split.meta.tcl +9 -0
  93. tcl_lsp/meta/tcllib/textutil/string.meta.tcl +24 -0
  94. tcl_lsp/meta/tcllib/textutil/tabify.meta.tcl +15 -0
  95. tcl_lsp/meta/tcllib/textutil/textutil.meta.tcl +78 -0
  96. tcl_lsp/meta/tcllib/textutil/trim.meta.tcl +18 -0
  97. tcl_lsp/meta/tcllib/textutil/wcswidth.meta.tcl +12 -0
  98. tcl_lsp/meta_tools.py +5 -0
  99. tcl_lsp/metadata_paths.py +122 -0
  100. tcl_lsp/parser/__init__.py +30 -0
  101. tcl_lsp/parser/expr.py +452 -0
  102. tcl_lsp/parser/helpers.py +103 -0
  103. tcl_lsp/parser/model.py +87 -0
  104. tcl_lsp/parser/parser.py +758 -0
  105. tcl_lsp/plugins/__init__.py +1 -0
  106. tcl_lsp/plugins/host.py +681 -0
  107. tcl_lsp/project/__init__.py +1 -0
  108. tcl_lsp/project/config.py +100 -0
  109. tcl_lsp/project/indexing.py +275 -0
  110. tcl_lsp/project/paths.py +66 -0
  111. tcl_lsp/project_config.py +17 -0
  112. tcl_lsp/tools/tcl_meta.tcl +353 -0
  113. tcl_lsp/workspace.py +15 -0
@@ -0,0 +1,172 @@
1
+ Metadata-Version: 2.4
2
+ Name: tcl-ls
3
+ Version: 0.2.1
4
+ Summary: Tcl language server, checker, and metadata toolkit.
5
+ Project-URL: Documentation, https://lewis6991.github.io/tcl-ls/
6
+ Project-URL: Homepage, https://github.com/lewis6991/tcl-ls
7
+ Project-URL: Issues, https://github.com/lewis6991/tcl-ls/issues
8
+ Project-URL: Repository, https://github.com/lewis6991/tcl-ls
9
+ Project-URL: Changelog, https://github.com/lewis6991/tcl-ls/blob/main/CHANGELOG.md
10
+ Author-email: Lewis Russell <lewis6991@gmail.com>
11
+ License-File: LICENSE
12
+ Keywords: language-server,lint,lsp,static-analysis,tcl
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Natural Language :: English
18
+ Classifier: Operating System :: OS Independent
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3 :: Only
21
+ Classifier: Programming Language :: Python :: 3.14
22
+ Classifier: Topic :: Software Development
23
+ Classifier: Topic :: Software Development :: Quality Assurance
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.14
26
+ Requires-Dist: pygls>=2.1.0
27
+ Description-Content-Type: text/markdown
28
+
29
+ # tcl-ls
30
+
31
+ A Tcl language server, checker, and metadata toolkit implemented in typed
32
+ Python.
33
+
34
+ ## Install
35
+
36
+ `tcl-ls` currently targets Python 3.14 or newer.
37
+
38
+ Install from PyPI with:
39
+
40
+ ```sh
41
+ python3 -m pip install tcl-ls
42
+ ```
43
+
44
+ You can also download packaged server archives and editor assets from the
45
+ [GitHub Releases](https://github.com/lewis6991/tcl-ls/releases) page.
46
+
47
+ This installs three command-line entry points:
48
+
49
+ * `tcl-ls` for the stdio language server
50
+ * `tcl-check` for batch diagnostics
51
+ * `tcl-meta` for Tcl metadata-helper workflows
52
+
53
+ For local development from a checkout, sync the environment once and prefer
54
+ `uv run ...` for commands you do not want to install globally:
55
+
56
+ ```sh
57
+ uv sync
58
+ ```
59
+
60
+ ## Current Scope
61
+
62
+ This is an alpha release aimed at early adopters. The server is already useful
63
+ for diagnostics, navigation, completion, rename, signature help, semantic
64
+ tokens, and metadata-assisted checking, but it still analyzes Tcl
65
+ conservatively and does not try to model the full dynamic runtime.
66
+
67
+ ## Documentation
68
+
69
+ Published docs are available at
70
+ <https://lewis6991.github.io/tcl-ls/>.
71
+
72
+ Build the Sphinx docs locally with:
73
+
74
+ ```sh
75
+ make docs
76
+ ```
77
+
78
+ The rendered site is written to `docs/_build/html/index.html` and covers
79
+ getting started, editor and checker workflows, metadata authoring, support,
80
+ and development notes.
81
+
82
+ Editor-specific helpers shipped in this repository:
83
+
84
+ * `editors/vscode` for the VS Code extension
85
+ * `editors/nvim` for the Neovim 0.11+ built-in LSP config
86
+
87
+ ## Maintenance
88
+
89
+ Refresh builtin subcommand metadata with:
90
+
91
+ ```sh
92
+ python3 scripts/generate_builtin_commands.py
93
+ ```
94
+
95
+ The generator updates `meta/tcl8.6/tcl.meta.tcl` in place and
96
+ supports `--input`, `--output`, `--doc-root`, `--tcl-doc-series`, and
97
+ `--version-label` for versioned metadata workflows.
98
+
99
+ ## Metadata
100
+
101
+ Metadata files use the `*.meta.tcl` suffix and `meta command` entries:
102
+
103
+ ```tcl
104
+ meta command regexp {args} {
105
+ option -start value
106
+ option -- stop
107
+ bind after-options 3..
108
+ }
109
+ ```
110
+
111
+ The optional body is declarative analysis metadata. Current annotations are:
112
+ `option`, `keyword`, `subcommand`, `bind`, `ref`, `script-body`,
113
+ `source`, and `package`.
114
+
115
+ ## Diagnostics
116
+
117
+ Install the package first so the CLI entry points are available:
118
+
119
+ ```sh
120
+ python3 -m pip install tcl-ls
121
+ ```
122
+
123
+ Analyze a Tcl file or project tree with:
124
+
125
+ ```sh
126
+ tcl-check path/to/project
127
+ ```
128
+
129
+ In an interactive terminal, the checker prepares package-scoped workspaces
130
+ (directories rooted by `pkgIndex.tcl`), then prints final grouped diagnostics
131
+ with source context and a finished summary as each workspace is analyzed. Use
132
+ `--color=always|never|auto` and `--context-lines=N` to tune the terminal
133
+ output.
134
+
135
+ To inspect the current diagnostics emitted for `tcllib`, use:
136
+
137
+ ```sh
138
+ make check-tcllib
139
+ ```
140
+
141
+ Pass extra checker flags through `make` with:
142
+
143
+ ```sh
144
+ make check-tcllib TCL_CHECK_ARGS="--context-lines=1 --fail-on-diagnostics"
145
+ ```
146
+
147
+ Project-local config can live in `tcllsrc.tcl`. Supported commands are:
148
+
149
+ ```tcl
150
+ plugin-path .tcl-ls/sample.tcl
151
+ lib-path ../tcllib
152
+ ```
153
+
154
+ Configured paths are resolved relative to the config file. `lib-path` roots are
155
+ scanned for `pkgIndex.tcl` files so external library trees can satisfy
156
+ `package require` without moving them into the project.
157
+
158
+ To build metadata inside a tool-specific Tcl shell, source the bundled helper
159
+ reported by:
160
+
161
+ ```sh
162
+ tcl-meta helper-path
163
+ ```
164
+
165
+ Then, inside that tool Tcl shell, write metadata directly with:
166
+
167
+ ```tcl
168
+ tcl-meta build-file output.meta.tcl
169
+ ```
170
+
171
+ For local development in this repository, use `uv run ...` when you want to
172
+ run the CLI without installing it first.
@@ -0,0 +1,113 @@
1
+ tcl_lsp/__init__.py,sha256=Z_jHDkh_mAq0LRipUViJMN0oNTAzHX6neKNuTUnkHGY,147
2
+ tcl_lsp/__main__.py,sha256=2YaqmK33z90UFfgA8BPtYZ9fH1fhiZQwNojFH-iwQeI,151
3
+ tcl_lsp/cache.py,sha256=bRIVwTgiEUcZGRC4eOeQUdKSnTqGXa8C76lZuTOUkyM,1139
4
+ tcl_lsp/common.py,sha256=6Fj3rM05s-VjdkMKH1WO27KZhWMEa8UmE8l-zUaoTYg,2991
5
+ tcl_lsp/meta_tools.py,sha256=RNhFSGZtik7bhXNFMLks74epgRhrmwgugsGvZd6cdUs,134
6
+ tcl_lsp/metadata_paths.py,sha256=oZ1pEuhaC93QmZpXw8ymfLBPlvWQQnAVZLmvw_4M_kM,4277
7
+ tcl_lsp/project_config.py,sha256=qkLYiryXKLh93D_JmTpdmEtchfFt0QeEnWiMSEDkeMs,360
8
+ tcl_lsp/workspace.py,sha256=EZWu5Uu5VT5KB3JRVlOivdu-5ACbV_56upk5fbXUIFQ,321
9
+ tcl_lsp/analysis/__init__.py,sha256=B6YiB56c4OTsBB4O2CUK8vLSVKs8Yrjq7xCZTJfmvig,1057
10
+ tcl_lsp/analysis/arity.py,sha256=bl2YCEPv-wk4xOrJMzOT9jxDOtfcYmAUke9rvTpgACo,5251
11
+ tcl_lsp/analysis/builtins.py,sha256=9VIAYyp3QIA5gsjHR-GkNSkvHV1CDkNXp4xAnEjcuuM,16778
12
+ tcl_lsp/analysis/control_flow.py,sha256=rPKZov0Yhev1ZEJBe3XTt_HmwCJo3lpH1ikAc9FSbAE,6534
13
+ tcl_lsp/analysis/embedded_languages.py,sha256=IL0c_m6ADdbEvv6NZa-Y0sQIv-C3xwsiqrL3Sw5WOKs,18081
14
+ tcl_lsp/analysis/index.py,sha256=T42xz9uw-U1IjWpFrXwNJHxsRyGaT-4uFOoiT1NM3U4,15397
15
+ tcl_lsp/analysis/metadata_commands.py,sha256=WAGpTFF_O18y6jn4mVse5rjWROZeW6NOJhHsRFRJGdM,61729
16
+ tcl_lsp/analysis/metadata_effects.py,sha256=Vkzj60HuFIYrXdVxm3v47CswS7tyCoaiSrLo1zYbkMk,10341
17
+ tcl_lsp/analysis/model.py,sha256=7ueeweDhw69XiFCcPFRwE6rDJJRSecNawtN0Kxl3wOw,5776
18
+ tcl_lsp/analysis/resolver.py,sha256=OX4PQSTmmF3n-UPWL1IGYcPCZUtXUazfW7_3l4mZdO0,44103
19
+ tcl_lsp/analysis/signature_matching.py,sha256=GI7Xtw90FHRyqfUigHROJ8zwjtaH5yZrXB6609cvld8,8488
20
+ tcl_lsp/analysis/diagnostics/__init__.py,sha256=CQzLP8hO2utYjX40bv8GElFYK2ofpvDfhKTbIkwl874,1763
21
+ tcl_lsp/analysis/diagnostics/ambiguous_command.py,sha256=5k3qfA6MslCtglYigjeEYI83i03Qb0FZv7UETGbXJUg,937
22
+ tcl_lsp/analysis/diagnostics/ambiguous_variable.py,sha256=rLnuSFr9GL4qtFKK8G--BtHOcn0mBZS5gIro9hPiC7Y,1004
23
+ tcl_lsp/analysis/diagnostics/base.py,sha256=B5_3UrHglgjooY8J6PrLLh-q_bRs6CwMVST-QwLpaI8,2071
24
+ tcl_lsp/analysis/diagnostics/duplicate_proc.py,sha256=Zf86DcxIjr44zZ5qgrRacr_9_VV_aADIhU4OammVcFE,843
25
+ tcl_lsp/analysis/diagnostics/helpers.py,sha256=UmVonvysV9gxjqbbOp8_s-spDeTbg6o5EYG3AqfKskw,9279
26
+ tcl_lsp/analysis/diagnostics/invalid_arguments.py,sha256=tnMcvs9DsQsVBZPcYpJ8x4bS-4Givze1kQvTcp_NwMw,2023
27
+ tcl_lsp/analysis/diagnostics/invalid_regex.py,sha256=9NFtZqXTidKdBQ5uH2VgikyKXtOWraZGK-v7lupsDkM,4532
28
+ tcl_lsp/analysis/diagnostics/missing_option_value.py,sha256=KLhyGTqzRC84wcgNwmK8Ovb5UMf_Dru2dmXdKiiGoyU,1088
29
+ tcl_lsp/analysis/diagnostics/unknown_option.py,sha256=IzEhFqlZbsRsN9WQVqFHgVJp6IIW0_MczKiq2Zez6AY,1068
30
+ tcl_lsp/analysis/diagnostics/unknown_subcommand.py,sha256=6hWGsRUNsAAURbpEQL3K16m8SFRZkiJ9Q7MYw_bNWMw,1030
31
+ tcl_lsp/analysis/diagnostics/unreachable_code.py,sha256=BRVFxqnjdpCa_z2RGqVLnznrL8pKh8tcP8LxaC9hSak,11033
32
+ tcl_lsp/analysis/diagnostics/unresolved_command.py,sha256=R5gN4A67z_FlJeYyb2gtH5fC7dbJ9oy42HoQTP68CIo,919
33
+ tcl_lsp/analysis/diagnostics/unresolved_package.py,sha256=GUmT_pRVYe9S4XHAg4QDlkRpY87Dou5kCcUs-g8fC34,999
34
+ tcl_lsp/analysis/diagnostics/unresolved_variable.py,sha256=-Q-XTxAAe108twSGAuDhpnqlRaSHhTgFf-PT8JXe8P8,988
35
+ tcl_lsp/analysis/diagnostics/wrong_argument_count.py,sha256=c84SuBJLAjrxZ2C-6V2YiYZVVELynSR1yVFK3KmT1xc,2979
36
+ tcl_lsp/analysis/facts/__init__.py,sha256=P4Qx6dokoG8MK7hlnp9baWqQA-2tCejKGC0Syuse2d4,88
37
+ tcl_lsp/analysis/facts/collector.py,sha256=iLLENMtIq_WqYe_SSUBkLafTMffoQ0CR1RnA9IKUQ-k,112276
38
+ tcl_lsp/analysis/facts/lowering.py,sha256=dqgX2KoquUdeuHDwJ1vlMrsyQaJrS7e3FZWenhCmQ8I,47385
39
+ tcl_lsp/analysis/facts/parsing.py,sha256=qnc-y1rsyjWSm2P7y3TjCjowGMlG9NbTRRpZuEZ2Qdg,9621
40
+ tcl_lsp/analysis/facts/utils.py,sha256=9NA0rwZ3rllSkASrZjIl_YeOjsPb2K6WeZEZ3GL2eZQ,8697
41
+ tcl_lsp/analysis/flow/__init__.py,sha256=8ikNrMKDvjHH0GmQ8Iqj_RID3ZpgsqjbrA8wZoCMzkY,651
42
+ tcl_lsp/analysis/flow/exprs.py,sha256=HifYM4iaaiMIkVEhTyFfW84bQRkqNZUCCa7wkiNHczU,12893
43
+ tcl_lsp/analysis/flow/variables.py,sha256=ZtHNSBnZaARP_FmbfTAqdJrssOt1fvHF_4Qq806u-ug,16579
44
+ tcl_lsp/checker/__init__.py,sha256=8zH7jAyrKmGeg2hNwAa-hccY93JWZjc8ypgR4B1jDkA,304
45
+ tcl_lsp/checker/cli.py,sha256=sDt7AlwT32PrOIv_j67j3VV4d-grXmYs6cS3jpMLqJE,3481
46
+ tcl_lsp/checker/model.py,sha256=1908XxAhwzZNOQgBncwgm7iqet7qQZwUqGCrSafvluk,772
47
+ tcl_lsp/checker/reporting.py,sha256=hNwUOH5r1Wsfixs1a6DIXe2fyl1r6ALdXsf0gq-vrT8,13174
48
+ tcl_lsp/checker/service.py,sha256=Lrx3KutMXhgL8bf-tiwkuLANsXEVmszqIvRLh4xIyfE,17930
49
+ tcl_lsp/lsp/__init__.py,sha256=uFcP3Faz7XRcwAW0hL3YQMkwGiP1LeIl0XJ7shn_4Us,94
50
+ tcl_lsp/lsp/document_changes.py,sha256=HbsVdiDQPeZtTFWeg88irjuDOvyNB8-Wg2otZzL8U2o,5293
51
+ tcl_lsp/lsp/semantic_tokens.py,sha256=ruuzqm0lxNHC5XtZFua8J8BfeJH8FehqicqwL3MqngE,12539
52
+ tcl_lsp/lsp/server.py,sha256=ng5UqLdlY6BifhI0e7cTnSeGruiwtI7WS_SZn2tyToM,44132
53
+ tcl_lsp/lsp/state.py,sha256=ERvfUwYGOLb7kZQ0NDIRjRcxO62r-asK9wDWQX_yArU,1493
54
+ tcl_lsp/lsp/workspace_rebuild.py,sha256=m5UomA9p81eM7K9OqO6nWztL7O2Q7-Kt1oPMe_552wU,21458
55
+ tcl_lsp/lsp/features/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
56
+ tcl_lsp/lsp/features/completion.py,sha256=oGiXwUaQLoRAGhWDP-Jbz7AqX0kZTylGvunuaeJM_G8,29996
57
+ tcl_lsp/lsp/features/cursor_context.py,sha256=TsOHN9J7S5Pr32TqlmT9zEsWEZ9ot-6Nxef58YaKxQ8,7051
58
+ tcl_lsp/lsp/features/document_links.py,sha256=S6qi62vR85IFTSdiswbemmZ62f_SkZa1xwbFeiSjT_A,2179
59
+ tcl_lsp/lsp/features/folding.py,sha256=zkWUhmybb59M7x6yCbtpEcM0ZxhtEouLL5odRCTmrPM,3418
60
+ tcl_lsp/lsp/features/highlights.py,sha256=SlNH7uOl6REG0AHGk7RQEAuIz6VAs-S_Cie4uPFVcWU,2105
61
+ tcl_lsp/lsp/features/hover.py,sha256=BuPQ2-tqKGk7XdWK8Jril2oB0kxAdGc_NtVfnFNn1kA,664
62
+ tcl_lsp/lsp/features/navigation.py,sha256=8_1kxmXHU0iD-vjsQXvSRtYlUQxhW6otJleHGWTgG2Q,9532
63
+ tcl_lsp/lsp/features/rename.py,sha256=06rYI8tLKl4MXohgL7vYvmis_xD923eO7eFzihBT4iQ,10769
64
+ tcl_lsp/lsp/features/signature_help.py,sha256=yzcHwkPI4oyBMhwn73juhX3g3ONSh4BzWu4w75G0M-U,6329
65
+ tcl_lsp/lsp/features/symbols.py,sha256=ezX-iztPbsxuKfb0m0cjuk8hneBCNenn-zm2DcUV_-0,4319
66
+ tcl_lsp/lsp/features/workspace_symbols.py,sha256=yFWO5IpQoAvlQbIbRmRndcN3Yk7-raTcQ2AZdthxGjo,4064
67
+ tcl_lsp/meta/__init__.py,sha256=0BQGmFI-DN86Qk3u8sosI4jrqu5zm_WpKkjeWpQ0enY,1843
68
+ tcl_lsp/parser/__init__.py,sha256=Xcn-8Fhc5VrUF7hNQPyZc8iadXkZsowcSqaFZL4H8tI,607
69
+ tcl_lsp/parser/expr.py,sha256=0ME4Xgb7y8N3l5SonQDERYA95_0V7OCpIEZQ3LicKe0,14338
70
+ tcl_lsp/parser/helpers.py,sha256=p3ZO8gNuuDa8YIkwPkLdlr421dCc6EsyzivZxrphegY,2803
71
+ tcl_lsp/parser/model.py,sha256=FXTRIi2XgjZxbtf0P0pk1xo-YVydGa9UajyWwM28tE4,1650
72
+ tcl_lsp/parser/parser.py,sha256=CaSHIFfK2zYTWPeQhU_q2j9gDzu3XCzAjqgZOLmZ5zE,26994
73
+ tcl_lsp/plugins/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
74
+ tcl_lsp/plugins/host.py,sha256=VNUsONnD5v8GTr2CmXmMy3L9rIYLBWhEMsf1fhlfYY8,25309
75
+ tcl_lsp/project/__init__.py,sha256=-OIX0tODIsxobWvnL_znXzf9Bc6-pJn14IujHZBGulM,57
76
+ tcl_lsp/project/config.py,sha256=J-6cOlpiTrLf60SvnsJH0yVWAAuLYM8zCNeaunPX5gQ,3497
77
+ tcl_lsp/project/indexing.py,sha256=7g7nfQEgLtnE81FQm4j9CENejY-hXyQtnnAFwyTibLg,9517
78
+ tcl_lsp/project/paths.py,sha256=vNa1gv9G23GAnPtTxWCDs98WgVEZmLTb1MrjxJLNJMo,1828
79
+ tcl_lsp/meta/meta.meta.tcl,sha256=l3Yzbk2ctP4esFeh9drnfL8oOm1GuI2Gt0bSLwrP7WY,10570
80
+ tcl_lsp/meta/plugins/host.tcl,sha256=e-19IW8gTYtDhalP7N0Y61cY0MMN7q3S4kKoINyZNmA,3646
81
+ tcl_lsp/meta/tcl8.6/msgcat.meta.tcl,sha256=N6GfkbhNCehL5CSG21sZmr1jZqWrUirXB_53exoqThs,791
82
+ tcl_lsp/meta/tcl8.6/tcl.meta.tcl,sha256=UNW3jrypZNR29DtYM8hLGyPp85ESMSINHtF1eGlYICE,77898
83
+ tcl_lsp/meta/tcl8.6/tcloo.meta.tcl,sha256=kxHMfZv036lcQ2wOrmfV0ymqhUY10uLu9Qsg2IRZpBA,1844
84
+ tcl_lsp/meta/tcl8.6/tcltest.meta.tcl,sha256=Ywxvd7hlY162kmYJWiSInxXBqPtvWGr-lnSp4MYGbYQ,4331
85
+ tcl_lsp/meta/tcl8.6/tk.meta.tcl,sha256=WmM2eQ3OFe3Ryhw9-sPFUpbS8fDtw8m4dVUYY-hr_UM,2918
86
+ tcl_lsp/meta/tcllib/asn.meta.tcl,sha256=JSuFYV5sE2QzlznPsydoZYg0oLpcAXOk_1qCZCvdd9A,2125
87
+ tcl_lsp/meta/tcllib/clay.meta.tcl,sha256=tI0LJ5SjS6JPhpDrgR9fDm3QnlE-2SLFcXH4xMmhPhU,1747
88
+ tcl_lsp/meta/tcllib/cmdline.meta.tcl,sha256=Dkp3iLNhQ8X2_erqWSgLYCe4sxT0HuEH0zqwIYI6bKw,843
89
+ tcl_lsp/meta/tcllib/fileutil.meta.tcl,sha256=BlQBd4-fmiI3ZJ9hMMiegPuhZ_tWGt8qQEOV1yICX1M,1024
90
+ tcl_lsp/meta/tcllib/json.meta.tcl,sha256=7M5aENYJrwI6eLRop8oR-EwHsBHOhlliXdwqZrSXXx0,331
91
+ tcl_lsp/meta/tcllib/log.meta.tcl,sha256=yKiclBUzACe_SPXHQSoZ3ebGxLYqEcjpNi-cEO2TucI,335
92
+ tcl_lsp/meta/tcllib/logger.meta.tcl,sha256=nMB5gntetkbTp7G4Va8dy1WPk8suoY0m07YkYuM-Bv4,964
93
+ tcl_lsp/meta/tcllib/tepam.meta.tcl,sha256=W7S_WHtCe8RbOJa5pFH9S2FDWDt56ljcfXc2drDOGFU,235
94
+ tcl_lsp/meta/tcllib/tepam.tcl,sha256=KlLM_4Y_537VlSbE1T3YNiC0-CEGpXTvfNr9s093rpA,1763
95
+ tcl_lsp/meta/tcllib/testutilities.meta.tcl,sha256=2pf7mGU7dJIcRceeAHZA1mry5BRGFdKGrGWfS2QxUDI,864
96
+ tcl_lsp/meta/tcllib/doctools/text.meta.tcl,sha256=4gG8qWM_uKFL1A713K5GyXcUA5b1LWuiftjZrokYZZY,881
97
+ tcl_lsp/meta/tcllib/json/write.meta.tcl,sha256=-lz2Mw3i9sp66jlSqmJD0GMNZuIqLsD94xWxTTCGpJo,795
98
+ tcl_lsp/meta/tcllib/oo/meta.meta.tcl,sha256=EUbE8boQ22olznFIArckbCC3FNxrZhpIv9BywTU5tOI,262
99
+ tcl_lsp/meta/tcllib/struct/set.meta.tcl,sha256=s6LGaXx41TziyzD8M4NvzJErzVxMoGdTz_COobI7YcM,1502
100
+ tcl_lsp/meta/tcllib/textutil/adjust.meta.tcl,sha256=SZy3iVa-B1kaVZ37vhwWMPlOITt5Z1MQco5wIMuynzY,881
101
+ tcl_lsp/meta/tcllib/textutil/repeat.meta.tcl,sha256=POfuzlCm18yqDDUUE-mP3pfVhUlzPTrID--b10XTuZ0,338
102
+ tcl_lsp/meta/tcllib/textutil/split.meta.tcl,sha256=gDplp6P__nj-AayBQRJbh5WJNAyBZJRLRugYGS0PQkc,346
103
+ tcl_lsp/meta/tcllib/textutil/string.meta.tcl,sha256=F6_0DP4Pnwcq9YXnLthJqfx1U7CuijsPN3uvAhcC7Uc,850
104
+ tcl_lsp/meta/tcllib/textutil/tabify.meta.tcl,sha256=7LJ9tM-k5ayL8Z4gXKm9dAUtgPMAJnJ4wt9nWicgN9k,574
105
+ tcl_lsp/meta/tcllib/textutil/textutil.meta.tcl,sha256=7E-FPNOv9BCnZF2PBFAI6DMSij0863YGLsK0tGHWnIc,2418
106
+ tcl_lsp/meta/tcllib/textutil/trim.meta.tcl,sha256=vNfJKhvtbM7WhlxW2YMjJs7yivGWBFJ4ZvewVwzrtIQ,690
107
+ tcl_lsp/meta/tcllib/textutil/wcswidth.meta.tcl,sha256=i8ed55gOWRuqTMttOhL7YUfX-2Bn6AGoAI2kI1HZEqk,462
108
+ tcl_lsp/tools/tcl_meta.tcl,sha256=N_maNu6-6O3rjyB4iJVkVpUvGCM1gXplmy7maJB_hzQ,10590
109
+ tcl_ls-0.2.1.dist-info/METADATA,sha256=FWIbSMtU_FvUmKhDGrXFOfU4_u6x1DOBBmDKC1qH_sE,4795
110
+ tcl_ls-0.2.1.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
111
+ tcl_ls-0.2.1.dist-info/entry_points.txt,sha256=NskNMhUhsbnrx-F0dLgEbIud05yZn9rUHECWx83T9RQ,111
112
+ tcl_ls-0.2.1.dist-info/licenses/LICENSE,sha256=0IRROPkZlOEEIQsh7ZIPstOEIqUoU1IFm1i5HntE8s8,1070
113
+ tcl_ls-0.2.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.30.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,4 @@
1
+ [console_scripts]
2
+ tcl-check = tcl_lsp.checker:main
3
+ tcl-ls = tcl_lsp.__main__:main
4
+ tcl-meta = tcl_lsp.meta:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Lewis Russell
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.
tcl_lsp/__init__.py ADDED
@@ -0,0 +1,7 @@
1
+ """Typed Tcl language server package."""
2
+
3
+ __all__ = ['__version__']
4
+
5
+ # x-release-please-start-version
6
+ __version__ = '0.2.1'
7
+ # x-release-please-end
tcl_lsp/__main__.py ADDED
@@ -0,0 +1,11 @@
1
+ from __future__ import annotations
2
+
3
+ from tcl_lsp.lsp import server
4
+
5
+
6
+ def main() -> None:
7
+ server.start_io()
8
+
9
+
10
+ if __name__ == '__main__':
11
+ main()
@@ -0,0 +1,49 @@
1
+ from tcl_lsp.analysis.facts import FactExtractor
2
+ from tcl_lsp.analysis.index import WorkspaceIndex
3
+ from tcl_lsp.analysis.model import (
4
+ AnalysisResult,
5
+ AnalysisUncertainty,
6
+ CommandArity,
7
+ CommandCall,
8
+ CommandImport,
9
+ DefinitionTarget,
10
+ DocumentFacts,
11
+ NamespaceScope,
12
+ PackageIndexEntry,
13
+ PackageProvide,
14
+ PackageRequire,
15
+ ParameterDecl,
16
+ ProcDecl,
17
+ ReferenceSite,
18
+ ResolutionResult,
19
+ ResolvedReference,
20
+ SourceDirective,
21
+ VarBinding,
22
+ VariableReference,
23
+ )
24
+ from tcl_lsp.analysis.resolver import Resolver
25
+
26
+ __all__ = [
27
+ 'AnalysisResult',
28
+ 'AnalysisUncertainty',
29
+ 'CommandArity',
30
+ 'CommandCall',
31
+ 'CommandImport',
32
+ 'DefinitionTarget',
33
+ 'DocumentFacts',
34
+ 'FactExtractor',
35
+ 'NamespaceScope',
36
+ 'PackageIndexEntry',
37
+ 'PackageProvide',
38
+ 'PackageRequire',
39
+ 'ParameterDecl',
40
+ 'ProcDecl',
41
+ 'ReferenceSite',
42
+ 'ResolutionResult',
43
+ 'ResolvedReference',
44
+ 'Resolver',
45
+ 'SourceDirective',
46
+ 'VarBinding',
47
+ 'VariableReference',
48
+ 'WorkspaceIndex',
49
+ ]
@@ -0,0 +1,158 @@
1
+ from __future__ import annotations
2
+
3
+ from tcl_lsp.analysis.facts.parsing import ListItem, is_simple_name, split_tcl_list
4
+ from tcl_lsp.analysis.model import CommandArity
5
+ from tcl_lsp.common import Position
6
+
7
+ _ZERO_POSITION = Position(offset=0, line=0, character=0)
8
+ _DEFAULT_LITERAL_KEYWORDS = frozenset({'start'})
9
+
10
+
11
+ def proc_parameter_arity(items: tuple[ListItem, ...]) -> CommandArity | None:
12
+ min_args = 0
13
+ max_args = 0
14
+
15
+ for index, item in enumerate(items):
16
+ parameter = _proc_parameter_spec(item)
17
+ if parameter is None:
18
+ return None
19
+
20
+ name, optional = parameter
21
+ if name == 'args':
22
+ if optional or index != len(items) - 1:
23
+ return None
24
+ return CommandArity(min_args=min_args, max_args=None)
25
+
26
+ if not optional:
27
+ min_args += 1
28
+ max_args += 1
29
+
30
+ return CommandArity(min_args=min_args, max_args=max_args)
31
+
32
+
33
+ def metadata_signature_arity(signature: str) -> CommandArity | None:
34
+ text = signature.strip()
35
+ if not text or text == '{}':
36
+ return CommandArity(min_args=0, max_args=0)
37
+
38
+ items = tuple(item.text for item in split_tcl_list(text, _ZERO_POSITION))
39
+ if not items or items == ('',):
40
+ return CommandArity(min_args=0, max_args=0)
41
+ return _signature_items_arity(items)
42
+
43
+
44
+ def _signature_items_arity(items: tuple[str, ...]) -> CommandArity | None:
45
+ min_args = 0
46
+ max_args = 0
47
+ index = 0
48
+
49
+ while index < len(items):
50
+ token = items[index]
51
+ if token == '?':
52
+ try:
53
+ group_end = items.index('?', index + 1)
54
+ except ValueError:
55
+ return None
56
+
57
+ group_arity = _optional_group_arity(items[index + 1 : group_end])
58
+ if group_arity is None:
59
+ return None
60
+
61
+ max_args += group_arity.max_args
62
+ index = group_end + 1
63
+ continue
64
+
65
+ token_arity = _signature_token_arity(token, is_last=index == len(items) - 1)
66
+ if token_arity is None:
67
+ return None
68
+
69
+ min_args += token_arity.min_args
70
+ if token_arity.max_args is None:
71
+ if index != len(items) - 1:
72
+ return None
73
+ return CommandArity(min_args=min_args, max_args=None)
74
+
75
+ max_args += token_arity.max_args
76
+ index += 1
77
+
78
+ return CommandArity(min_args=min_args, max_args=max_args)
79
+
80
+
81
+ def _optional_group_arity(tokens: tuple[str, ...]) -> CommandArity | None:
82
+ if not tokens:
83
+ return None
84
+
85
+ max_args = 0
86
+ for token in tokens:
87
+ token_arity = _signature_token_arity(token, is_last=False)
88
+ if token_arity is None or token_arity.max_args is None:
89
+ return None
90
+ max_args += token_arity.max_args
91
+ return CommandArity(min_args=0, max_args=max_args)
92
+
93
+
94
+ def _signature_token_arity(token: str, *, is_last: bool) -> CommandArity | None:
95
+ if not token:
96
+ return CommandArity(min_args=0, max_args=0)
97
+ if token.startswith('=') and len(token) > 1:
98
+ return CommandArity(min_args=1, max_args=1)
99
+ if token.startswith('<') and token.endswith('>') and len(token) > 2:
100
+ slot_name = token[1:-1]
101
+ if slot_name in {'selector', 'bodySelector', 'ownerSelector', 'procedureSelector'}:
102
+ # Selectors range from one word (`1`) to five words
103
+ # (`after-options list 1..last step 2`).
104
+ return CommandArity(min_args=1, max_args=5)
105
+ return CommandArity(min_args=1, max_args=1)
106
+ if token == 'args':
107
+ if not is_last:
108
+ return None
109
+ return CommandArity(min_args=0, max_args=None)
110
+ if _is_safe_inline_optional_literal(token):
111
+ return CommandArity(min_args=0, max_args=1)
112
+ if token == '?' or '...' in token or '|' in token or '?' in token:
113
+ return None
114
+ if not _has_internal_whitespace(token):
115
+ return CommandArity(min_args=1, max_args=1)
116
+
117
+ default_arity = _default_like_token_arity(token)
118
+ if default_arity is not None:
119
+ return default_arity
120
+ return None
121
+
122
+
123
+ def _proc_parameter_spec(item: ListItem) -> tuple[str, bool] | None:
124
+ if not _has_internal_whitespace(item.text):
125
+ return item.text, False
126
+
127
+ subitems = split_tcl_list(item.text, item.content_start)
128
+ if len(subitems) != 2 or not is_simple_name(subitems[0].text):
129
+ return None
130
+ return subitems[0].text, True
131
+
132
+
133
+ def _default_like_token_arity(token: str) -> CommandArity | None:
134
+ subitems = split_tcl_list(token, _ZERO_POSITION)
135
+ if len(subitems) != 2 or not is_simple_name(subitems[0].text):
136
+ return None
137
+
138
+ default_value = subitems[1].text
139
+ if (
140
+ not default_value
141
+ or not is_simple_name(default_value)
142
+ or default_value.isdigit()
143
+ or default_value.startswith('-')
144
+ or default_value in _DEFAULT_LITERAL_KEYWORDS
145
+ ):
146
+ return CommandArity(min_args=0, max_args=1)
147
+ return None
148
+
149
+
150
+ def _is_safe_inline_optional_literal(token: str) -> bool:
151
+ if len(token) <= 2 or not token.startswith('?') or not token.endswith('?'):
152
+ return False
153
+ inner = token[1:-1]
154
+ return bool(inner) and inner.startswith('-') and '?' not in inner and '...' not in inner
155
+
156
+
157
+ def _has_internal_whitespace(text: str) -> bool:
158
+ return any(char.isspace() for char in text)