Skip to content

Commit 35e5c3f

Browse files
authored
Merge pull request #290 from kkoomen/feature/rust
Add support for Rust
2 parents 54ace23 + ae27c4a commit 35e5c3f

File tree

11 files changed

+288
-3
lines changed

11 files changed

+288
-3
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ Is your favorite doc standard not supported?
8383
| :white_check_mark: | C++ | [Doxygen][doxygen] |
8484
| :white_check_mark: | C | [Doxygen][doxygen], [KernelDoc][kerneldoc] |
8585
| :white_check_mark: | Bash | [Google][sh-google] |
86+
| :white_check_mark: | Rust | [RustDoc][rustdoc] |
8687

8788
# Getting started
8889

@@ -153,6 +154,7 @@ Here is the full list of available doc standards per filetype:
153154
| `g:doge_doc_standard_cpp` | `'doxygen_javadoc'` | `'doxygen_javadoc'`, `'doxygen_javadoc_no_asterisk'`, `'doxygen_javadoc_banner'`, `'doxygen_qt'`, `'doxygen_qt_no_asterisk'` |
154155
| `g:doge_doc_standard_c` | `'doxygen_javadoc'` | `'kernel_doc'`, `'doxygen_javadoc'`, `'doxygen_javadoc_no_asterisk'`, `'doxygen_javadoc_banner'`, `'doxygen_qt'`, `'doxygen_qt_no_asterisk'` |
155156
| `g:doge_doc_standard_sh` | `'google'` | `'google'` |
157+
| `g:doge_doc_standard_rs` | `'rustdoc'` | `'rustdoc'` |
156158

157159
## Options
158160

@@ -383,6 +385,7 @@ DoGe is licensed under the GPL-3.0 license.
383385
[yard]: https://www.rubydoc.info/gems/yard/file/docs/Tags.md
384386
[roxygen2]: https://github.com/klutometis/roxygen
385387
[doxygen]: http://www.doxygen.nl
388+
[rustdoc]: https://doc.rust-lang.org/rust-by-example/meta/doc.html
386389
[kerneldoc]: https://www.kernel.org/doc/html/latest/doc-guide/kernel-doc.html
387390
[sh-google]: https://google.github.io/styleguide/shell.xml#Function_Comments
388391
[demo-readme]: https://github.com/kkoomen/vim-doge/blob/master/doc/demos

ftplugin/rust.vim

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
" ==============================================================================
2+
" The Rust documentation should follow the 'rustdoc' conventions.
3+
" see https://doc.rust-lang.org/rust-by-example/meta/doc.html
4+
" ==============================================================================
5+
6+
let s:save_cpo = &cpoptions
7+
set cpoptions&vim
8+
9+
let b:doge_parser = 'rust'
10+
let b:doge_insert = 'above'
11+
12+
let b:doge_supported_doc_standards = doge#buffer#get_supported_doc_standards(['rustdoc'])
13+
let b:doge_doc_standard = doge#buffer#get_doc_standard('rs')
14+
let b:doge_patterns = doge#buffer#get_patterns()
15+
16+
" ==============================================================================
17+
"
18+
" Define the pattern types.
19+
"
20+
" ==============================================================================
21+
22+
" ------------------------------------------------------------------------------
23+
" Matches regular functions.
24+
" ------------------------------------------------------------------------------
25+
let s:function_pattern = {
26+
\ 'nodeTypes': ['function_item'],
27+
\ 'parameters': {
28+
\ 'format': '* `{name}` - !description'
29+
\ },
30+
\}
31+
32+
" ==============================================================================
33+
"
34+
" Define the doc standards.
35+
"
36+
" ==============================================================================
37+
38+
call doge#buffer#register_doc_standard('rustdoc', [
39+
\ doge#helpers#deepextend(s:function_pattern, {
40+
\ 'template': [
41+
\ '/// !description',
42+
\ '%(parameters|///)%',
43+
\ '%(parameters|/// # Arguments)%',
44+
\ '%(parameters|///)%',
45+
\ '%(parameters|/// {parameters})%',
46+
\ '%(unsafe|///)%',
47+
\ '%(unsafe|/// # Safety)%',
48+
\ '%(unsafe|///)%',
49+
\ '%(unsafe|/// !description)%',
50+
\ '%(errors|///)%',
51+
\ '%(errors|/// # Errors)%',
52+
\ '%(errors|///)%',
53+
\ '%(errors|/// !description)%',
54+
\ '///',
55+
\ '/// # Examples',
56+
\ '///',
57+
\ '/// ```',
58+
\ '/// !example',
59+
\ '/// ```',
60+
\ ],
61+
\ }),
62+
\])
63+
64+
let &cpoptions = s:save_cpo
65+
unlet s:save_cpo

ftplugin/sh.vim

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
" ==============================================================================
2-
" The R documentation should follow the 'Roxygen2' conventions.
3-
" see https://github.com/klutometis/roxygen
2+
" The Shell documentation should follow the 'Google' conventions.
3+
" see https://google.github.io/styleguide/shell.xml#Function_Comments
44
" ==============================================================================
55

66
let s:save_cpo = &cpoptions

package-lock.json

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
"tree-sitter-php": "^0.16.2",
5858
"tree-sitter-python": "^0.17.1",
5959
"tree-sitter-ruby": "^0.17.0",
60+
"tree-sitter-rust": "^0.16.0",
6061
"tree-sitter-typescript": "^0.16.3"
6162
},
6263
"devDependencies": {

src/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ export enum Language {
88
RUBY = 'ruby',
99
LUA = 'lua',
1010
JAVA = 'java',
11+
RUST = 'rust',
1112
}

src/helpers.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import Lua from 'tree-sitter-lua';
66
import PHP from 'tree-sitter-php';
77
import Python from 'tree-sitter-python';
88
import Ruby from 'tree-sitter-ruby';
9+
import Rust from 'tree-sitter-rust';
910
import TypeScript from 'tree-sitter-typescript/tsx';
1011
import { Language } from './constants';
1112
import { ValueOf } from './types';
@@ -21,6 +22,7 @@ export function loadParserPackage(language: ValueOf<Language>): any {
2122
[Language.RUBY]: Ruby,
2223
[Language.LUA]: Lua,
2324
[Language.JAVA]: Java,
25+
[Language.RUST]: Rust,
2426
};
2527

2628
if (!Object.keys(languages).includes(language as string)) {

src/parsers/index.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { LuaParserService } from './lua.service';
99
import { PhpParserService } from './php.service';
1010
import { PythonParserService } from './python.service';
1111
import { RubyParserService } from './ruby.service';
12+
import { RustParserService } from './rust.service';
1213
import { TypeScriptParserService } from './typescript.service';
1314

1415
export type ParserService =
@@ -20,7 +21,8 @@ export type ParserService =
2021
| BashParserService
2122
| RubyParserService
2223
| LuaParserService
23-
| JavaParserService;
24+
| JavaParserService
25+
| RustParserService;
2426

2527
export function getParserService(
2628
language: ValueOf<Language>,
@@ -54,6 +56,9 @@ export function getParserService(
5456
case Language.JAVA:
5557
return new JavaParserService(...args);
5658

59+
case Language.RUST:
60+
return new RustParserService(...args);
61+
5762
default:
5863
// prettier-ignore
5964
console.error(`Could not get parser service for unknown language: ${language}`);

src/parsers/rust.service.ts

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import { SyntaxNode } from 'tree-sitter';
2+
import { BaseParserService } from './base-parser.service';
3+
import { CustomParserService } from './custom-parser-service.interface';
4+
5+
enum NodeType {
6+
FUNCTION_ITEM = 'function_item',
7+
}
8+
9+
export class RustParserService
10+
extends BaseParserService
11+
implements CustomParserService {
12+
constructor(
13+
readonly rootNode: SyntaxNode,
14+
private readonly lineNumber: number,
15+
private readonly nodeTypes: string[],
16+
) {
17+
super();
18+
}
19+
20+
public traverse(node: SyntaxNode): void {
21+
if (
22+
node.startPosition.row === this.lineNumber &&
23+
this.nodeTypes.includes(node.type) &&
24+
this.done === false
25+
) {
26+
switch (node.type) {
27+
case NodeType.FUNCTION_ITEM: {
28+
this.result = {
29+
name: null,
30+
parameters: [],
31+
unsafe: false,
32+
errors: false,
33+
};
34+
this.runNodeParser(this.parseFunction, node);
35+
break;
36+
}
37+
38+
default: {
39+
console.error(`Unable to handle node type: ${node.type}`);
40+
break;
41+
}
42+
}
43+
} else if (node.childCount > 0) {
44+
node.children.forEach((childNode: SyntaxNode) => {
45+
this.traverse(childNode);
46+
});
47+
}
48+
}
49+
50+
private parseFunction(node: SyntaxNode): void {
51+
node.children.forEach((childNode: SyntaxNode) => {
52+
switch (childNode.type) {
53+
case 'identifier': {
54+
this.result.name = childNode.text;
55+
break;
56+
}
57+
58+
case 'function_modifiers': {
59+
childNode.children.forEach((n: SyntaxNode) => {
60+
if (n.type === 'unsafe') {
61+
this.result.unsafe = true;
62+
}
63+
});
64+
break;
65+
}
66+
67+
case 'generic_type': {
68+
const hasResultReturnType = childNode.children
69+
.filter(
70+
(n: SyntaxNode) =>
71+
n.type === 'type_identifier' && n.text === 'Result',
72+
)
73+
.shift();
74+
if (hasResultReturnType) {
75+
this.result.errors = true;
76+
}
77+
break;
78+
}
79+
80+
case 'parameters': {
81+
childNode.children
82+
.filter((n: SyntaxNode) => n.type === 'parameter')
83+
.forEach((child: SyntaxNode) => {
84+
this.result.parameters.push({
85+
name: child.children
86+
.filter((n: SyntaxNode) => n.type === 'identifier')
87+
.shift()?.text,
88+
});
89+
});
90+
break;
91+
}
92+
93+
default:
94+
break;
95+
}
96+
});
97+
}
98+
}

src/vim-doge.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ declare module 'tree-sitter-bash';
77
declare module 'tree-sitter-ruby';
88
declare module 'tree-sitter-lua';
99
declare module 'tree-sitter-java';
10+
declare module 'tree-sitter-rust';

test/filetypes/rust/functions.vader

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# ==============================================================================
2+
# Functions with parameters.
3+
# ==============================================================================
4+
Given rust (impl methods with parameters):
5+
struct Person {
6+
name: String,
7+
}
8+
9+
impl Person {
10+
fn new(name: &str) -> Person {
11+
Person {
12+
name: name.to_string(),
13+
}
14+
}
15+
16+
pub fn hello(&self) -> u8 {
17+
return 0;
18+
}
19+
}
20+
21+
Do (trigger doge):
22+
:6\<CR>
23+
\<C-d>
24+
:23\<CR>
25+
\<C-d>
26+
27+
Expect rust (generated comments with Arguments and Examples sections):
28+
struct Person {
29+
name: String,
30+
}
31+
32+
impl Person {
33+
/// [TODO:description]
34+
///
35+
/// # Arguments
36+
///
37+
/// * `name` - [TODO:description]
38+
///
39+
/// # Examples
40+
///
41+
/// ```
42+
/// [TODO:example]
43+
/// ```
44+
fn new(name: &str) -> Person {
45+
Person {
46+
name: name.to_string(),
47+
}
48+
}
49+
50+
/// [TODO:description]
51+
///
52+
/// # Examples
53+
///
54+
/// ```
55+
/// [TODO:example]
56+
/// ```
57+
pub fn hello(&self) -> u8 {
58+
return 0;
59+
}
60+
}
61+
62+
# ==============================================================================
63+
# Functions with errors and safety section
64+
# ==============================================================================
65+
Given rust (impl methods with Errors and Safety section):
66+
pub unsafe fn foo(foo: usize) -> Result<(), FooError> {}
67+
68+
Do (trigger doge):
69+
\<C-d>
70+
71+
Expect rust (generated comments with Arguments, Safety, Errors and Examples sections):
72+
/// [TODO:description]
73+
///
74+
/// # Arguments
75+
///
76+
/// * `foo` - [TODO:description]
77+
///
78+
/// # Safety
79+
///
80+
/// [TODO:description]
81+
///
82+
/// # Errors
83+
///
84+
/// [TODO:description]
85+
///
86+
/// # Examples
87+
///
88+
/// ```
89+
/// [TODO:example]
90+
/// ```
91+
pub unsafe fn foo(foo: usize) -> Result<(), FooError> {}

0 commit comments

Comments
 (0)