docscribe 1.4.2 → 1.5.1
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 +601 -139
- data/exe/docscribe-client +105 -0
- data/lib/docscribe/cli/check_for_comments.rb +183 -0
- data/lib/docscribe/cli/config_builder.rb +107 -53
- data/lib/docscribe/cli/formatters/json.rb +294 -0
- data/lib/docscribe/cli/formatters/sarif.rb +235 -0
- data/lib/docscribe/cli/formatters/text.rb +208 -0
- data/lib/docscribe/cli/formatters.rb +26 -0
- data/lib/docscribe/cli/generate.rb +56 -62
- data/lib/docscribe/cli/init.rb +14 -6
- data/lib/docscribe/cli/options.rb +206 -89
- data/lib/docscribe/cli/rbs_gen.rb +529 -0
- data/lib/docscribe/cli/run.rb +433 -154
- data/lib/docscribe/cli/server.rb +135 -0
- data/lib/docscribe/cli/sigs.rb +366 -0
- data/lib/docscribe/cli/update_types.rb +103 -0
- data/lib/docscribe/cli.rb +21 -24
- data/lib/docscribe/config/defaults.rb +7 -2
- data/lib/docscribe/config/emit.rb +17 -0
- data/lib/docscribe/config/filtering.rb +17 -24
- data/lib/docscribe/config/loader.rb +19 -17
- data/lib/docscribe/config/plugin.rb +1 -1
- data/lib/docscribe/config/rbs.rb +39 -7
- data/lib/docscribe/config/sorbet.rb +22 -16
- data/lib/docscribe/config/sorting.rb +1 -1
- data/lib/docscribe/config/template.rb +10 -1
- data/lib/docscribe/config/utils.rb +11 -9
- data/lib/docscribe/config.rb +10 -6
- data/lib/docscribe/infer/ast_walk.rb +1 -1
- data/lib/docscribe/infer/literals.rb +6 -11
- data/lib/docscribe/infer/names.rb +2 -3
- data/lib/docscribe/infer/params.rb +14 -16
- data/lib/docscribe/infer/raises.rb +3 -5
- data/lib/docscribe/infer/returns.rb +615 -151
- data/lib/docscribe/infer.rb +29 -26
- data/lib/docscribe/inline_rewriter/collector.rb +159 -164
- data/lib/docscribe/inline_rewriter/doc_block.rb +145 -115
- data/lib/docscribe/inline_rewriter/doc_builder.rb +1032 -723
- data/lib/docscribe/inline_rewriter/source_helpers.rb +48 -48
- data/lib/docscribe/inline_rewriter/tag_sorter.rb +82 -85
- data/lib/docscribe/inline_rewriter.rb +485 -488
- data/lib/docscribe/lru_cache.rb +49 -0
- data/lib/docscribe/parsing.rb +28 -9
- data/lib/docscribe/plugin/base/collector_plugin.rb +2 -1
- data/lib/docscribe/plugin/base/tag_plugin.rb +0 -1
- data/lib/docscribe/plugin/context.rb +28 -18
- data/lib/docscribe/plugin/registry.rb +25 -26
- data/lib/docscribe/plugin/tag.rb +9 -14
- data/lib/docscribe/plugin.rb +17 -16
- data/lib/docscribe/server.rb +608 -0
- data/lib/docscribe/types/provider_chain.rb +4 -2
- data/lib/docscribe/types/rbs/collection_loader.rb +2 -2
- data/lib/docscribe/types/rbs/provider.rb +177 -51
- data/lib/docscribe/types/rbs/type_formatter.rb +224 -83
- data/lib/docscribe/types/signature.rb +22 -42
- data/lib/docscribe/types/sorbet/base_provider.rb +29 -21
- data/lib/docscribe/types/sorbet/rbi_provider.rb +6 -5
- data/lib/docscribe/types/sorbet/source_provider.rb +6 -4
- data/lib/docscribe/types/yard/formatter.rb +100 -0
- data/lib/docscribe/types/yard/parser.rb +240 -0
- data/lib/docscribe/types/yard/types.rb +52 -0
- data/lib/docscribe/version.rb +1 -1
- metadata +38 -1
data/README.md
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="assets/icons/icon_128x128.png" alt="Docscribe logo" width="128">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">DocScribe</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://rubygems.org/gems/docscribe"><img src="https://img.shields.io/gem/v/docscribe.svg" alt="Gem Version"></a>
|
|
9
|
+
<a href="https://rubygems.org/gems/docscribe"><img src="https://img.shields.io/gem/dt/docscribe.svg" alt="RubyGems Downloads"></a>
|
|
10
|
+
<a href="https://github.com/unurgunite/docscribe/actions/workflows/ci.yml"><img src="https://github.com/unurgunite/docscribe/actions/workflows/ci.yml/badge.svg?branch=master" alt="CI"></a>
|
|
11
|
+
<a href="https://github.com/unurgunite/docscribe/blob/master/LICENSE.txt"><img src="https://img.shields.io/github/license/unurgunite/docscribe.svg" alt="License"></a>
|
|
12
|
+
<a href="#installation"><img src="https://img.shields.io/badge/ruby-%3E%3D%202.7-blue.svg" alt="Ruby"></a>
|
|
13
|
+
<a href="https://marketplace.visualstudio.com/items?itemName=unurgunite.docscribe-vscode"><img src="https://img.shields.io/badge/VS%20Code-plugin-blue?logo=visualstudiocode" alt="VS Code"></a>
|
|
14
|
+
<a href="https://plugins.jetbrains.com/plugin/32349-docscribe"><img src="https://img.shields.io/badge/RubyMine-plugin-green?logo=jetbrains" alt="RubyMine"></a>
|
|
15
|
+
</p>
|
|
8
16
|
|
|
9
17
|

|
|
10
18
|
|
|
@@ -29,6 +37,10 @@ returns), and respects Ruby visibility semantics — without using YARD to parse
|
|
|
29
37
|
- `attr_reader` / `attr_writer` / `attr_accessor`;
|
|
30
38
|
- `Struct.new` declarations in both constant-assigned and class-based styles.
|
|
31
39
|
|
|
40
|
+
> [!NOTE]
|
|
41
|
+
> Docscribe is under **active development**. If you run into any edge cases or have ideas for improvement, feel free to
|
|
42
|
+
> [open an issue](https://github.com/unurgunite/docscribe/issues/new) or submit a pull request.
|
|
43
|
+
|
|
32
44
|
Common workflows:
|
|
33
45
|
|
|
34
46
|
- Inspect what safe doc updates would be applied: `docscribe lib`
|
|
@@ -38,21 +50,49 @@ Common workflows:
|
|
|
38
50
|
- Use RBS signatures when available: `docscribe -a --rbs --sig-dir sig lib`
|
|
39
51
|
- Use Sorbet signatures when available: `docscribe -a --sorbet --rbi-dir sorbet/rbi lib`
|
|
40
52
|
|
|
53
|
+
## Quick start
|
|
54
|
+
|
|
55
|
+
```shell
|
|
56
|
+
# Check what safe doc updates would be applied
|
|
57
|
+
docscribe lib
|
|
58
|
+
|
|
59
|
+
# Apply safe updates (insert missing docs, merge existing)
|
|
60
|
+
docscribe -a lib
|
|
61
|
+
|
|
62
|
+
# Rebuild all doc blocks aggressively
|
|
63
|
+
docscribe -A lib
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
> [!TIP]
|
|
67
|
+
> See [CLI](#cli) for all options and [Update strategies](#update-strategies) for the
|
|
68
|
+
> difference between safe and aggressive modes.
|
|
69
|
+
>
|
|
70
|
+
> Want IDE integration? Check out
|
|
71
|
+
> the [VS Code](https://marketplace.visualstudio.com/items?itemName=unurgunite.docscribe-vscode)
|
|
72
|
+
> and [RubyMine](https://plugins.jetbrains.com/plugin/32349-docscribe) plugins.
|
|
73
|
+
|
|
41
74
|
## Contents
|
|
42
75
|
|
|
43
76
|
* [Docscribe](#docscribe)
|
|
77
|
+
* [Quick start](#quick-start)
|
|
44
78
|
* [Contents](#contents)
|
|
45
79
|
* [Installation](#installation)
|
|
46
|
-
* [Quick start](#quick-start)
|
|
47
80
|
* [Architecture](#architecture)
|
|
48
81
|
* [Data flow](#data-flow)
|
|
49
82
|
* [CLI](#cli)
|
|
83
|
+
* [Exit codes](#exit-codes)
|
|
50
84
|
* [Options](#options)
|
|
51
85
|
* [Examples](#examples)
|
|
86
|
+
* [`docscribe sigs` — check RBS signature coverage](#docscribe-sigs--check-rbs-signature-coverage)
|
|
87
|
+
* [`docscribe rbs` — generate RBS from YARD](#docscribe-rbs--generate-rbs-from-yard)
|
|
88
|
+
* [`docscribe update_types` — two-pass type-aware documentation update](#docscribe-update_types--two-pass-type-aware-documentation-update)
|
|
89
|
+
* [`docscribe check_for_comments` — find placeholder documentation](#docscribe-check_for_comments--find-placeholder-documentation)
|
|
90
|
+
* [`docscribe server` — persistent daemon mode](#docscribe-server--persistent-daemon-mode)
|
|
52
91
|
* [Update strategies](#update-strategies)
|
|
53
92
|
* [Safe strategy](#safe-strategy)
|
|
54
93
|
* [Aggressive strategy](#aggressive-strategy)
|
|
55
94
|
* [Output markers](#output-markers)
|
|
95
|
+
* [Tips & tricks](#tips--tricks)
|
|
56
96
|
* [Parser backend (Parser gem vs Prism)](#parser-backend-parser-gem-vs-prism)
|
|
57
97
|
* [External type integrations (optional)](#external-type-integrations-optional)
|
|
58
98
|
* [RBS](#rbs)
|
|
@@ -77,6 +117,7 @@ Common workflows:
|
|
|
77
117
|
* [Idempotency](#idempotency)
|
|
78
118
|
* [Plugin examples](#plugin-examples)
|
|
79
119
|
* [Configuration](#configuration)
|
|
120
|
+
* [Anonymous block parameters](#anonymous-block-parameters)
|
|
80
121
|
* [Filtering](#filtering)
|
|
81
122
|
* [`attr_*` example](#attr_-example)
|
|
82
123
|
* [`Struct.new` examples](#structnew-examples)
|
|
@@ -86,14 +127,17 @@ Common workflows:
|
|
|
86
127
|
* [Param tag style](#param-tag-style)
|
|
87
128
|
* [Create a starter config](#create-a-starter-config)
|
|
88
129
|
* [Generate a plugin skeleton](#generate-a-plugin-skeleton)
|
|
130
|
+
* [Full configuration reference](#full-configuration-reference)
|
|
89
131
|
* [CI integration](#ci-integration)
|
|
90
132
|
* [Comparison to YARD's parser](#comparison-to-yards-parser)
|
|
91
|
-
* [Mermaid Architecture Reference](#mermaid-architecture-reference)
|
|
92
|
-
* [Data flow](#data-flow)
|
|
93
133
|
* [Limitations](#limitations)
|
|
94
134
|
* [Roadmap](#roadmap)
|
|
135
|
+
* [Editor Integration](#editor-integration)
|
|
136
|
+
* [VS Code](#vs-code)
|
|
137
|
+
* [RubyMine](#rubymine)
|
|
95
138
|
* [Contributing](#contributing)
|
|
96
139
|
* [Discussion & Community](#discussion--community)
|
|
140
|
+
* [Logo Attribution](#logo-attribution)
|
|
97
141
|
* [License](#license)
|
|
98
142
|
|
|
99
143
|
## Installation
|
|
@@ -118,102 +162,16 @@ gem install docscribe
|
|
|
118
162
|
|
|
119
163
|
Requires Ruby 2.7+.
|
|
120
164
|
|
|
121
|
-
## Quick start
|
|
122
|
-
|
|
123
|
-
Given code:
|
|
124
|
-
|
|
125
|
-
```ruby
|
|
126
|
-
class Demo
|
|
127
|
-
def foo(a, options: {})
|
|
128
|
-
42
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
def bar(verbose: true)
|
|
132
|
-
123
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
private
|
|
136
|
-
|
|
137
|
-
def self.bump
|
|
138
|
-
:ok
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
class << self
|
|
142
|
-
private
|
|
143
|
-
|
|
144
|
-
def internal; end
|
|
145
|
-
end
|
|
146
|
-
end
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
Run:
|
|
150
|
-
|
|
151
|
-
```shell
|
|
152
|
-
echo "...code above..." | docscribe --stdin
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
Output:
|
|
156
|
-
|
|
157
|
-
```ruby
|
|
158
|
-
class Demo
|
|
159
|
-
# +Demo#foo+ -> Integer
|
|
160
|
-
#
|
|
161
|
-
# Method documentation.
|
|
162
|
-
#
|
|
163
|
-
# @param [Object] a Param documentation.
|
|
164
|
-
# @param [Hash] options Param documentation.
|
|
165
|
-
# @return [Integer]
|
|
166
|
-
def foo(a, options: {})
|
|
167
|
-
42
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
# +Demo#bar+ -> Integer
|
|
171
|
-
#
|
|
172
|
-
# Method documentation.
|
|
173
|
-
#
|
|
174
|
-
# @param [Boolean] verbose Param documentation.
|
|
175
|
-
# @return [Integer]
|
|
176
|
-
def bar(verbose: true)
|
|
177
|
-
123
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
private
|
|
181
|
-
|
|
182
|
-
# +Demo.bump+ -> Symbol
|
|
183
|
-
#
|
|
184
|
-
# Method documentation.
|
|
185
|
-
#
|
|
186
|
-
# @return [Symbol]
|
|
187
|
-
def self.bump
|
|
188
|
-
:ok
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
class << self
|
|
192
|
-
private
|
|
193
|
-
|
|
194
|
-
# +Demo.internal+ -> Object
|
|
195
|
-
#
|
|
196
|
-
# Method documentation.
|
|
197
|
-
#
|
|
198
|
-
# @private
|
|
199
|
-
# @return [Object]
|
|
200
|
-
def internal; end
|
|
201
|
-
end
|
|
202
|
-
end
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
> [!NOTE]
|
|
206
|
-
> - The tool inserts doc headers before method headers and preserves everything else.
|
|
207
|
-
> - For methods with a leading Sorbet `sig`, docs are inserted above the first `sig`.
|
|
208
|
-
> - Class methods show with a dot (`+Demo.bump+`, `+Demo.internal+`).
|
|
209
|
-
> - Methods inside `class << self` under `private` are marked `@private`.
|
|
210
|
-
|
|
211
165
|
## Architecture
|
|
212
166
|
|
|
213
|
-
Docscribe is organized into
|
|
214
|
-
then delegates to the core engine which parses source code, collects methods (using an AST walker), builds YARD
|
|
215
|
-
lines — combining heuristic type inference, external RBS/Sorbet signatures, and plugin output — and finally rewrites
|
|
216
|
-
source via a strategy (safe merge or aggressive replace).
|
|
167
|
+
Docscribe is organized into several subsystems. The CLI layer receives user input and orchestrates configuration
|
|
168
|
+
loading, then delegates to the core engine which parses source code, collects methods (using an AST walker), builds YARD
|
|
169
|
+
doc lines — combining heuristic type inference, external RBS/Sorbet signatures, and plugin output — and finally rewrites
|
|
170
|
+
the source via a strategy (safe merge or aggressive replace).
|
|
171
|
+
|
|
172
|
+
In server mode, a persistent daemon (`docscribe server`) keeps the runtime loaded and caches parsed results across
|
|
173
|
+
invocations via an LRU cache, enabling near-instant repeated checks for IDE plugins. A thin client (`docscribe-client`)
|
|
174
|
+
provides minimal-overhead socket communication without loading the full gem.
|
|
217
175
|
|
|
218
176
|
```mermaid
|
|
219
177
|
flowchart TB
|
|
@@ -223,6 +181,9 @@ flowchart TB
|
|
|
223
181
|
Options["CLI::Options\nARGV parsing\n(mode, strategy,\nfilters, flags)"]
|
|
224
182
|
InitCmd["CLI::Init\ndocscribe init\nGenerate config"]
|
|
225
183
|
GenCmd["CLI::Generate\ndocscribe generate\nScaffold plugins"]
|
|
184
|
+
SigsCmd["CLI::Sigs\ndocscribe sigs\nCheck RBS coverage"]
|
|
185
|
+
RbsGenCmd["CLI::RbsGen\ndocscribe rbs\nGenerate RBS from YARD"]
|
|
186
|
+
SarifFormatter["CLI::Formatters::Sarif\nSARIF 2.1 JSON\nCode Scanning"]
|
|
226
187
|
ConfigBuilder["CLI::ConfigBuilder\nApply CLI overrides\nto config"]
|
|
227
188
|
end
|
|
228
189
|
|
|
@@ -244,10 +205,10 @@ flowchart TB
|
|
|
244
205
|
end
|
|
245
206
|
|
|
246
207
|
subgraph Core["Core Engine"]
|
|
247
|
-
InlineRewriter["InlineRewriter\n· parse
|
|
208
|
+
InlineRewriter["InlineRewriter\n· parse -> collect\n· deduplicate -> dispatch\n· rewrite"]
|
|
248
209
|
Collector["Collector\n< Parser::AST::Processor\nAST walker\n· find methods/attrs\n· track visibility\n· track containers"]
|
|
249
210
|
DocBuilder["DocBuilder\nGenerate YARD doc lines\n· combine inference\n· external signatures\n· plugin tags"]
|
|
250
|
-
DocBlock["DocBlock\nSafe strategy:\nparse
|
|
211
|
+
DocBlock["DocBlock\nSafe strategy:\nparse -> merge -> sort\nexisting doc blocks"]
|
|
251
212
|
SourceHelpers["SourceHelpers\nPosition/range\nutilities"]
|
|
252
213
|
end
|
|
253
214
|
|
|
@@ -256,36 +217,57 @@ flowchart TB
|
|
|
256
217
|
Params["Infer::Params\nParameter type\nfrom name + default"]
|
|
257
218
|
Returns["Infer::Returns\nReturn type\nfrom method body"]
|
|
258
219
|
Raises["Infer::Raises\n@raise tags\nfrom raise/rescue"]
|
|
259
|
-
Literals["Infer::Literals\nAST literal
|
|
260
|
-
Names["Infer::Names\n:const node
|
|
220
|
+
Literals["Infer::Literals\nAST literal ->\ntype string"]
|
|
221
|
+
Names["Infer::Names\n:const node ->\nFQN string"]
|
|
261
222
|
ASTWalk["Infer::ASTWalk\nRecursive DFS\nAST traversal"]
|
|
262
223
|
end
|
|
263
224
|
|
|
264
225
|
subgraph Plugins["Plugin System"]
|
|
265
226
|
PluginModule["Plugin\nTag/Collector\ndispatch"]
|
|
266
|
-
Registry["Plugin::Registry\nGlobal registry\n· register
|
|
267
|
-
TagPlugin["Base::TagPlugin\nOverride #call(context)\n
|
|
268
|
-
CollectorPlugin["Base::CollectorPlugin\nOverride #collect(ast, buffer)\n
|
|
227
|
+
Registry["Plugin::Registry\nGlobal registry\n· register -> route\n· tag_entries\n· collector_entries"]
|
|
228
|
+
TagPlugin["Base::TagPlugin\nOverride #call(context)\n-> Array<Tag>"]
|
|
229
|
+
CollectorPlugin["Base::CollectorPlugin\nOverride #collect(ast, buffer)\n-> Array<Hash>"]
|
|
269
230
|
TagValue["Plugin::Tag\nStruct (name, text, types)"]
|
|
270
231
|
Context["Plugin::Context\nMethod snapshot struct"]
|
|
271
232
|
end
|
|
272
233
|
|
|
273
234
|
subgraph Types["External Type System"]
|
|
274
235
|
ProviderChain["ProviderChain\nComposite:\nquery in order\nfirst match wins"]
|
|
275
|
-
RBSProvider["RBS::Provider\n.rbs files\n
|
|
276
|
-
RBSFormatter["RBS::TypeFormatter\nRBS type
|
|
236
|
+
RBSProvider["RBS::Provider\n.rbs files\n-> RBS lib"]
|
|
237
|
+
RBSFormatter["RBS::TypeFormatter\nRBS type ->\nYARD type string"]
|
|
277
238
|
RBSCollection["RBS::CollectionLoader\nrbs_collection\n.lock.yaml"]
|
|
278
239
|
SorbetBase["Sorbet::BaseProvider\nRBS::Prototype::RBI\nbridge"]
|
|
279
240
|
SorbetSource["Sorbet::SourceProvider\nInline sig{}\ndeclarations"]
|
|
280
241
|
SorbetRBI["Sorbet::RBIProvider\n.rbi files\ndirectories"]
|
|
281
242
|
end
|
|
282
243
|
|
|
244
|
+
subgraph YardTypes["YARD Type Parser"]
|
|
245
|
+
YParser["Yard::Parser\nParse YARD type\nstrings -> AST"]
|
|
246
|
+
YFormatter["Yard::Formatter\nYARD AST ->\nRBS string"]
|
|
247
|
+
YTypes["Yard::Types\n9 AST node types\n(Named, Generic, etc.)"]
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
subgraph Server["Server / Daemon"]
|
|
251
|
+
ThinClient["exe/docscribe-client\nThin client\n· socket send/receive\n· no gem load"]
|
|
252
|
+
ServerDaemon["Server::Daemon\nSocket listener\n· check / fix / shutdown\n· JSON-RPC 2.0"]
|
|
253
|
+
Cache["Docscribe::LRUCache\nFile result cache\n(max 1000, by mtime)"]
|
|
254
|
+
end
|
|
255
|
+
|
|
283
256
|
Exe --> Run
|
|
257
|
+
Exe --> ServerDaemon
|
|
258
|
+
ThinClient --> ServerDaemon
|
|
259
|
+
ServerDaemon --> ConfigClass
|
|
260
|
+
ServerDaemon --> Cache
|
|
261
|
+
ServerDaemon --> InlineRewriter
|
|
284
262
|
Run --> Options
|
|
285
263
|
Run --> InitCmd
|
|
286
264
|
Run --> GenCmd
|
|
265
|
+
Run --> SigsCmd
|
|
266
|
+
Run --> RbsGenCmd
|
|
267
|
+
Run --> SarifFormatter
|
|
287
268
|
Run --> ConfigBuilder
|
|
288
269
|
ConfigBuilder --> ConfigClass
|
|
270
|
+
ConfigBuilder --> ServerDaemon
|
|
289
271
|
ConfigClass --> Defaults
|
|
290
272
|
ConfigClass --> Loader
|
|
291
273
|
ConfigClass --> Emit
|
|
@@ -325,13 +307,35 @@ flowchart TB
|
|
|
325
307
|
TagPlugin --> Context
|
|
326
308
|
InlineRewriter --> DocBlock
|
|
327
309
|
InlineRewriter --> SourceHelpers
|
|
310
|
+
RbsGenCmd --> ParsingModule
|
|
311
|
+
RbsGenCmd --> YParser
|
|
312
|
+
YParser --> YTypes
|
|
313
|
+
YParser --> YFormatter
|
|
328
314
|
```
|
|
329
315
|
|
|
330
316
|
### Data flow
|
|
331
317
|
|
|
332
318
|
```mermaid
|
|
333
319
|
flowchart LR
|
|
334
|
-
|
|
320
|
+
subgraph Entry["Entry Points"]
|
|
321
|
+
Direct["docscribe lib\n(no --server)"]
|
|
322
|
+
ViaServer["docscribe --server\nor docscribe-client"]
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
subgraph Daemon["Server Daemon (Unix Socket)"]
|
|
326
|
+
Socket["Daemon#listen_loop\nJSON-RPC 2.0 dispatch"]
|
|
327
|
+
CacheCheck{"File cached &\nmtime fresh?"}
|
|
328
|
+
CacheStorage["LRUCache\n(1000 entries)"]
|
|
329
|
+
ApplyOverrides["apply_cli_overrides\n(reset on nil)"]
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
ViaServer --> Socket
|
|
333
|
+
Socket --> ApplyOverrides
|
|
334
|
+
ApplyOverrides --> CacheCheck
|
|
335
|
+
CacheCheck -->|Hit| Socket
|
|
336
|
+
CacheCheck -->|Miss| Parse
|
|
337
|
+
Direct --> Parse
|
|
338
|
+
Parse["Parsing.parse_buffer\nParser gem / Prism"]
|
|
335
339
|
Parse --> AST["AST + Comments"]
|
|
336
340
|
AST --> Collect["Collector.process\n· Find methods\n· Track visibility\n· Find attr_*"]
|
|
337
341
|
AST --> CollectPlugins["CollectorPlugin#collect\n· Custom AST walks\n· Non-standard constructs"]
|
|
@@ -348,25 +352,42 @@ flowchart LR
|
|
|
348
352
|
ResultDoc --> Strategy{"Strategy?"}
|
|
349
353
|
Strategy -->|Safe| Merge["DocBlock.merge\npreserve + append + sort"]
|
|
350
354
|
Strategy -->|Aggressive| Replace["Replace entirely"]
|
|
351
|
-
Merge --> Rewritten["Rewriter#process\n
|
|
355
|
+
Merge --> Rewritten["Rewriter#process\n-> rewritten source"]
|
|
352
356
|
Replace --> Rewritten
|
|
353
|
-
Rewritten -->
|
|
357
|
+
Rewritten --> Result["Result / response"]
|
|
358
|
+
Rewritten --> CacheStorage
|
|
359
|
+
CacheStorage --> Socket
|
|
360
|
+
Result -->|Direct mode| Output["Modified .rb file / STDOUT"]
|
|
361
|
+
Result -->|Server mode| Socket
|
|
354
362
|
```
|
|
355
363
|
|
|
356
364
|
## CLI
|
|
357
365
|
|
|
358
366
|
```shell
|
|
359
367
|
docscribe [options] [files...]
|
|
368
|
+
docscribe init [options]
|
|
369
|
+
docscribe generate [type] [name] [options]
|
|
370
|
+
docscribe sigs [options] [files...]
|
|
371
|
+
docscribe rbs [options] [files...]
|
|
372
|
+
docscribe update_types [directory]
|
|
373
|
+
docscribe check_for_comments [paths...]
|
|
374
|
+
docscribe server [start|status|stop] [options]
|
|
360
375
|
```
|
|
361
376
|
|
|
362
377
|
Docscribe has three main ways to run:
|
|
363
378
|
|
|
364
|
-
- **Inspect mode** (default): checks what safe doc updates would be applied and exits
|
|
379
|
+
- **Inspect mode** (default): checks what safe doc updates would be applied and exits 1 if files need changes.
|
|
365
380
|
- **Safe autocorrect** (`-a`, `--autocorrect`): writes safe, non-destructive updates in place.
|
|
366
381
|
- **Aggressive autocorrect** (`-A`, `--autocorrect-all`): rewrites existing doc blocks more aggressively.
|
|
367
382
|
- **STDIN mode** (`--stdin`): reads Ruby source from STDIN and prints rewritten source to STDOUT.
|
|
368
383
|
|
|
369
|
-
If you pass no files and don
|
|
384
|
+
If you pass no files and don't use `--stdin`, Docscribe processes the current directory recursively.
|
|
385
|
+
|
|
386
|
+
### Exit codes
|
|
387
|
+
|
|
388
|
+
- **0** — all files are up to date (no changes needed)
|
|
389
|
+
- **1** — some files need documentation updates
|
|
390
|
+
- **2** — execution error (parse error, missing files, etc.)
|
|
370
391
|
|
|
371
392
|
### Options
|
|
372
393
|
|
|
@@ -385,10 +406,32 @@ If you pass no files and don’t use `--stdin`, Docscribe processes the current
|
|
|
385
406
|
Read source from STDIN and print rewritten output.
|
|
386
407
|
|
|
387
408
|
- `--verbose`
|
|
388
|
-
Print per-file actions.
|
|
409
|
+
Print per-file actions. Also enables `--progress`.
|
|
410
|
+
|
|
411
|
+
- `--progress`
|
|
412
|
+
Show progress (`[N/total] path/to/file.rb`) on stderr as each file is processed.
|
|
413
|
+
|
|
414
|
+
- `--quiet` (`-q`)
|
|
415
|
+
Only show status, no details (suppresses change reasons).
|
|
416
|
+
Overrides the default detailed output.
|
|
389
417
|
|
|
390
418
|
- `--explain`
|
|
391
|
-
Show detailed reasons for each file
|
|
419
|
+
Show detailed reasons for each file (default; no-op for compatibility).
|
|
420
|
+
|
|
421
|
+
- `--server`
|
|
422
|
+
Run via a persistent daemon (Unix socket). Speeds up repeated invocations
|
|
423
|
+
by keeping the Ruby runtime loaded and caching results.
|
|
424
|
+
|
|
425
|
+
- `-k`, `--keep-descriptions`
|
|
426
|
+
Preserve existing documentation text when rebuilding doc blocks in aggressive mode.
|
|
427
|
+
|
|
428
|
+
- `-B`, `--no-boilerplate`
|
|
429
|
+
Suppress boilerplate text (`Method documentation.`, `Param documentation.`) in output.
|
|
430
|
+
Equivalent to `emit.include_default_message: false` and `emit.include_param_documentation: false` in config.
|
|
431
|
+
|
|
432
|
+
- `--format FORMAT`
|
|
433
|
+
Output format: `text` (default, human-readable), `json` (machine-readable, RuboCop-compatible), or `sarif` (SARIF 2.1
|
|
434
|
+
JSON, compatible with GitHub Code Scanning).
|
|
392
435
|
|
|
393
436
|
- `--rbs`
|
|
394
437
|
Use RBS signatures for `@param`/`@return` when available (falls back to inference).
|
|
@@ -396,6 +439,12 @@ If you pass no files and don’t use `--stdin`, Docscribe processes the current
|
|
|
396
439
|
- `--sig-dir DIR`
|
|
397
440
|
Add an RBS signature directory (repeatable). Implies `--rbs`.
|
|
398
441
|
|
|
442
|
+
- `--sorbet`
|
|
443
|
+
Use Sorbet signatures for `@param`/`@return` when available (falls back to inference).
|
|
444
|
+
|
|
445
|
+
- `--rbi-dir DIR`
|
|
446
|
+
Add an Sorbet RBI directory (repeatable). Implies `--sorbet`.
|
|
447
|
+
|
|
399
448
|
- `--include PATTERN`
|
|
400
449
|
Include PATTERN (method id or file path; glob or `/regex/`).
|
|
401
450
|
|
|
@@ -456,9 +505,224 @@ If you pass no files and don’t use `--stdin`, Docscribe processes the current
|
|
|
456
505
|
|
|
457
506
|
- Show detailed reasons for files that would change:
|
|
458
507
|
```shell
|
|
459
|
-
docscribe --verbose
|
|
508
|
+
docscribe --verbose lib
|
|
460
509
|
```
|
|
461
510
|
|
|
511
|
+
### `docscribe sigs` — check RBS signature coverage
|
|
512
|
+
|
|
513
|
+
> [!WARNING]
|
|
514
|
+
> `docscribe sigs` requires **Ruby 3.0+** and the `rbs` gem. On Ruby 2.7 it will print an error and exit with code 2.
|
|
515
|
+
|
|
516
|
+
`docscribe sigs` parses Ruby files, extracts method definitions, and checks each
|
|
517
|
+
method against the configured RBS signature directories. Reports methods that lack RBS type signatures.
|
|
518
|
+
|
|
519
|
+
```shell
|
|
520
|
+
# Check lib/ against sig/ signatures
|
|
521
|
+
docscribe sigs lib
|
|
522
|
+
|
|
523
|
+
# Use RBS collection
|
|
524
|
+
docscribe sigs --rbs-collection lib
|
|
525
|
+
|
|
526
|
+
# Multiple signature directories
|
|
527
|
+
docscribe sigs -s sig -s vendor/sigs lib
|
|
528
|
+
|
|
529
|
+
# Verbose output (also prints methods that have signatures)
|
|
530
|
+
docscribe sigs --verbose lib
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
**Flags:**
|
|
534
|
+
|
|
535
|
+
- `-s`, `--sig-dir DIR` — add an RBS signature directory (repeatable). Default: `sig`.
|
|
536
|
+
- `--rbs-collection` — use RBS collection (reads `rbs_collection.lock.yaml`).
|
|
537
|
+
- `--verbose` — also print methods that have signatures.
|
|
538
|
+
- `-h`, `--help` — show help.
|
|
539
|
+
|
|
540
|
+
**Exit codes:**
|
|
541
|
+
|
|
542
|
+
- `0` — all methods have RBS signatures.
|
|
543
|
+
- `1` — some methods lack RBS signatures.
|
|
544
|
+
- `2` — error occurred.
|
|
545
|
+
|
|
546
|
+
### `docscribe rbs` — generate RBS from YARD
|
|
547
|
+
|
|
548
|
+
> [!WARNING]
|
|
549
|
+
> `docscribe rbs` requires **Ruby 3.0+** and the `rbs` gem. On Ruby 2.7 it will print an error and exit with code 2.
|
|
550
|
+
|
|
551
|
+
`docscribe rbs` parses YARD comments (`@param`, `@return`, `@option`) from Ruby files
|
|
552
|
+
and generates corresponding `.rbs` signature files.
|
|
553
|
+
|
|
554
|
+
```shell
|
|
555
|
+
# Generate RBS files in sig/
|
|
556
|
+
docscribe rbs lib
|
|
557
|
+
|
|
558
|
+
# Print to stdout (no files written)
|
|
559
|
+
docscribe rbs -n lib
|
|
560
|
+
|
|
561
|
+
# Specify output directory
|
|
562
|
+
docscribe rbs -o sig/gen lib
|
|
563
|
+
|
|
564
|
+
# Overwrite existing files
|
|
565
|
+
docscribe rbs -f lib
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
**Flags:**
|
|
569
|
+
|
|
570
|
+
- `-o`, `--output DIR` — output directory (default: `sig`).
|
|
571
|
+
- `-n`, `--dry-run` — print generated RBS to stdout, do not write files.
|
|
572
|
+
- `-f`, `--force` — overwrite existing files.
|
|
573
|
+
- `-h`, `--help` — show help.
|
|
574
|
+
|
|
575
|
+
**Exit codes:**
|
|
576
|
+
|
|
577
|
+
- `0` — success.
|
|
578
|
+
- `1` — errors occurred during processing.
|
|
579
|
+
- `2` — execution error (no files found).
|
|
580
|
+
|
|
581
|
+
```ruby
|
|
582
|
+
# Example: lib/user.rb
|
|
583
|
+
class User
|
|
584
|
+
# @param [String] name
|
|
585
|
+
# @param [Integer] age
|
|
586
|
+
# @return [User]
|
|
587
|
+
def initialize(name, age)
|
|
588
|
+
@name = name
|
|
589
|
+
@age = age
|
|
590
|
+
end
|
|
591
|
+
end
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
```shell
|
|
595
|
+
docscribe rbs lib
|
|
596
|
+
# Generated sig/user.rbs
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
```ruby.rbs
|
|
600
|
+
# sig/user.rbs
|
|
601
|
+
class User
|
|
602
|
+
def initialize: (String name, Integer age) -> User
|
|
603
|
+
end
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
### `docscribe update_types` — two-pass type-aware documentation update
|
|
607
|
+
|
|
608
|
+
> [!NOTE]
|
|
609
|
+
> `docscribe update_types` is a convenience alias for the two-pass workflow above. It requires Ruby 3.0+ and the `rbs`
|
|
610
|
+
> gem (because of `--rbs-collection`). The RBS collection must be set up first with
|
|
611
|
+
> `bundle exec rbs collection install`. Type accuracy depends on your RBS signatures — if signatures are incomplete or
|
|
612
|
+
> missing, types will fall back to AST inference.
|
|
613
|
+
|
|
614
|
+
`docscribe update_types` runs two passes to bring both docs and RBS signatures up to date:
|
|
615
|
+
|
|
616
|
+
1. **Pass 1** — `docscribe -AkB --rbs-collection <dir>`: aggressively rebuilds doc blocks, preserves existing
|
|
617
|
+
descriptions, suppresses boilerplate, uses RBS collection types.
|
|
618
|
+
2. **Pass 2** — `docscribe -aB --rbs-collection <dir>`: safe merge cleanup with no boilerplate.
|
|
619
|
+
|
|
620
|
+
```shell
|
|
621
|
+
# Update docs in lib/ using RBS collection
|
|
622
|
+
docscribe update_types lib
|
|
623
|
+
|
|
624
|
+
# Defaults to current directory
|
|
625
|
+
docscribe update_types
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
### `docscribe check_for_comments` — find placeholder documentation
|
|
629
|
+
|
|
630
|
+
`docscribe check_for_comments` scans Ruby source files for YARD comments that still contain default placeholder
|
|
631
|
+
text. Reads the configured placeholder messages from `docscribe.yml` (or built-in defaults). Useful in CI to catch
|
|
632
|
+
files where auto-generated text was never replaced with real documentation.
|
|
633
|
+
|
|
634
|
+
```shell
|
|
635
|
+
# Scan entire project
|
|
636
|
+
docscribe check_for_comments
|
|
637
|
+
|
|
638
|
+
# Scan specific directories
|
|
639
|
+
docscribe check_for_comments lib app
|
|
640
|
+
```
|
|
641
|
+
|
|
642
|
+
Exit code `0` if no placeholders found, `1` if any are detected.
|
|
643
|
+
|
|
644
|
+
**Flags:**
|
|
645
|
+
|
|
646
|
+
- `-h`, `--help` — show help.
|
|
647
|
+
|
|
648
|
+
### `docscribe server` — persistent daemon mode
|
|
649
|
+
|
|
650
|
+
> [!NOTE]
|
|
651
|
+
> Server mode requires **Ruby 3.1+** (`Process.fork` for background daemon).
|
|
652
|
+
|
|
653
|
+
`docscribe server` starts a background daemon that keeps the Ruby runtime loaded and caches parsed ASTs across
|
|
654
|
+
invocations. Subsequent `docscribe` calls with `--server` communicate with the daemon over a Unix socket instead of
|
|
655
|
+
reloading the entire toolchain.
|
|
656
|
+
|
|
657
|
+
```shell
|
|
658
|
+
# Start the daemon (auto-detached in background)
|
|
659
|
+
docscribe server start
|
|
660
|
+
|
|
661
|
+
# Check if the daemon is running
|
|
662
|
+
docscribe server status
|
|
663
|
+
|
|
664
|
+
# Stop the daemon
|
|
665
|
+
docscribe server stop
|
|
666
|
+
|
|
667
|
+
# Use the daemon for file checks (much faster on repeated calls)
|
|
668
|
+
docscribe --server lib
|
|
669
|
+
docscribe -a --server lib
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
**How it works:**
|
|
673
|
+
|
|
674
|
+
1. `docscribe server start` forks a background process that listens on a Unix socket in the system temp directory.
|
|
675
|
+
2. The socket path is derived from the project root, `Gemfile.lock` mtime, and `rbs_collection.lock.yaml` mtime — so any
|
|
676
|
+
environment change spawns a fresh daemon automatically.
|
|
677
|
+
3. Client requests (`docscribe --server`) send JSON-RPC 2.0 messages over the socket: `check` (inspect), `fix` (apply
|
|
678
|
+
changes), `shutdown`, and `ping` (health/version info).
|
|
679
|
+
4. The daemon holds a reusable `Docscribe::Config` instance and an LRU file cache (bounded at 1000 entries), so repeated
|
|
680
|
+
checks on the same files are nearly instant.
|
|
681
|
+
5. After 5 minutes of inactivity the daemon shuts down automatically.
|
|
682
|
+
|
|
683
|
+
**Thin client (`docscribe-client`):**
|
|
684
|
+
|
|
685
|
+
For IDE plugins and CI systems that need minimal startup overhead, a standalone thin client is available as
|
|
686
|
+
`exe/docscribe-client`. It connects to the daemon via the same Unix socket protocol without loading the full docscribe
|
|
687
|
+
gem:
|
|
688
|
+
|
|
689
|
+
```shell
|
|
690
|
+
# Check a file via the daemon
|
|
691
|
+
docscribe-client --check lib/user.rb
|
|
692
|
+
|
|
693
|
+
# Apply fixes via the daemon
|
|
694
|
+
docscribe-client --fix lib/user.rb
|
|
695
|
+
|
|
696
|
+
# Check if the daemon is running
|
|
697
|
+
docscribe-client --status
|
|
698
|
+
|
|
699
|
+
# Get version, pid, uptime from the daemon
|
|
700
|
+
docscribe-client --ping
|
|
701
|
+
|
|
702
|
+
# Stop the daemon
|
|
703
|
+
docscribe-client --shutdown
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
The thin client is used automatically by
|
|
707
|
+
the [VS Code](https://marketplace.visualstudio.com/items?itemName=unurgunite.docscribe-vscode)
|
|
708
|
+
and [RubyMine](https://plugins.jetbrains.com/plugin/32349-docscribe) plugins when the daemon is running.
|
|
709
|
+
|
|
710
|
+
**Environment invalidation:**
|
|
711
|
+
|
|
712
|
+
The daemon's socket path includes a hash of:
|
|
713
|
+
|
|
714
|
+
- `Gemfile.lock` mtime — gem changes that may affect RBS signatures.
|
|
715
|
+
- `rbs_collection.lock.yaml` mtime — RBS collection signature updates.
|
|
716
|
+
|
|
717
|
+
If any of these files change, the next `docscribe --server` call will start a new daemon automatically. The old daemon
|
|
718
|
+
is left to idle-timeout on its own.
|
|
719
|
+
|
|
720
|
+
**CLI override handling:**
|
|
721
|
+
|
|
722
|
+
When CLI overrides (`-C`, `--include`, `--exclude`, etc.) change between requests, the daemon resets its effective
|
|
723
|
+
configuration, clears the file cache, and applies the new overrides before processing. If no overrides are passed, the
|
|
724
|
+
cached state from the initial load is used, preserving performance.
|
|
725
|
+
|
|
462
726
|
## Update strategies
|
|
463
727
|
|
|
464
728
|
Docscribe supports two update strategies: **safe** and **aggressive**.
|
|
@@ -493,6 +757,9 @@ Aggressive strategy:
|
|
|
493
757
|
|
|
494
758
|
Use it when you want to rebaseline or regenerate docs wholesale.
|
|
495
759
|
|
|
760
|
+
> [!CAUTION]
|
|
761
|
+
> Aggressive mode rewrites existing doc blocks entirely. Review changes with `git diff` before committing.
|
|
762
|
+
|
|
496
763
|
### Output markers
|
|
497
764
|
|
|
498
765
|
In inspect mode, Docscribe prints one character per file:
|
|
@@ -500,14 +767,20 @@ In inspect mode, Docscribe prints one character per file:
|
|
|
500
767
|
- `.` = file is up to date
|
|
501
768
|
- `F` = file would change
|
|
502
769
|
- `E` = file had an error
|
|
770
|
+
- `M` = type mismatch detected (external RBS/Sorbet signature differs from inferred type)
|
|
503
771
|
|
|
504
772
|
In write modes:
|
|
505
773
|
|
|
506
774
|
- `.` = file already OK
|
|
507
775
|
- `C` = file was updated
|
|
508
776
|
- `E` = file had an error
|
|
777
|
+
- `M` = file updated but type mismatch remains
|
|
778
|
+
|
|
779
|
+
> [!IMPORTANT]
|
|
780
|
+
> The `M` marker only appears when `--rbs` or `--sorbet` is enabled, since type mismatches are detected by
|
|
781
|
+
> comparing external signatures against inferred types.
|
|
509
782
|
|
|
510
|
-
With `--verbose`, Docscribe prints per-file statuses instead.
|
|
783
|
+
With `--verbose`, Docscribe prints per-file statuses instead; type mismatches show as `MT` with the specific difference.
|
|
511
784
|
|
|
512
785
|
With `--explain`, Docscribe also prints detailed reasons, such as:
|
|
513
786
|
|
|
@@ -516,6 +789,27 @@ With `--explain`, Docscribe also prints detailed reasons, such as:
|
|
|
516
789
|
- missing module_function note
|
|
517
790
|
- unsorted tags
|
|
518
791
|
|
|
792
|
+
Use `--quiet` to suppress these details and show only file names and the summary line.
|
|
793
|
+
|
|
794
|
+
## Tips & tricks
|
|
795
|
+
|
|
796
|
+
Useful flag combinations for common workflows:
|
|
797
|
+
|
|
798
|
+
- `docscribe -aB lib` — safe autocorrect without boilerplate. Uses safe mode but suppresses default text, leaving only
|
|
799
|
+
type tags (`@param`, `@return`). Clean minimal docs.
|
|
800
|
+
- `docscribe -AkB lib` — aggressive autocorrect with preserved descriptions and no boilerplate. Rebuilds doc blocks,
|
|
801
|
+
keeps your hand-written descriptions, suppresses default text. Best for rebaselining.
|
|
802
|
+
- `docscribe -a --rbs-collection lib` — safe autocorrect using RBS gem collection signatures. Recommended for Rails
|
|
803
|
+
projects.
|
|
804
|
+
- `docscribe -a --sorbet --rbi-dir sorbet/rbi lib` — safe autocorrect using Sorbet RBI signatures.
|
|
805
|
+
- `docscribe update_types lib` — two-pass type-aware update: aggressively rebuilds docs with kept descriptions and RBS
|
|
806
|
+
collection, then safe-merges to clean up.
|
|
807
|
+
See [docscribe update_types](#docscribe-update_types--two-pass-type-aware-documentation-update).
|
|
808
|
+
|
|
809
|
+
> [!NOTE]
|
|
810
|
+
> `docscribe update_types` is a convenient shortcut, but be aware it uses `--rbs-collection` under the hood.
|
|
811
|
+
> If your RBS signatures are incomplete, types may fall back to AST inference.
|
|
812
|
+
|
|
519
813
|
## Parser backend (Parser gem vs Prism)
|
|
520
814
|
|
|
521
815
|
Docscribe internally works with `parser`-gem-compatible AST nodes and `Parser::Source::*` objects (so it can use
|
|
@@ -524,6 +818,11 @@ Docscribe internally works with `parser`-gem-compatible AST nodes and `Parser::S
|
|
|
524
818
|
- On Ruby **<= 3.3**, Docscribe parses using the `parser` gem.
|
|
525
819
|
- On Ruby **>= 3.4**, Docscribe parses using **Prism** and translates the tree into the `parser` gem's AST.
|
|
526
820
|
|
|
821
|
+
> [!NOTE]
|
|
822
|
+
> Prism support is automatic on Ruby 3.4+. On earlier Rubies, the `parser` gem is always used.
|
|
823
|
+
> You can override with `DOCSCRIBE_PARSER_BACKEND=prism` on Ruby 3.1+ if you have the `prism` gem installed,
|
|
824
|
+
> but this is not recommended for production use.
|
|
825
|
+
|
|
527
826
|
You can force a backend with an environment variable:
|
|
528
827
|
|
|
529
828
|
```shell
|
|
@@ -537,16 +836,33 @@ Docscribe can improve generated `@param` and `@return` types by reading external
|
|
|
537
836
|
AST inference.
|
|
538
837
|
|
|
539
838
|
> [!IMPORTANT]
|
|
540
|
-
>
|
|
541
|
-
> - inline Sorbet `sig` declarations in the current Ruby source;
|
|
542
|
-
> - Sorbet RBI files;
|
|
543
|
-
> - RBS files;
|
|
544
|
-
> - AST inference fallback.
|
|
839
|
+
> Docscribe resolves types in a two-level chain. For **documentation tags** (`@param`, `@return`), the priority is:
|
|
545
840
|
>
|
|
546
|
-
>
|
|
841
|
+
> | Priority | Source | When active |
|
|
842
|
+
> |----------|-------------------------------------------------------------|----------------------------------------------------------|
|
|
843
|
+
> | **1** | Inline Sorbet `sig { ... }` in current file | `--sorbet` or `sorbet.enabled: true` |
|
|
844
|
+
> | **2** | Sorbet RBI files (`.rbi`) | `--sorbet --rbi-dir` or `sorbet.rbi_dirs` |
|
|
845
|
+
> | **3** | RBS files (sig_dirs + collection, loaded into one env) | `--rbs --sig-dir` / `--rbs-collection` or `rbs.*` config |
|
|
846
|
+
> | **3a** | └─ Fallback: sig_dirs only (collection dropped on conflict) | Automatic if priority 3 env load fails |
|
|
847
|
+
> | **4** | AST inference (fallback) | Always active |
|
|
848
|
+
>
|
|
849
|
+
> For **intra-method body inference** (e.g. resolving `Integer#positive?` -> `Boolean`), a separate core RBS provider
|
|
850
|
+
> loads only stdlib/built-in signatures and is active automatically when the `rbs` gem is available — even without
|
|
851
|
+
> `--rbs`.
|
|
852
|
+
>
|
|
853
|
+
> If an external signature cannot be loaded or parsed, Docscribe falls back to the next source in the chain instead of
|
|
854
|
+
> failing.
|
|
547
855
|
|
|
548
856
|
### RBS
|
|
549
857
|
|
|
858
|
+
> [!IMPORTANT]
|
|
859
|
+
> All RBS features require the `rbs` gem. Add `gem "rbs"` to your Gemfile and run `bundle install`,
|
|
860
|
+
> or install it globally with `gem install rbs`.
|
|
861
|
+
>
|
|
862
|
+
> On Ruby 2.7 the `rbs` gem cannot load at all — `--rbs`, `--sig-dir`, `--rbs-collection`,
|
|
863
|
+
> `docscribe sigs`, and `docscribe rbs` will print a warning and fall back to AST-only inference.
|
|
864
|
+
> On Ruby 3.0+, if the gem is missing, Docscribe silently falls back to inference when you pass RBS flags.
|
|
865
|
+
|
|
550
866
|
Docscribe can read method signatures from `.rbs` files and use them to generate more accurate parameter and return
|
|
551
867
|
types.
|
|
552
868
|
|
|
@@ -566,12 +882,23 @@ Config:
|
|
|
566
882
|
|
|
567
883
|
```yaml
|
|
568
884
|
rbs:
|
|
569
|
-
enabled:
|
|
885
|
+
enabled: false
|
|
570
886
|
sig_dirs:
|
|
571
887
|
- sig
|
|
888
|
+
collection: false
|
|
572
889
|
collapse_generics: false
|
|
890
|
+
collapse_object_generics: false
|
|
891
|
+
warn_missing_collection: true
|
|
573
892
|
```
|
|
574
893
|
|
|
894
|
+
- `collection` — enable auto-discovery of RBS collection from `rbs_collection.lock.yaml`. Pass `--rbs-collection` on the
|
|
895
|
+
CLI.
|
|
896
|
+
- `collapse_generics` — strip all generic type arguments (e.g. `Array<String>` -> `Array`).
|
|
897
|
+
- `collapse_object_generics` — only strip generic arguments when all are `Object` (e.g. `Hash<Object, Object>` ->
|
|
898
|
+
`Hash`, but `Hash<Symbol, Object>` stays).
|
|
899
|
+
- `warn_missing_collection` — warn on stderr when `rbs_collection.lock.yaml` is found but collection is not enabled. Set
|
|
900
|
+
to `false` to suppress.
|
|
901
|
+
|
|
575
902
|
Example:
|
|
576
903
|
|
|
577
904
|
```ruby
|
|
@@ -794,24 +1121,29 @@ end
|
|
|
794
1121
|
|
|
795
1122
|
### Generic type formatting
|
|
796
1123
|
|
|
797
|
-
Both RBS and Sorbet integrations support
|
|
1124
|
+
Both RBS and Sorbet integrations support generic type collapsing.
|
|
798
1125
|
|
|
799
|
-
|
|
1126
|
+
**`collapse_generics`** — strips all generic type arguments.
|
|
1127
|
+
**`collapse_object_generics`** — only strips arguments when all are `Object` (e.g. `Hash<Object, Object>` -> `Hash`, but
|
|
1128
|
+
`Hash<Symbol, Object>` stays).
|
|
1129
|
+
|
|
1130
|
+
When both disabled:
|
|
800
1131
|
|
|
801
1132
|
```yaml
|
|
802
1133
|
rbs:
|
|
803
1134
|
collapse_generics: false
|
|
1135
|
+
collapse_object_generics: false
|
|
804
1136
|
|
|
805
1137
|
sorbet:
|
|
806
1138
|
collapse_generics: false
|
|
807
1139
|
```
|
|
808
1140
|
|
|
809
|
-
Docscribe preserves generic container details
|
|
1141
|
+
Docscribe preserves generic container details, for example:
|
|
810
1142
|
|
|
811
1143
|
- `Array<String>`
|
|
812
1144
|
- `Hash<Symbol, Integer>`
|
|
813
1145
|
|
|
814
|
-
When enabled:
|
|
1146
|
+
When `collapse_generics` is enabled:
|
|
815
1147
|
|
|
816
1148
|
```yaml
|
|
817
1149
|
rbs:
|
|
@@ -821,7 +1153,7 @@ sorbet:
|
|
|
821
1153
|
collapse_generics: true
|
|
822
1154
|
```
|
|
823
1155
|
|
|
824
|
-
Docscribe simplifies container types to their outer names, for example:
|
|
1156
|
+
Docscribe simplifies all container types to their outer names, for example:
|
|
825
1157
|
|
|
826
1158
|
- `Array`
|
|
827
1159
|
- `Hash`
|
|
@@ -856,6 +1188,11 @@ Return values:
|
|
|
856
1188
|
- For simple bodies, Docscribe looks at the last expression or explicit `return`.
|
|
857
1189
|
- Unions with `nil` become optional types (e.g. `String` or `nil` -> `String?`).
|
|
858
1190
|
- For control flow (`if`/`case`), it unifies branches conservatively.
|
|
1191
|
+
|
|
1192
|
+
> [!TIP]
|
|
1193
|
+
> Docscribe resolves return types for core Ruby methods (`Integer#positive?`, `String#upcase`, etc.)
|
|
1194
|
+
> **even without `--rbs`** — the core RBS provider is always active when the `rbs` gem is available.
|
|
1195
|
+
|
|
859
1196
|
- **RBS core type inference**: when `--rbs` is enabled, Docscribe resolves return types for method calls on core types
|
|
860
1197
|
from their RBS definitions:
|
|
861
1198
|
- `arg.positive?` (`arg = 1`) -> `Boolean` (from `Integer#positive?`)
|
|
@@ -1168,6 +1505,19 @@ Sample plugins available at [examples](examples/plugins):
|
|
|
1168
1505
|
|
|
1169
1506
|
Docscribe can be configured via a YAML file (`docscribe.yml` by default, or pass `--config PATH`).
|
|
1170
1507
|
|
|
1508
|
+
### Anonymous block parameters
|
|
1509
|
+
|
|
1510
|
+
Ruby 3.2+ allows anonymous block arguments (`def foo(&)`). By default, Docscribe generates `@param [Proc] block` for
|
|
1511
|
+
these — but since the parameter has no name, the tag is misleading.
|
|
1512
|
+
|
|
1513
|
+
To suppress the `@param` tag for anonymous block arguments:
|
|
1514
|
+
|
|
1515
|
+
```yaml
|
|
1516
|
+
skip_anonymous_block_params: true
|
|
1517
|
+
```
|
|
1518
|
+
|
|
1519
|
+
When `false` (default), anonymous block params generate `@param [Proc] block`.
|
|
1520
|
+
|
|
1171
1521
|
### Filtering
|
|
1172
1522
|
|
|
1173
1523
|
Docscribe can filter both *files* and *methods*.
|
|
@@ -1391,6 +1741,7 @@ docscribe generate tag SincePlugin --stdout
|
|
|
1391
1741
|
```
|
|
1392
1742
|
|
|
1393
1743
|
The generated file contains:
|
|
1744
|
+
|
|
1394
1745
|
- the correct base class (`Base::TagPlugin` or `Base::CollectorPlugin`)
|
|
1395
1746
|
- inline comments describing every available `Context` attribute (TagPlugin)
|
|
1396
1747
|
or the expected return shape (CollectorPlugin)
|
|
@@ -1401,6 +1752,58 @@ The generated file contains:
|
|
|
1401
1752
|
> The class name must be a valid Ruby constant (`MyPlugin`, `My::Plugin`).
|
|
1402
1753
|
> The output filename is the snake_case equivalent (`my_plugin.rb`, `my/plugin.rb`).
|
|
1403
1754
|
|
|
1755
|
+
### Full configuration reference
|
|
1756
|
+
|
|
1757
|
+
<details>
|
|
1758
|
+
<summary>Click to expand — 43 config keys</summary>
|
|
1759
|
+
|
|
1760
|
+
| Key | Type | Default | Description |
|
|
1761
|
+
|-------------------------------------------|------------|----------------------------------------------------------------------------------------------|----------------------------------------------------------------------------|
|
|
1762
|
+
| `keep_descriptions` | `bool` | `false` | Preserve existing doc text in aggressive mode |
|
|
1763
|
+
| `skip_anonymous_block_params` | `bool` | `false` | Skip `@param [Proc] block` for anonymous `&` params |
|
|
1764
|
+
| `emit.header` | `bool` | `false` | Generate method header line (`+#foo+ -> ...`) |
|
|
1765
|
+
| `emit.include_default_message` | `bool` | `true` | Insert default message (`Method documentation.`) |
|
|
1766
|
+
| `emit.include_param_documentation` | `bool` | `true` | Insert param description text (`Param documentation.`) |
|
|
1767
|
+
| `emit.param_tags` | `bool` | `true` | Generate `@param` tags |
|
|
1768
|
+
| `emit.return_tag` | `bool` | `true` | Generate `@return` tag |
|
|
1769
|
+
| `emit.visibility_tags` | `bool` | `true` | Generate `@private`/`@protected` tags |
|
|
1770
|
+
| `emit.raise_tags` | `bool` | `true` | Generate `@raise` tags (for `raise` in method body) |
|
|
1771
|
+
| `emit.rescue_conditional_returns` | `bool` | `true` | Consider `rescue` branches in return type inference |
|
|
1772
|
+
| `emit.attributes` | `bool` | `false` | Generate `@!attribute` for `attr_*` and `Struct.new` |
|
|
1773
|
+
| `doc.default_message` | `string` | `"Method documentation."` | Default text for method description |
|
|
1774
|
+
| `doc.param_documentation` | `string` | `"Param documentation."` | Default text for param description |
|
|
1775
|
+
| `doc.sort_tags` | `bool` | `true` | Sort tags according to `tag_order` |
|
|
1776
|
+
| `doc.tag_order` | `string[]` | `["todo","note","api","private","protected","param","option","yieldparam","raise","return"]` | Tag sort order |
|
|
1777
|
+
| `doc.param_tag_style` | `string` | `"type_name"` | `@param` style: `type_name` (`[Type] name`) or `name_type` (`name [Type]`) |
|
|
1778
|
+
| `inference.fallback_type` | `string` | `"Object"` | Fallback type when inference yields no result |
|
|
1779
|
+
| `inference.nil_as_optional` | `bool` | `true` | Treat `nil` as an optional type |
|
|
1780
|
+
| `inference.treat_options_keyword_as_hash` | `bool` | `true` | Treat `**options` as `Hash` in `@option` tags |
|
|
1781
|
+
| `filter.include` | `string[]` | `[]` | Only include methods matching pattern |
|
|
1782
|
+
| `filter.exclude` | `string[]` | `[]` | Exclude methods matching pattern |
|
|
1783
|
+
| `filter.visibilities` | `string[]` | `["public","protected","private"]` | Visibilities to process |
|
|
1784
|
+
| `filter.scopes` | `string[]` | `["instance","class"]` | Scopes to process |
|
|
1785
|
+
| `filter.files.include` | `string[]` | `[]` | Only process files matching pattern |
|
|
1786
|
+
| `filter.files.exclude` | `string[]` | `["spec"]` | Skip files matching pattern |
|
|
1787
|
+
| `methods.instance.public` | `object` | `{}` | Overrides for instance public methods |
|
|
1788
|
+
| `methods.instance.protected` | `object` | `{}` | Overrides for instance protected methods |
|
|
1789
|
+
| `methods.instance.private` | `object` | `{}` | Overrides for instance private methods |
|
|
1790
|
+
| `methods.class.public` | `object` | `{}` | Overrides for class public methods |
|
|
1791
|
+
| `methods.class.protected` | `object` | `{}` | Overrides for class protected methods |
|
|
1792
|
+
| `methods.class.private` | `object` | `{}` | Overrides for class private methods |
|
|
1793
|
+
| `rbs.enabled` | `bool` | `false` | Enable RBS integration |
|
|
1794
|
+
| `rbs.collection` | `bool` | `false` | Auto-discover RBS collection from `rbs_collection.lock.yaml` |
|
|
1795
|
+
| `rbs.sig_dirs` | `string[]` | `["sig"]` | RBS signature directories |
|
|
1796
|
+
| `rbs.collection_dirs` | `string[]` | `[]` | RBS collection directories (set automatically) |
|
|
1797
|
+
| `rbs.collapse_generics` | `bool` | `false` | Strip generic arguments (`Array<String>` -> `Array`) |
|
|
1798
|
+
| `rbs.collapse_object_generics` | `bool` | `false` | Strip generics only when all are `Object` |
|
|
1799
|
+
| `rbs.warn_missing_collection` | `bool` | `true` | Warn when `rbs_collection.lock.yaml` found but collection not enabled |
|
|
1800
|
+
| `sorbet.enabled` | `bool` | `false` | Enable Sorbet integration |
|
|
1801
|
+
| `sorbet.rbi_dirs` | `string[]` | `["sorbet/rbi","rbi"]` | RBI file directories |
|
|
1802
|
+
| `sorbet.collapse_generics` | `bool` | `false` | Strip generic arguments from Sorbet signatures |
|
|
1803
|
+
| `plugins.require` | `string[]` | `[]` | Plugin paths for `require` |
|
|
1804
|
+
|
|
1805
|
+
</details>
|
|
1806
|
+
|
|
1404
1807
|
## CI integration
|
|
1405
1808
|
|
|
1406
1809
|
Fail the build if files would need safe updates:
|
|
@@ -1417,11 +1820,33 @@ Apply safe fixes before the test stage:
|
|
|
1417
1820
|
run: docscribe -a lib
|
|
1418
1821
|
```
|
|
1419
1822
|
|
|
1420
|
-
|
|
1823
|
+
Rebuild docs aggressively, preserve descriptions, suppress boilerplate, with verbose output:
|
|
1421
1824
|
|
|
1422
1825
|
```yaml
|
|
1423
1826
|
- name: Rebuild inline docs
|
|
1424
|
-
run: docscribe -
|
|
1827
|
+
run: docscribe -AkB --verbose
|
|
1828
|
+
```
|
|
1829
|
+
|
|
1830
|
+
Run static type checking with Steep (requires Ruby ≥ 3.2):
|
|
1831
|
+
|
|
1832
|
+
```yaml
|
|
1833
|
+
- name: Steep (type check)
|
|
1834
|
+
if: ${{ matrix.ruby >= 3.2 }}
|
|
1835
|
+
run: bundle exec steep check
|
|
1836
|
+
```
|
|
1837
|
+
|
|
1838
|
+
Fail the build if any generated docs still contain default placeholder text:
|
|
1839
|
+
|
|
1840
|
+
```yaml
|
|
1841
|
+
- name: Check for placeholder docs
|
|
1842
|
+
run: docscribe check_for_comments lib/
|
|
1843
|
+
```
|
|
1844
|
+
|
|
1845
|
+
For stricter validation, check for empty doc blocks:
|
|
1846
|
+
|
|
1847
|
+
```yaml
|
|
1848
|
+
- name: Check for empty doc blocks
|
|
1849
|
+
run: "! grep -rn '^ # $' lib/"
|
|
1425
1850
|
```
|
|
1426
1851
|
|
|
1427
1852
|
## Comparison to YARD's parser
|
|
@@ -1450,13 +1875,42 @@ yard doc -o docs
|
|
|
1450
1875
|
## Roadmap
|
|
1451
1876
|
|
|
1452
1877
|
- Method behavior inference from AST;
|
|
1453
|
-
-
|
|
1878
|
+
- Deeper YARD integration — parse `.c` source comments and generate docs for C-extensions;
|
|
1879
|
+
- Custom parser plugins — support non-Ruby languages (Crystal, etc.) via the plugin system;
|
|
1454
1880
|
- Effective config dump;
|
|
1455
|
-
- JSON output;
|
|
1456
1881
|
- Overload-aware signature selection;
|
|
1457
1882
|
- Manual `@!attribute` merge policy;
|
|
1458
1883
|
- Richer inference for common APIs;
|
|
1459
|
-
-
|
|
1884
|
+
- Documentation coverage report — percentage of documented methods, params, returns;
|
|
1885
|
+
- Pre-commit hook auto-install (`docscribe init --pre-commit`);
|
|
1886
|
+
- Parallel processing for large codebases.
|
|
1887
|
+
|
|
1888
|
+
## Editor Integration
|
|
1889
|
+
|
|
1890
|
+
Docscribe provides IDE plugins for a better development experience:
|
|
1891
|
+
|
|
1892
|
+
### VS Code
|
|
1893
|
+
|
|
1894
|
+
[](https://marketplace.visualstudio.com/items?itemName=unurgunite.docscribe-vscode)
|
|
1895
|
+
|
|
1896
|
+
- **Repository:** [github.com/FlorexLabs/docscribe-vscode](https://github.com/FlorexLabs/docscribe-vscode)
|
|
1897
|
+
- **Marketplace:** [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=unurgunite.docscribe-vscode)
|
|
1898
|
+
- **Features:** inline diagnostics, quick-fix, auto-check on save, status bar
|
|
1899
|
+
|
|
1900
|
+
### RubyMine
|
|
1901
|
+
|
|
1902
|
+
[](https://plugins.jetbrains.com/plugin/32349-docscribe)
|
|
1903
|
+
|
|
1904
|
+
- **Repository:** [github.com/FlorexLabs/docscribe-rubymine](https://github.com/FlorexLabs/docscribe-rubymine)
|
|
1905
|
+
- **Marketplace:** [JetBrains Marketplace](https://plugins.jetbrains.com/plugin/32349-docscribe)
|
|
1906
|
+
- **Features:** ExternalAnnotator, AnAction, IntentionAction, Settings UI
|
|
1907
|
+
|
|
1908
|
+
> [!NOTE]
|
|
1909
|
+
> Both plugins require **docscribe >= 1.5.0**.
|
|
1910
|
+
>
|
|
1911
|
+
> For optimal performance, both plugins use the `docscribe-client` thin client
|
|
1912
|
+
> to communicate with the docscribe daemon (see [server mode](#docscribe-server--persistent-daemon-mode)).
|
|
1913
|
+
> Start the daemon with `docscribe server start` for near-instant diagnostics.
|
|
1460
1914
|
|
|
1461
1915
|
## Contributing
|
|
1462
1916
|
|
|
@@ -1471,6 +1925,14 @@ bundle exec rubocop
|
|
|
1471
1925
|
- [Dev.to article](https://dev.to/unurgunite)
|
|
1472
1926
|
- [GitHub Discussions](https://github.com/unurgunite/docscribe/discussions)
|
|
1473
1927
|
|
|
1928
|
+
## Logo Attribution
|
|
1929
|
+
|
|
1930
|
+
The Docscribe logo uses the Ruby gem icon by [FlorexLabs](https://github.com/FlorexLabs).
|
|
1931
|
+
|
|
1932
|
+
<p>
|
|
1933
|
+
<img src="assets/icons/icon_128x128.png" alt="Docscribe logo">
|
|
1934
|
+
</p>
|
|
1935
|
+
|
|
1474
1936
|
## License
|
|
1475
1937
|
|
|
1476
1938
|
MIT
|