Skip to content

Commit 997320b

Browse files
committed
Added new extension settings for including example section and only include pub/extern examples section.
Added a expanded safety section to include more detail for the safety section. Updated Workflow file as well as the package.
1 parent 4127a35 commit 997320b

9 files changed

Lines changed: 118 additions & 42 deletions

File tree

.github/workflows/test_and_publish.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,17 @@ jobs:
4747

4848
- run: npm ci
4949

50+
- name: Move [Unreleased] to new version in CHANGELOG
51+
run: |
52+
VERSION="${GITHUB_REF##*/}"
53+
DATE=$(date +%Y-*m-%d)
54+
sed -i.bak "0,/## \[Unreleased\]/s//## [Unreleased]\n\n## [${VERSION}] - ${DATE}/" CHANGELOG.md
55+
rm CHANGELOG.md.bak
56+
git config user.name "github-actions"
57+
git config user.email "github-actions@github.com"
58+
git commit -am "chore: update CHANGELOG for ${VERSION}"
59+
git push
60+
5061
- name: Github Release
5162
uses: softprops/action-gh-release@v1
5263
with:

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ node_modules
22
.vscode-test/
33
*.vsix
44
.env
5+
releases

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@
33
## [Unreleased]
44

55
- Extension settings
6+
- Added setting `rustdocstring.includeExamples`
7+
- Added setting `rustdocstring.examplesOnlyForPublicOrExtern`
8+
- Added setting `rustdocstring.includeSafetyDetails`
69

7-
## [0.1.1] Initial release - 2025-05-03
10+
## [v0.1.1] Initial release - 2025-05-04
811

912
- Initial release with support for `fn`, `struct`, and `enum` Rust items
1013
- Context-aware comment generation

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,24 @@
5454
5555
---
5656

57+
## Configuration Options
58+
59+
The Rust Doc String extension supports several settings to control the generated documentation style. These can be set in your `settings.json` or through the VSCode Settings UI.
60+
61+
| Setting | Description | Default |
62+
|--------|-------------|---------|
63+
| `rustdocstring.includeExamples` | Include the `# Examples` section in generated doc comments. | `true` |
64+
| `rustdocstring.examplesOnlyForPublicOrExtern` | Only include examples for functions marked `pub` or `extern`. Requires `includeExamples` to be enabled. | `false` |
65+
| `rustdocstring.inludeSafetyDetails` | Include detailed safety requirements in the `# Safety` section for `unsafe` or `extern` functions. | `false` |
66+
67+
You can update these by searching for “Rust Doc String Generator” or "rustdocstring" in the VSCode Settings UI or by adding them to your `settings.json`:
68+
69+
```json
70+
"rustdocstring.includeExamples": true,
71+
"rustdocstring.examplesOnlyForPublicOrExtern": false,
72+
"rustdocstring.inludeSafetyDetails": false
73+
```
74+
5775
## How It Works
5876

5977
RustDocString uses a signature parser (`utils.js`) to scan for the next Rust item and normalize its declaration. Then, depending on the item type:

package.json

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,28 @@
5656
"Rust"
5757
]
5858
}
59-
]
59+
],
60+
"configuration": {
61+
"title": "Rust Doc String Generator",
62+
"properties": {
63+
"rustdocstring.includeExamples": {
64+
"type": "boolean",
65+
"default": true,
66+
"description": "Include the '# Examples' section in generated Rust documentation."
67+
},
68+
"rustdocstring.examplesOnlyForPublicOrExtern": {
69+
"type": "boolean",
70+
"default": false,
71+
"description": "Only include examples for functions marked 'pub' or 'extern'.",
72+
"markdownDescription": "This applies only if `includeExamples` is enabled."
73+
},
74+
"rustdocstring.includeSafetyDetails": {
75+
"type": "boolean",
76+
"default": false,
77+
"description": "Include extended detailed safety requirments in the '# Safety' section for unsafe or extern functions."
78+
}
79+
}
80+
}
6081
},
6182
"scripts": {
6283
"lint": "eslint .",

src/docgen.js

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const { generateFunctionDoc } = require('./gen_fn_doc.js');
22
const { generateStructDoc } = require('./gen_struct_doc.js');
33
const { generateEnumDoc } = require('./gen_enum_doc.js');
4+
const vscode = require('vscode');
45

56
/**
67
* Dispatcher for generating Rust doc comments based on the type of code item.
@@ -10,20 +11,25 @@ const { generateEnumDoc } = require('./gen_enum_doc.js');
1011
* @returns {string|null} - The formatted doc comment, or null if unsupported.
1112
*/
1213
function generateDocComment(line) {
14+
const config = vscode.workspace.getConfiguration('rustdocstring');
15+
const includeExamples = config.get('includeExamples', true);
16+
const examplesOnlyForPublicOrExtern = config.get('examplesOnlyForPublicOrExtern', false);
17+
const includeSafetyDetails = config.get('includeSafetyDetails', false);
18+
1319
const itemType = getRustItemType(line);
1420
if (!itemType) return null;
1521

1622
switch (itemType) {
1723
case 'function':
18-
return generateFunctionDoc(line);
24+
return generateFunctionDoc(line, includeExamples, examplesOnlyForPublicOrExtern, includeSafetyDetails);
1925
case 'struct':
20-
return generateStructDoc(line);
26+
return generateStructDoc(line, includeExamples, examplesOnlyForPublicOrExtern);
2127
case 'enum':
22-
return generateEnumDoc(line);
28+
return generateEnumDoc(line, includeExamples, examplesOnlyForPublicOrExtern);
2329
// case 'trait': // TODO
24-
// return generateTraitDoc(line);
30+
// return generateTraitDoc(line, includeExamples);
2531
// case 'union': // TODO
26-
// return generateUnionDoc(line);
32+
// return generateUnionDoc(line, includeExamples);
2733
default:
2834
return null;
2935
}

src/gen_enum_doc.js

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@
1111
* @param {string} line - The line containing the Rust `enum` declaration with its body.
1212
* @returns {string|null} A formatted doc comment block or null if parsing fails.
1313
*/
14-
function generateEnumDoc(line) {
14+
function generateEnumDoc(line, includeExamples, examplesOnlyForPublicOrExtern) {
15+
// Check if enum is public or externed.
16+
const isPublicOrExtern = /\b(pub(\s*\([^)]*\)|\s+self|\s+super)?|extern(\s*"[^"]*")?)\b/.test(line);
17+
1518
// Remove trailing comments and trim whitespace
1619
line = line.replace(/\/\/.*$/, '').trim();
1720

@@ -60,12 +63,14 @@ function generateEnumDoc(line) {
6063

6164
const defaultVariant = variants[0].split(/[({]/)[0].trim();
6265

63-
// Examples section
64-
docLines.push(``, `# Examples`, ``, '```', `use crate::\${${currentTabStop++}:...};`, ``);
65-
docLines.push(`let ${name.toLowerCase()} = ${name}::${defaultVariant};`); // Add function name.
66-
docLines.push(...exampleLines);
67-
docLines.push('}');
68-
docLines.push('```'); // End the example section markdown code block
66+
if (includeExamples && (!examplesOnlyForPublicOrExtern || isPublicOrExtern)) {
67+
// Examples section
68+
docLines.push(``, `# Examples`, ``, '```', `use crate::\${${currentTabStop++}:...};`, ``);
69+
docLines.push(`let ${name.toLowerCase()} = ${name}::${defaultVariant};`); // Add function name.
70+
docLines.push(...exampleLines);
71+
docLines.push('}');
72+
docLines.push('```'); // End the example section markdown code block
73+
}
6974

7075
// Format as Rust doc comment block
7176
return [docLines[0], ...docLines.slice(1).map(line => `/// ${line}`)].join('\n');

src/gen_fn_doc.js

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@
1515
* @param {string} line - The normalized function signature, stripped of leading comments or extra lines.
1616
* @returns {string|null} The formatted Rust doc comment block as a string, or `null` if the input is not a valid function signature.
1717
*/
18-
function generateFunctionDoc(line) {
18+
function generateFunctionDoc(line, includeExamples, examplesOnlyForPublicOrExtern, includeSafetyDetails) {
19+
// Check if function is public or externed.
20+
const isPublicOrExtern = /\b(pub(\s*\([^)]*\)|\s+self|\s+super)?|extern(\s*"[^"]*")?)\b/.test(line);
21+
1922
// Strip pub, pub(crate), pub(in ...), pub(self), pub(super)
2023
const cleanLine = line.replace(/pub(\s*\([^)]*\)|\s+self|\s+super)?\s+/, '');
2124

@@ -73,10 +76,13 @@ function generateFunctionDoc(line) {
7376
if (hasUnsafe || hasExtern) {
7477
// Check if unsafe and extern
7578
docLines.push(``, `# Safety`, ``);
76-
docLines.push(`- **The caller must ensure that:**`);
77-
docLines.push(` - Any internal state or memory accessed by this function is in a valid state.`);
78-
docLines.push(` - Preconditions specific to this function's logic are satisfied.`);
79-
docLines.push(` - This function is only called in the correct program state to avoid UB.`);
79+
80+
if (includeSafetyDetails) {
81+
docLines.push(`- **The caller must ensure that:**`);
82+
docLines.push(` - Any internal state or memory accessed by this function is in a valid state.`);
83+
docLines.push(` - Preconditions specific to this function's logic are satisfied.`);
84+
docLines.push(` - This function is only called in the correct program state to avoid UB.`);
85+
}
8086

8187
if (hasUnsafe) {
8288
// Check if also unsafe
@@ -94,12 +100,12 @@ function generateFunctionDoc(line) {
94100
docLines.push(`\${${currentTabStop++}:Describe possible errors.}`);
95101
}
96102

97-
// TODO Make editable configuration to include example sections as a checkbox in the settings.
98-
99-
// Examples section
100-
const { exampleLines, nextTabStop } = createExampleSection(name, currentTabStop, hasAsync, hasUnsafe);
101-
docLines.push(...exampleLines); // add example section
102-
currentTabStop = nextTabStop; // Update counter
103+
if (includeExamples && (!examplesOnlyForPublicOrExtern || isPublicOrExtern)) {
104+
// Examples section
105+
const { exampleLines, nextTabStop } = createExampleSection(name, currentTabStop, hasAsync, hasUnsafe);
106+
docLines.push(...exampleLines); // add example section
107+
currentTabStop = nextTabStop; // Update counter
108+
}
103109

104110
// Prefix all lines with Rust doc syntax (`///`) and return as a snippet string
105111
return [

src/gen_struct_doc.js

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@
1818
* @param {string} line - A string containing the full struct declaration, including its body.
1919
* @returns {string|null} The formatted doc comment block, or `null` if the input is not a valid documentable struct.
2020
*/
21-
function generateStructDoc(line) {
21+
function generateStructDoc(line, includeExamples, examplesOnlyForPublicOrExtern) {
22+
// Check if struct is public or externed.
23+
const isPublicOrExtern = /\b(pub(\s*\([^)]*\)|\s+self|\s+super)?|extern(\s*"[^"]*")?)\b/.test(line);
24+
2225
// Reject unit structs (single struct no fields)
2326
if (/struct\s+\w+\s*(;|\{\})/.test(line)) return null; // Struct ends with ';' or '{}'
2427

@@ -60,25 +63,27 @@ function generateStructDoc(line) {
6063
docLines.push(``, `# Fields`, ``, ...fields);
6164
}
6265

63-
// Examples section
64-
docLines.push(``, `# Examples`, ``, '```', `use crate::\${${currentTabStop++}:...};`, ``);
66+
if (includeExamples && (!examplesOnlyForPublicOrExtern || isPublicOrExtern)) {
67+
// Examples section
68+
docLines.push(``, `# Examples`, ``, '```', `use crate::\${${currentTabStop++}:...};`, ``);
6569

66-
if (body.startsWith('{')) {
67-
// Field-style struct
68-
docLines.push(`let s = ${name} {`);
69-
for (const field of fields) {
70-
const fieldName = field.match(/`([^`]+)`/)[1]; // extract field name
71-
docLines.push(` ${fieldName}: value,`);
70+
if (body.startsWith('{')) {
71+
// Field-style struct
72+
docLines.push(`let s = ${name} {`);
73+
for (const field of fields) {
74+
const fieldName = field.match(/`([^`]+)`/)[1]; // extract field name
75+
docLines.push(` ${fieldName}: value,`);
76+
}
77+
docLines.push(`};`);
78+
} else if (body.startsWith('(')) {
79+
// Tuple-style struct
80+
const types = body.slice(1, -1).split(',').map(t => t.trim()).filter(Boolean);
81+
const tupleArgs = types.map(() => `value`).join(', ');
82+
docLines.push(`let s = ${name}(${tupleArgs});`);
7283
}
73-
docLines.push(`};`);
74-
} else if (body.startsWith('(')) {
75-
// Tuple-style struct
76-
const types = body.slice(1, -1).split(',').map(t => t.trim()).filter(Boolean);
77-
const tupleArgs = types.map(() => `value`).join(', ');
78-
docLines.push(`let s = ${name}(${tupleArgs});`);
79-
}
8084

81-
docLines.push('```'); // End the example section markdown code block
85+
docLines.push('```'); // End the example section markdown code block
86+
}
8287

8388
// Format as Rust doc comment block
8489
return [docLines[0], ...docLines.slice(1).map(line => `/// ${line}`)].join('\n');

0 commit comments

Comments
 (0)