docscribe 1.6.0 → 1.6.2
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.
- checksums.yaml +4 -4
- data/README.md +76 -193
- data/exe/docscribe-client +26 -7
- data/lib/docscribe/cli/config_builder.rb +37 -2
- data/lib/docscribe/cli/coverage.rb +5 -5
- data/lib/docscribe/cli/formatters/json.rb +74 -29
- data/lib/docscribe/cli/formatters/sarif.rb +20 -3
- data/lib/docscribe/cli/options.rb +17 -2
- data/lib/docscribe/cli/rbs_gen.rb +4 -4
- data/lib/docscribe/cli/run.rb +107 -24
- data/lib/docscribe/cli/update_types.rb +61 -17
- data/lib/docscribe/cli.rb +19 -13
- data/lib/docscribe/config/defaults.rb +1 -0
- data/lib/docscribe/config/rbs.rb +22 -1
- data/lib/docscribe/config/template.rb +3 -0
- data/lib/docscribe/config/validation.rb +19 -0
- data/lib/docscribe/config.rb +1 -0
- data/lib/docscribe/infer/behavior.rb +13 -13
- data/lib/docscribe/infer/params.rb +2 -2
- data/lib/docscribe/infer/raises.rb +5 -6
- data/lib/docscribe/infer/returns.rb +1612 -151
- data/lib/docscribe/infer.rb +7 -7
- data/lib/docscribe/inline_rewriter/doc_builder.rb +485 -102
- data/lib/docscribe/inline_rewriter.rb +263 -97
- data/lib/docscribe/plugin/registry.rb +1 -0
- data/lib/docscribe/server/base.rb +255 -0
- data/lib/docscribe/server/client.rb +95 -0
- data/lib/docscribe/server/daemon.rb +678 -0
- data/lib/docscribe/server/protocol.rb +50 -0
- data/lib/docscribe/server.rb +4 -835
- data/lib/docscribe/types/primitive.rb +160 -0
- data/lib/docscribe/types/sorbet/base_provider.rb +33 -1
- data/lib/docscribe/types/yard/formatter.rb +35 -6
- data/lib/docscribe/types/yard/parser.rb +25 -20
- data/lib/docscribe/types/yard/validator.rb +131 -0
- data/lib/docscribe/validator/generic_compatibility.rb +698 -0
- data/lib/docscribe/validator/type_mismatch_validator.rb +287 -0
- data/lib/docscribe/version.rb +1 -1
- metadata +12 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3c7164ab1529448e4446137381b717690e22d57ced3cf9042be38ab00ff5f470
|
|
4
|
+
data.tar.gz: 7dc01723974b29fe8a6179220d04ffe3c9141e471c3ec1fbed309b9e5a780a5e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cee99e8d318b327bc64786555c0347cc995e6a23d5fb42863c13efda6e6467bdc5032bd37f1a7b518ca3f4c41c5967034f278664f0b9ee1110d712f01011f000
|
|
7
|
+
data.tar.gz: 59ab6f4b18a349fc9e40c3b39f85dd331c76f606bb9162ff410b11e40338f9a3e3d05e8ab002e345c6ef3be47a1ee524cc9d12649ce7d0a7deb149c376c6edd5
|
data/README.md
CHANGED
|
@@ -78,6 +78,7 @@ docscribe -A lib
|
|
|
78
78
|
* [Installation](#installation)
|
|
79
79
|
* [Architecture](#architecture)
|
|
80
80
|
* [Data flow](#data-flow)
|
|
81
|
+
* [IDE plugin and gem interaction](#ide-plugin-and-gem-interaction)
|
|
81
82
|
* [CLI](#cli)
|
|
82
83
|
* [Exit codes](#exit-codes)
|
|
83
84
|
* [Options](#options)
|
|
@@ -175,193 +176,31 @@ In server mode, a persistent daemon (`docscribe server`) keeps the runtime loade
|
|
|
175
176
|
invocations via an LRU cache, enabling near-instant repeated checks for IDE plugins. A thin client (`docscribe-client`)
|
|
176
177
|
provides minimal-overhead socket communication without loading the full gem.
|
|
177
178
|
|
|
178
|
-
|
|
179
|
-
flowchart TB
|
|
180
|
-
subgraph CLI["CLI Layer"]
|
|
181
|
-
Exe["exe/docscribe\nEntry point"]
|
|
182
|
-
Run["CLI::Run\nMain execution\n· expand paths\n· iterate files\n· report results"]
|
|
183
|
-
Options["CLI::Options\nARGV parsing\n(mode, strategy,\nfilters, flags)"]
|
|
184
|
-
InitCmd["CLI::Init\ndocscribe init\nGenerate config"]
|
|
185
|
-
GenCmd["CLI::Generate\ndocscribe generate\nScaffold plugins"]
|
|
186
|
-
SigsCmd["CLI::Sigs\ndocscribe sigs\nCheck RBS coverage"]
|
|
187
|
-
RbsGenCmd["CLI::RbsGen\ndocscribe rbs\nGenerate RBS from YARD"]
|
|
188
|
-
SarifFormatter["CLI::Formatters::Sarif\nSARIF 2.1 JSON\nCode Scanning"]
|
|
189
|
-
ConfigBuilder["CLI::ConfigBuilder\nApply CLI overrides\nto config"]
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
subgraph Config["Configuration"]
|
|
193
|
-
ConfigClass["Config\nCentral config object\n· raw hash\n· query methods"]
|
|
194
|
-
Defaults["config/defaults.rb\nDEFAULT hash"]
|
|
195
|
-
Loader["config/loader.rb\nYAML loading\n+ deep merge"]
|
|
196
|
-
Emit["config/emit.rb\nEmission toggles\n(header, tags, etc.)"]
|
|
197
|
-
Filtering["config/filtering.rb\nFile/method\ninclude/exclude"]
|
|
198
|
-
RBSConfig["config/rbs.rb\nRBS provider\nfactory"]
|
|
199
|
-
SorbetConfig["config/sorbet.rb\nSorbet provider\nchain factory"]
|
|
200
|
-
PluginConfig["config/plugin.rb\nPlugin loading\nfrom YAML"]
|
|
201
|
-
end
|
|
179
|
+
<img src="assets/diagrams/architecture.svg" alt="Docscribe architecture">
|
|
202
180
|
|
|
203
|
-
|
|
204
|
-
ParsingModule["Parsing\nBackend selection\n(:parser / :prism)"]
|
|
205
|
-
ParserGem["Parser gem\n(whitequark/parser)"]
|
|
206
|
-
Prism["Prism translator\n(Ruby 3.4+)"]
|
|
207
|
-
end
|
|
181
|
+
Source: [assets/diagrams/architecture.mmd](assets/diagrams/architecture.mmd) — regenerate with `bash assets/diagrams/render.sh`.
|
|
208
182
|
|
|
209
|
-
|
|
210
|
-
InlineRewriter["InlineRewriter\n· parse -> collect\n· deduplicate -> dispatch\n· rewrite"]
|
|
211
|
-
Collector["Collector\n< Parser::AST::Processor\nAST walker\n· find methods/attrs\n· track visibility\n· track containers"]
|
|
212
|
-
DocBuilder["DocBuilder\nGenerate YARD doc lines\n· combine inference\n· external signatures\n· plugin tags"]
|
|
213
|
-
DocBlock["DocBlock\nSafe strategy:\nparse -> merge -> sort\nexisting doc blocks"]
|
|
214
|
-
SourceHelpers["SourceHelpers\nPosition/range\nutilities"]
|
|
215
|
-
end
|
|
183
|
+
### Data flow
|
|
216
184
|
|
|
217
|
-
|
|
218
|
-
InferModule["Infer\nEntry point"]
|
|
219
|
-
Params["Infer::Params\nParameter type\nfrom name + default"]
|
|
220
|
-
Returns["Infer::Returns\nReturn type\nfrom method body"]
|
|
221
|
-
Raises["Infer::Raises\n@raise tags\nfrom raise/rescue"]
|
|
222
|
-
Literals["Infer::Literals\nAST literal ->\ntype string"]
|
|
223
|
-
Names["Infer::Names\n:const node ->\nFQN string"]
|
|
224
|
-
ASTWalk["Infer::ASTWalk\nRecursive DFS\nAST traversal"]
|
|
225
|
-
end
|
|
185
|
+
<img src="assets/diagrams/dataflow.svg" alt="Docscribe data flow">
|
|
226
186
|
|
|
227
|
-
|
|
228
|
-
PluginModule["Plugin\nTag/Collector\ndispatch"]
|
|
229
|
-
Registry["Plugin::Registry\nGlobal registry\n· register -> route\n· tag_entries\n· collector_entries"]
|
|
230
|
-
TagPlugin["Base::TagPlugin\nOverride #call(context)\n-> Array<Tag>"]
|
|
231
|
-
CollectorPlugin["Base::CollectorPlugin\nOverride #collect(ast, buffer)\n-> Array<Hash>"]
|
|
232
|
-
TagValue["Plugin::Tag\nStruct (name, text, types)"]
|
|
233
|
-
Context["Plugin::Context\nMethod snapshot struct"]
|
|
234
|
-
end
|
|
187
|
+
Source: [assets/diagrams/dataflow.mmd](assets/diagrams/dataflow.mmd) — regenerate with `bash assets/diagrams/render.sh`.
|
|
235
188
|
|
|
236
|
-
|
|
237
|
-
ProviderChain["ProviderChain\nComposite:\nquery in order\nfirst match wins"]
|
|
238
|
-
RBSProvider["RBS::Provider\n.rbs files\n-> RBS lib"]
|
|
239
|
-
RBSFormatter["RBS::TypeFormatter\nRBS type ->\nYARD type string"]
|
|
240
|
-
RBSCollection["RBS::CollectionLoader\nrbs_collection\n.lock.yaml"]
|
|
241
|
-
SorbetBase["Sorbet::BaseProvider\nRBS::Prototype::RBI\nbridge"]
|
|
242
|
-
SorbetSource["Sorbet::SourceProvider\nInline sig{}\ndeclarations"]
|
|
243
|
-
SorbetRBI["Sorbet::RBIProvider\n.rbi files\ndirectories"]
|
|
244
|
-
end
|
|
189
|
+
### IDE plugin and gem interaction
|
|
245
190
|
|
|
246
|
-
|
|
247
|
-
YParser["Yard::Parser\nParse YARD type\nstrings -> AST"]
|
|
248
|
-
YFormatter["Yard::Formatter\nYARD AST ->\nRBS string"]
|
|
249
|
-
YTypes["Yard::Types\n9 AST node types\n(Named, Generic, etc.)"]
|
|
250
|
-
end
|
|
191
|
+
<img src="assets/diagrams/plugin-gem.svg" alt="RubyMine plugin and gem interaction">
|
|
251
192
|
|
|
252
|
-
|
|
253
|
-
ThinClient["exe/docscribe-client\nThin client\n· socket send/receive\n· no gem load"]
|
|
254
|
-
ServerDaemon["Server::Daemon\nSocket listener\n· check / fix / shutdown\n· JSON-RPC 2.0"]
|
|
255
|
-
Cache["Docscribe::LRUCache\nFile result cache\n(max 1000, by mtime)"]
|
|
256
|
-
end
|
|
193
|
+
Source: [assets/diagrams/plugin-gem.mmd](assets/diagrams/plugin-gem.mmd) — regenerate with `bash assets/diagrams/render.sh`.
|
|
257
194
|
|
|
258
|
-
|
|
259
|
-
Exe --> ServerDaemon
|
|
260
|
-
ThinClient --> ServerDaemon
|
|
261
|
-
ServerDaemon --> ConfigClass
|
|
262
|
-
ServerDaemon --> Cache
|
|
263
|
-
ServerDaemon --> InlineRewriter
|
|
264
|
-
Run --> Options
|
|
265
|
-
Run --> InitCmd
|
|
266
|
-
Run --> GenCmd
|
|
267
|
-
Run --> SigsCmd
|
|
268
|
-
Run --> RbsGenCmd
|
|
269
|
-
Run --> SarifFormatter
|
|
270
|
-
Run --> ConfigBuilder
|
|
271
|
-
ConfigBuilder --> ConfigClass
|
|
272
|
-
ConfigBuilder --> ServerDaemon
|
|
273
|
-
ConfigClass --> Defaults
|
|
274
|
-
ConfigClass --> Loader
|
|
275
|
-
ConfigClass --> Emit
|
|
276
|
-
ConfigClass --> Filtering
|
|
277
|
-
ConfigClass --> RBSConfig
|
|
278
|
-
ConfigClass --> SorbetConfig
|
|
279
|
-
ConfigClass --> PluginConfig
|
|
280
|
-
Run --> InlineRewriter
|
|
281
|
-
InlineRewriter --> ParsingModule
|
|
282
|
-
ParsingModule --> ParserGem
|
|
283
|
-
ParsingModule --> Prism
|
|
284
|
-
InlineRewriter --> Collector
|
|
285
|
-
Collector --> PluginModule
|
|
286
|
-
PluginModule --> Registry
|
|
287
|
-
Registry --> CollectorPlugin
|
|
288
|
-
InlineRewriter --> DocBuilder
|
|
289
|
-
DocBuilder --> InferModule
|
|
290
|
-
InferModule --> Params
|
|
291
|
-
InferModule --> Returns
|
|
292
|
-
InferModule --> Raises
|
|
293
|
-
Params --> Literals
|
|
294
|
-
Returns --> Literals
|
|
295
|
-
Raises --> ASTWalk
|
|
296
|
-
Raises --> Names
|
|
297
|
-
DocBuilder --> ProviderChain
|
|
298
|
-
ProviderChain --> SorbetSource
|
|
299
|
-
ProviderChain --> SorbetRBI
|
|
300
|
-
ProviderChain --> RBSProvider
|
|
301
|
-
SorbetSource --> SorbetBase
|
|
302
|
-
SorbetRBI --> SorbetBase
|
|
303
|
-
RBSProvider --> RBSFormatter
|
|
304
|
-
RBSProvider --> RBSCollection
|
|
305
|
-
DocBuilder --> PluginModule
|
|
306
|
-
PluginModule --> Registry
|
|
307
|
-
Registry --> TagPlugin
|
|
308
|
-
TagPlugin --> TagValue
|
|
309
|
-
TagPlugin --> Context
|
|
310
|
-
InlineRewriter --> DocBlock
|
|
311
|
-
InlineRewriter --> SourceHelpers
|
|
312
|
-
RbsGenCmd --> ParsingModule
|
|
313
|
-
RbsGenCmd --> YParser
|
|
314
|
-
YParser --> YTypes
|
|
315
|
-
YParser --> YFormatter
|
|
316
|
-
```
|
|
195
|
+
How it works, briefly: the IDE annotator collects file info on open/type/save, asks the gem daemon (`check` over Unix-socket JSON-RPC, CLI fallback), maps offenses to YARD tag lines with per-cop quick-fixes, and caches by file stamp + config hash. Daemon errors surface as file-level errors instead of silence; quick-fixes run `fix` or file-scoped `update_types`; workspace check aggregates `check_batch` into a balloon.
|
|
317
196
|
|
|
318
|
-
|
|
197
|
+
Version gates (plugin behavior by gem version):
|
|
319
198
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
end
|
|
326
|
-
|
|
327
|
-
subgraph Daemon["Server Daemon (Unix Socket)"]
|
|
328
|
-
Socket["Daemon#listen_loop\nJSON-RPC 2.0 dispatch"]
|
|
329
|
-
CacheCheck{"File cached &\nmtime fresh?"}
|
|
330
|
-
CacheStorage["LRUCache\n(1000 entries)"]
|
|
331
|
-
ApplyOverrides["apply_cli_overrides\n(reset on nil)"]
|
|
332
|
-
end
|
|
333
|
-
|
|
334
|
-
ViaServer --> Socket
|
|
335
|
-
Socket --> ApplyOverrides
|
|
336
|
-
ApplyOverrides --> CacheCheck
|
|
337
|
-
CacheCheck -->|Hit| Socket
|
|
338
|
-
CacheCheck -->|Miss| Parse
|
|
339
|
-
Direct --> Parse
|
|
340
|
-
Parse["Parsing.parse_buffer\nParser gem / Prism"]
|
|
341
|
-
Parse --> AST["AST + Comments"]
|
|
342
|
-
AST --> Collect["Collector.process\n· Find methods\n· Track visibility\n· Find attr_*"]
|
|
343
|
-
AST --> CollectPlugins["CollectorPlugin#collect\n· Custom AST walks\n· Non-standard constructs"]
|
|
344
|
-
Collect --> Insertions["Insertion list\n(sorted by position)"]
|
|
345
|
-
CollectPlugins --> Insertions
|
|
346
|
-
Insertions --> Dedup["Deduplicate\n(override by position)"]
|
|
347
|
-
Dedup --> Build["DocBuilder.build_doc_lines\nper insertion"]
|
|
348
|
-
Build --> Infer["Infer params / returns / raises\n(heuristic fallback)"]
|
|
349
|
-
Build --> SigQuery["ProviderChain\nquery external types"]
|
|
350
|
-
Build --> TagPlugins["TagPlugin#call\n(add extra @tags)"]
|
|
351
|
-
Infer --> ResultDoc["Generated YARD doc block"]
|
|
352
|
-
SigQuery --> ResultDoc
|
|
353
|
-
TagPlugins --> ResultDoc
|
|
354
|
-
ResultDoc --> Strategy{"Strategy?"}
|
|
355
|
-
Strategy -->|Safe| Merge["DocBlock.merge\npreserve + append + sort"]
|
|
356
|
-
Strategy -->|Aggressive| Replace["Replace entirely"]
|
|
357
|
-
Merge --> Rewritten["Rewriter#process\n-> rewritten source"]
|
|
358
|
-
Replace --> Rewritten
|
|
359
|
-
Rewritten --> Result["Result / response"]
|
|
360
|
-
Rewritten --> CacheStorage
|
|
361
|
-
CacheStorage --> Socket
|
|
362
|
-
Result -->|Direct mode| Output["Modified .rb file / STDOUT"]
|
|
363
|
-
Result -->|Server mode| Socket
|
|
364
|
-
```
|
|
199
|
+
| Gem version | Plugin behavior |
|
|
200
|
+
|-------------|-------------------------------------------------------------|
|
|
201
|
+
| `< 1.5.1` | always CLI, no daemon mode |
|
|
202
|
+
| `< 1.5.2` | no batch mode (workspace check scans the directory via CLI) |
|
|
203
|
+
| `< 1.6.2` | `update_types` falls back to CLI on unknown method |
|
|
365
204
|
|
|
366
205
|
## CLI
|
|
367
206
|
|
|
@@ -371,7 +210,7 @@ docscribe init [options]
|
|
|
371
210
|
docscribe generate [type] [name] [options]
|
|
372
211
|
docscribe sigs [options] [files...]
|
|
373
212
|
docscribe rbs [options] [files...]
|
|
374
|
-
docscribe update_types [directory]
|
|
213
|
+
docscribe update_types [directory|file] [options]
|
|
375
214
|
docscribe check_for_comments [paths...]
|
|
376
215
|
docscribe server [start|status|stop] [options]
|
|
377
216
|
```
|
|
@@ -437,6 +276,10 @@ If you pass no files and don't use `--stdin`, Docscribe processes the current di
|
|
|
437
276
|
- `--format FORMAT`
|
|
438
277
|
Output format: `text` (default, human-readable), `json` (machine-readable, RuboCop-compatible), or `sarif` (SARIF 2.1
|
|
439
278
|
JSON, compatible with GitHub Code Scanning).
|
|
279
|
+
Every JSON offense carries `cop_name`, `message`, `location`, plus machine-readable `type`
|
|
280
|
+
(`updated_return`, `missing_param`, ...) and, for type mismatches, `source` (`"rbs"`, `"infer"` or `"syntax"`).
|
|
281
|
+
Files are grouped by normalized path, so the same file passed under different spellings
|
|
282
|
+
(absolute, relative, symlinked) appears once.
|
|
440
283
|
|
|
441
284
|
- `--rbs`
|
|
442
285
|
Use RBS signatures for `@param`/`@return` when available (falls back to inference).
|
|
@@ -450,6 +293,10 @@ If you pass no files and don't use `--stdin`, Docscribe processes the current di
|
|
|
450
293
|
- `--rbi-dir DIR`
|
|
451
294
|
Add an Sorbet RBI directory (repeatable). Implies `--sorbet`.
|
|
452
295
|
|
|
296
|
+
- `--[no-]validate-types`
|
|
297
|
+
Validate YARD types against inferred/RBS types and report mismatches.
|
|
298
|
+
`--no-validate-types` overrides `validate_types: true` from the config file.
|
|
299
|
+
|
|
453
300
|
- `--include PATTERN`
|
|
454
301
|
Include PATTERN (method id or file path; glob or `/regex/`).
|
|
455
302
|
|
|
@@ -611,21 +458,28 @@ end
|
|
|
611
458
|
### `docscribe update_types` — two-pass type-aware documentation update
|
|
612
459
|
|
|
613
460
|
> [!NOTE]
|
|
614
|
-
> `docscribe update_types` is a convenience alias for the two-pass workflow above.
|
|
615
|
-
> gem
|
|
616
|
-
> `bundle exec rbs collection install
|
|
617
|
-
> missing, types will fall back to AST inference.
|
|
461
|
+
> `docscribe update_types` is a convenience alias for the two-pass workflow above. RBS features require Ruby 3.0+ and
|
|
462
|
+
> the `rbs` gem; pass `--no-rbs` to skip them. If `rbs.collection: true` is set in `docscribe.yml`, the collection is
|
|
463
|
+
> discovered automatically (otherwise run `bundle exec rbs collection install` first). Type accuracy depends on your
|
|
464
|
+
> RBS signatures — if signatures are incomplete or missing, types will fall back to AST inference.
|
|
618
465
|
|
|
619
|
-
`docscribe update_types` runs two passes to bring both docs and RBS signatures up to date
|
|
466
|
+
`docscribe update_types` runs two passes to bring both docs and RBS signatures up to date. The target can be a
|
|
467
|
+
directory **or a single file** (handy for IDE quick-fixes — only that file is touched):
|
|
620
468
|
|
|
621
469
|
1. **Pass 1** — `docscribe -AkB --rbs-collection <dir>`: aggressively rebuilds doc blocks, preserves existing
|
|
622
470
|
descriptions, suppresses boilerplate, uses RBS collection types.
|
|
623
471
|
2. **Pass 2** — `docscribe -aB --rbs-collection <dir>`: safe merge cleanup with no boilerplate.
|
|
624
472
|
|
|
473
|
+
Extra flags (`--rbs`, `--sig-dir DIR` (repeatable), `--rbs-collection`, `--[no-]validate-types`, `--no-rbs`) are
|
|
474
|
+
passed through to both passes.
|
|
475
|
+
|
|
625
476
|
```shell
|
|
626
477
|
# Update docs in lib/ using RBS collection
|
|
627
478
|
docscribe update_types lib
|
|
628
479
|
|
|
480
|
+
# Update a single file only
|
|
481
|
+
docscribe update_types lib/user.rb
|
|
482
|
+
|
|
629
483
|
# Defaults to current directory
|
|
630
484
|
docscribe update_types
|
|
631
485
|
```
|
|
@@ -805,13 +659,17 @@ The daemon speaks JSON-RPC 2.0 over Unix socket. Each request is a JSON line, ea
|
|
|
805
659
|
|
|
806
660
|
Methods:
|
|
807
661
|
|
|
808
|
-
| Method
|
|
809
|
-
|
|
810
|
-
| `check`
|
|
811
|
-
| `fix`
|
|
812
|
-
| `check_batch`
|
|
813
|
-
| `
|
|
814
|
-
| `
|
|
662
|
+
| Method | Parameters | Result |
|
|
663
|
+
|----------------|-------------------------------------------------------------------------------------|---------------------------------------------------------|
|
|
664
|
+
| `check` | `file` (string), `strategy` ("safe"/"aggressive"), `cli_overrides` (hash, optional) | `status`, `changed`, `changes` |
|
|
665
|
+
| `fix` | same as `check` | `status`, `changed`, `changes` |
|
|
666
|
+
| `check_batch` | `files` (array of strings), `strategy`, `cli_overrides`, `timeout` (int, optional) | array of per-file results |
|
|
667
|
+
| `update_types` | `file` (string, optional), `dir` (string, optional; `file` wins) | `status`, `changed`, `changes` |
|
|
668
|
+
| `ping` | — | `version`, `pid`, `socket_path`, `started_at`, `uptime` |
|
|
669
|
+
| `shutdown` | — | `status` |
|
|
670
|
+
|
|
671
|
+
Each entry in `changes` carries `type`, `line`, `message` and, for type mismatches, `source` (`"rbs"`, `"infer"` or
|
|
672
|
+
`"syntax"`) plus `param` where applicable — IDE plugins use `source` to pick the right fix (RBS update vs YARD fix).
|
|
815
673
|
|
|
816
674
|
Error codes (standardized for IDE plugin integration):
|
|
817
675
|
|
|
@@ -931,11 +789,12 @@ Useful flag combinations for common workflows:
|
|
|
931
789
|
projects.
|
|
932
790
|
- `docscribe -a --sorbet --rbi-dir sorbet/rbi lib` — safe autocorrect using Sorbet RBI signatures.
|
|
933
791
|
- `docscribe update_types lib` — two-pass type-aware update: aggressively rebuilds docs with kept descriptions and RBS
|
|
934
|
-
collection, then safe-merges to clean up.
|
|
792
|
+
collection, then safe-merges to clean up. Accepts a single file too: `docscribe update_types lib/user.rb`.
|
|
935
793
|
See [docscribe update_types](#docscribe-update_types--two-pass-type-aware-documentation-update).
|
|
936
794
|
|
|
937
795
|
> [!NOTE]
|
|
938
|
-
> `docscribe update_types` is a convenient shortcut, but be aware it uses `--rbs-collection` under the hood
|
|
796
|
+
> `docscribe update_types` is a convenient shortcut, but be aware it uses `--rbs-collection` under the hood
|
|
797
|
+
> (or `rbs.collection: true` from the config — then no flag is needed).
|
|
939
798
|
> If your RBS signatures are incomplete, types may fall back to AST inference.
|
|
940
799
|
|
|
941
800
|
## Parser backend (Parser gem vs Prism)
|
|
@@ -1096,6 +955,13 @@ You can combine `--rbs-collection` with `--sig-dir` to mix gem signatures with y
|
|
|
1096
955
|
docscribe -a --rbs-collection --sig-dir sig lib
|
|
1097
956
|
```
|
|
1098
957
|
|
|
958
|
+
If `rbs.collection: true` is set in `docscribe.yml`, the collection is discovered automatically and the flag
|
|
959
|
+
can be omitted everywhere (including plain `check` and the daemon):
|
|
960
|
+
|
|
961
|
+
```shell
|
|
962
|
+
docscribe -a lib
|
|
963
|
+
```
|
|
964
|
+
|
|
1099
965
|
> [!NOTE]
|
|
1100
966
|
> `--rbs-collection` only improves types for methods defined in gems that ship RBS
|
|
1101
967
|
> signatures. For your own classes, provide a `sig/` directory with hand-written or
|
|
@@ -1301,7 +1167,7 @@ Heuristics (best-effort).
|
|
|
1301
1167
|
Parameters:
|
|
1302
1168
|
|
|
1303
1169
|
- `*args` -> `Array`
|
|
1304
|
-
- `**kwargs` -> `Hash`
|
|
1170
|
+
- `**kwargs` -> `Hash[Symbol, untyped]` with RBS, `Hash<Symbol, Object>` in YARD, bare `Hash` from pure inference
|
|
1305
1171
|
- `&block` -> `Proc`
|
|
1306
1172
|
- keyword args:
|
|
1307
1173
|
- `verbose: true` -> `Boolean`
|
|
@@ -1316,6 +1182,18 @@ Return values:
|
|
|
1316
1182
|
- For simple bodies, Docscribe looks at the last expression or explicit `return`.
|
|
1317
1183
|
- Unions with `nil` become optional types (e.g. `String` or `nil` -> `String?`).
|
|
1318
1184
|
- For control flow (`if`/`case`), it unifies branches conservatively.
|
|
1185
|
+
- Blocks resolve to generics where possible: `map`/`then` over known elements give `Array<String>` instead of
|
|
1186
|
+
bare `Array`; `arr << x` and `x += 1` resolve through RBS (`self`-returns stay `self`); `&.`/`||`/`()` receivers
|
|
1187
|
+
are unwrapped before lookup; `each_with_index` chains infer `Enumerator`/`Hash` element types
|
|
1188
|
+
(`arr.each_with_index.to_h { |x, i| [k, v] }` infers `Hash<K, V>` from the pair literal;
|
|
1189
|
+
the second block parameter is always the `Integer` index).
|
|
1190
|
+
- Generic compatibility is structural, not textual: `Hash` matches `Hash<Symbol, Config>`, `Array` matches
|
|
1191
|
+
`Array<String>`, `String?` equals `String, nil` and `String|nil`, and `String` is accepted where `Object` is
|
|
1192
|
+
expected. YARD `[]` and RBS `<>` (plus `untyped`/`Object`) are normalized before comparison.
|
|
1193
|
+
- `void` understands Ruby idioms: initializers and `setup` may return `Hash`/`self`/`Boolean`, predicates
|
|
1194
|
+
(`valid?`) may return `Boolean`.
|
|
1195
|
+
- One crashing method never blanks the whole file: the error goes to stderr (`Docscribe: skipping method foo ...`)
|
|
1196
|
+
and the remaining methods are still processed.
|
|
1319
1197
|
|
|
1320
1198
|
> [!TIP]
|
|
1321
1199
|
> Docscribe resolves return types for core Ruby methods (`Integer#positive?`, `String#upcase`, etc.)
|
|
@@ -1340,6 +1218,9 @@ Docscribe detects exceptions and rescue branches:
|
|
|
1340
1218
|
|
|
1341
1219
|
- Conditional return types for rescue branches:
|
|
1342
1220
|
- Docscribe adds `@return [Type] if ExceptionA, ExceptionB` for each rescue clause
|
|
1221
|
+
- Conditional tags never overwrite the main `@return` type, so `check` and `update_types` agree with each other
|
|
1222
|
+
(no ping-pong between runs). Rescue bodies referencing constants resolve to the constant's value type when
|
|
1223
|
+
the constant is visible from the method's scope.
|
|
1343
1224
|
|
|
1344
1225
|
## Visibility semantics
|
|
1345
1226
|
|
|
@@ -1889,6 +1770,7 @@ The generated file contains:
|
|
|
1889
1770
|
|-------------------------------------------|------------|----------------------------------------------------------------------------------------------|----------------------------------------------------------------------------|
|
|
1890
1771
|
| `keep_descriptions` | `bool` | `false` | Preserve existing doc text in aggressive mode |
|
|
1891
1772
|
| `skip_anonymous_block_params` | `bool` | `false` | Skip `@param [Proc] block` for anonymous `&` params |
|
|
1773
|
+
| `validate_types` | `bool` | `false` | Validate YARD types against inferred/RBS types and report mismatches |
|
|
1892
1774
|
| `emit.header` | `bool` | `false` | Generate method header line (`+#foo+ -> ...`) |
|
|
1893
1775
|
| `emit.include_default_message` | `bool` | `true` | Insert default message (`Method documentation.`) |
|
|
1894
1776
|
| `emit.include_param_documentation` | `bool` | `true` | Insert param description text (`Param documentation.`) |
|
|
@@ -1998,6 +1880,7 @@ yard doc -o docs
|
|
|
1998
1880
|
- Safe mode only merges into existing **doc-like** comment blocks. Ordinary comments that are not recognized as
|
|
1999
1881
|
documentation are preserved and treated conservatively.
|
|
2000
1882
|
- Type inference is heuristic. Complex flows and meta-programming will fall back to `Object` or best-effort types.
|
|
1883
|
+
- A method that crashes documentation generation is skipped with a stderr warning instead of failing the whole file.
|
|
2001
1884
|
- Aggressive mode (`-A`) replaces existing doc blocks and should be reviewed carefully.
|
|
2002
1885
|
|
|
2003
1886
|
## Roadmap
|
data/exe/docscribe-client
CHANGED
|
@@ -13,7 +13,8 @@ SOCKET_DIR = begin
|
|
|
13
13
|
sock_overhead = "/docscribe-#{'a' * 32}.sock".bytesize
|
|
14
14
|
tmp.bytesize <= 104 - sock_overhead ? tmp : '/tmp'
|
|
15
15
|
end
|
|
16
|
-
ENV_FILES = %w[Gemfile.lock rbs_collection.lock.yaml].freeze
|
|
16
|
+
ENV_FILES = %w[Gemfile.lock rbs_collection.lock.yaml docscribe.yml].freeze
|
|
17
|
+
SIG_RBS_GLOB = 'sig/**/*.rbs'
|
|
17
18
|
|
|
18
19
|
def socket_path(config_path = nil)
|
|
19
20
|
seed = +Dir.pwd
|
|
@@ -27,13 +28,27 @@ def socket_path(config_path = nil)
|
|
|
27
28
|
end
|
|
28
29
|
|
|
29
30
|
def env_hash
|
|
30
|
-
parts = ENV_FILES.map
|
|
31
|
-
|
|
32
|
-
File.exist?(path) ? File.mtime(path).to_f.to_s : '0'
|
|
33
|
-
end
|
|
31
|
+
parts = ENV_FILES.map { |file| env_file_mtime(file) }
|
|
32
|
+
parts.concat(sig_env_parts)
|
|
34
33
|
Digest::MD5.hexdigest(parts.join(':'))
|
|
35
34
|
end
|
|
36
35
|
|
|
36
|
+
def env_file_mtime(file)
|
|
37
|
+
path = File.join(Dir.pwd, file)
|
|
38
|
+
File.exist?(path) ? File.mtime(path).to_f.to_s : '0'
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def sig_env_parts
|
|
42
|
+
files = sig_files
|
|
43
|
+
mtimes = files.map { |p| File.mtime(p).to_f.to_s }
|
|
44
|
+
mtimes << files.size.to_s
|
|
45
|
+
mtimes
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def sig_files
|
|
49
|
+
Dir.glob(File.join(Dir.pwd, SIG_RBS_GLOB)).sort
|
|
50
|
+
end
|
|
51
|
+
|
|
37
52
|
def config_hash(config_path)
|
|
38
53
|
resolved = File.expand_path(config_path)
|
|
39
54
|
mtime = File.exist?(resolved) ? File.mtime(resolved).to_f : 0.0
|
|
@@ -141,8 +156,12 @@ when :ping
|
|
|
141
156
|
puts JSON.generate(result || { error: 'Connection failed' })
|
|
142
157
|
when :status
|
|
143
158
|
alive = begin
|
|
144
|
-
File.exist?(sock)
|
|
145
|
-
UNIXSocket.new(sock).close
|
|
159
|
+
if File.exist?(sock)
|
|
160
|
+
UNIXSocket.new(sock).close
|
|
161
|
+
true
|
|
162
|
+
else
|
|
163
|
+
false
|
|
164
|
+
end
|
|
146
165
|
rescue StandardError
|
|
147
166
|
false
|
|
148
167
|
end
|
|
@@ -22,13 +22,14 @@ module Docscribe
|
|
|
22
22
|
# @param [Hash<Symbol, Object>] options parsed CLI options
|
|
23
23
|
# @return [Docscribe::Config] merged effective config
|
|
24
24
|
def build(base, options)
|
|
25
|
-
return base unless needs_override?(options)
|
|
25
|
+
return warn_and_return_base(base, options) unless needs_override?(options)
|
|
26
26
|
|
|
27
27
|
raw = Marshal.load(Marshal.dump(base.raw))
|
|
28
28
|
apply_filter_overrides(raw, options)
|
|
29
29
|
apply_rbs_overrides(raw, options) if rbs_overrides?(options)
|
|
30
30
|
apply_sorbet_overrides(raw, options) if sorbet_overrides?(options)
|
|
31
31
|
apply_output_overrides(raw, options)
|
|
32
|
+
apply_validation_overrides(raw, options) if validation_overrides?(options)
|
|
32
33
|
conf = Docscribe::Config.new(config_path: base.config_path, **raw)
|
|
33
34
|
warn_missing_rbs_collection(conf, options)
|
|
34
35
|
conf
|
|
@@ -43,7 +44,21 @@ module Docscribe
|
|
|
43
44
|
filter_overrides?(options) ||
|
|
44
45
|
rbs_overrides?(options) ||
|
|
45
46
|
sorbet_overrides?(options) ||
|
|
46
|
-
output_overrides?(options)
|
|
47
|
+
output_overrides?(options) ||
|
|
48
|
+
validation_overrides?(options)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Warn about a missing collection opt-in and return the base config unchanged.
|
|
52
|
+
#
|
|
53
|
+
# Used when no CLI override is present so the nudge still fires on plain runs.
|
|
54
|
+
#
|
|
55
|
+
# @note module_function: defines #warn_and_return_base (visibility: private)
|
|
56
|
+
# @param [Docscribe::Config] base base config loaded from YAML/defaults
|
|
57
|
+
# @param [Hash<Symbol, Object>] options parsed CLI options
|
|
58
|
+
# @return [Docscribe::Config] base config unchanged
|
|
59
|
+
def warn_and_return_base(base, options)
|
|
60
|
+
warn_missing_rbs_collection(base, options)
|
|
61
|
+
base
|
|
47
62
|
end
|
|
48
63
|
|
|
49
64
|
# Whether any method or file filter CLI options were provided.
|
|
@@ -195,6 +210,25 @@ module Docscribe
|
|
|
195
210
|
raw['emit']['include_param_documentation'] = false if options[:no_boilerplate]
|
|
196
211
|
end
|
|
197
212
|
|
|
213
|
+
# Whether any validation-related CLI options were provided.
|
|
214
|
+
#
|
|
215
|
+
# @note module_function: defines #validation_overrides? (visibility: private)
|
|
216
|
+
# @param [Hash<Symbol, Object>] options parsed CLI options
|
|
217
|
+
# @return [Boolean]
|
|
218
|
+
def validation_overrides?(options)
|
|
219
|
+
!options[:validate_types].nil?
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# Apply validation-related CLI overrides to the raw config.
|
|
223
|
+
#
|
|
224
|
+
# @note module_function: defines #apply_validation_overrides (visibility: private)
|
|
225
|
+
# @param [Hash<String, Object>] raw raw config hash
|
|
226
|
+
# @param [Hash<Symbol, Object>] options parsed CLI options
|
|
227
|
+
# @return [void]
|
|
228
|
+
def apply_validation_overrides(raw, options)
|
|
229
|
+
raw['validate_types'] = options[:validate_types]
|
|
230
|
+
end
|
|
231
|
+
|
|
198
232
|
# Warn when rbs_collection.lock.yaml exists but --rbs-collection was not passed.
|
|
199
233
|
#
|
|
200
234
|
# The warning can be suppressed by setting `rbs.warn_missing_collection: false`
|
|
@@ -206,6 +240,7 @@ module Docscribe
|
|
|
206
240
|
# @return [void]
|
|
207
241
|
def warn_missing_rbs_collection(conf, options)
|
|
208
242
|
return if options[:rbs_collection]
|
|
243
|
+
return if conf.raw.dig('rbs', 'collection')
|
|
209
244
|
return unless conf.rbs_warn_missing_collection?
|
|
210
245
|
return unless File.exist?('rbs_collection.lock.yaml')
|
|
211
246
|
|
|
@@ -172,7 +172,7 @@ module Docscribe
|
|
|
172
172
|
end
|
|
173
173
|
|
|
174
174
|
# @private
|
|
175
|
-
# @param [
|
|
175
|
+
# @param [Parser::AST::Node] node
|
|
176
176
|
# @param [Docscribe::CLI::Coverage::CoverageStats] stats
|
|
177
177
|
# @param [String] src
|
|
178
178
|
# @return [void]
|
|
@@ -185,7 +185,7 @@ module Docscribe
|
|
|
185
185
|
end
|
|
186
186
|
|
|
187
187
|
# @private
|
|
188
|
-
# @param [
|
|
188
|
+
# @param [Parser::AST::Node] node
|
|
189
189
|
# @param [Docscribe::CLI::Coverage::CoverageStats] stats
|
|
190
190
|
# @param [String] src
|
|
191
191
|
# @return [void]
|
|
@@ -202,7 +202,7 @@ module Docscribe
|
|
|
202
202
|
end
|
|
203
203
|
|
|
204
204
|
# @private
|
|
205
|
-
# @param [
|
|
205
|
+
# @param [Parser::AST::Node] node
|
|
206
206
|
# @param [Docscribe::CLI::Coverage::CoverageStats] stats
|
|
207
207
|
# @param [String] doc_comment
|
|
208
208
|
# @return [void]
|
|
@@ -218,7 +218,7 @@ module Docscribe
|
|
|
218
218
|
end
|
|
219
219
|
|
|
220
220
|
# @private
|
|
221
|
-
# @param [
|
|
221
|
+
# @param [Parser::AST::Node] node
|
|
222
222
|
# @param [Docscribe::CLI::Coverage::CoverageStats] stats
|
|
223
223
|
# @return [void]
|
|
224
224
|
def count_method_params(node, stats)
|
|
@@ -226,7 +226,7 @@ module Docscribe
|
|
|
226
226
|
end
|
|
227
227
|
|
|
228
228
|
# @private
|
|
229
|
-
# @param [
|
|
229
|
+
# @param [Parser::AST::Node] node
|
|
230
230
|
# @return [Integer]
|
|
231
231
|
def count_params(node)
|
|
232
232
|
args_node = node.children[2] || node.children[1]
|