vscode_generator/snippets/snippet.rs
1use crate::prelude::*;
2use super::SnippetBuilder;
3use serde::Serialize;
4
5/// # The Snippet
6///
7/// 🧩 Represents a VS Code snippet with all its properties and functionality.
8/// This structure allows you to create and manage code snippets for VS Code
9/// with rich customization options.
10///
11/// ## Overview
12///
13/// - 🎯 Create snippets with prefix triggers and body content
14/// - 🔧 Optional description and scope settings
15/// - âš¡ Priority management for suggestion list
16/// - 📦 Special templates to simplify development
17///
18/// ## Usage
19///
20/// ```rust
21/// // Simple snippet creation
22/// let snippet = Snippet::new("fn main", vec![
23/// "fn main() {",
24/// " println!(\"Hello, World!\");",
25/// "}"
26/// ]);
27///
28/// // Using builder pattern
29/// let snippet = Snippet::builder()
30/// .set_prefix("fn test")
31/// .set_body(vec![
32/// "#[test]",
33/// "fn test_$1() {",
34/// " $0",
35/// "}"
36/// ])
37/// .set_description("Test function template")
38/// .set_scope("rust")
39/// .build()
40/// .unwrap();
41/// ```
42///
43/// ## Using Templates
44///
45/// ```rust
46/// // Text snippet
47/// let text = Snippet::text("hello", "println!(\"Hello, world!\");")
48/// .set_description("Prints 'Hello, world!'")
49/// .build()
50/// .unwrap();
51///
52/// // TODO comment
53/// let todo = Snippet::todo_comment("todo", "TODO", Some("//"))
54/// .build()
55/// .unwrap();
56///
57/// // Function alias
58/// let println = Snippet::fn_alias("pr", "println!")
59/// .build()
60/// .unwrap();
61/// ```
62///
63/// ## Rust-specific Templates
64///
65/// ```rust
66/// // Requires feature = ["rust"]
67///
68/// // Rust macro alias
69/// let macro_snippet = Snippet::rust_macro_alias("fmt", "format", None)
70/// .set_description("format! macro")
71/// .build()
72/// .unwrap();
73///
74/// // Rust attribute alias
75/// let derive = Snippet::rust_attr("der", "derive", vec!["Debug", "Clone"])
76/// .set_description("Common derive attributes")
77/// .build()
78/// .unwrap();
79/// ```
80///
81/// ## JSON Conversion
82///
83/// ```rust
84/// let snippet = Snippet::new("fn", vec!["function() {", " $0", "}"]);
85/// let json = snippet.to_json().unwrap();
86/// println!("{}", json);
87/// ```
88///
89/// ## Panics
90///
91/// The builder's `build()` method will panic if:
92///
93/// - `prefix` is empty
94/// - `name` is empty
95/// - `body` is empty
96///
97/// ## Notes
98///
99/// #### The syntax of VS Code snippets:
100///
101/// - 📌 Use `$0` to specify the final cursor position
102/// - 📌 Use `$1`, `$2`, etc. for tabstops
103/// - 📌 Use `${1:default text}` for placeholders with default values
104/// - 📌 Use `${1|one,two,three|}` for dropdown choices
105///
106///
107/// #### See Also
108///
109/// - 🔗 Structure [`SnippetFile`](super::SnippetFile) - For more flexible snippet construction
110/// - 🔗 VS Code [Snippet Guide](https://code.visualstudio.com/docs/editor/userdefinedsnippets)
111#[derive(Debug, Clone, PartialEq, Serialize)]
112pub struct Snippet {
113 /// Unique identifier for the snippet (not serialized)
114 #[serde(skip_serializing)]
115 pub name: String,
116 /// The trigger text for the snippet
117 pub prefix: String,
118 /// The actual content of the snippet
119 pub body: Vec<String>,
120 /// Optional description of what the snippet does
121 #[serde(skip_serializing_if = "Option::is_none")]
122 pub description: Option<String>,
123 /// Optional language scope (e.g., "rust")
124 #[serde(skip_serializing_if = "Option::is_none")]
125 pub scope: Option<String>,
126 /// Optional flag for file templates
127 #[serde(skip_serializing_if = "Option::is_none")]
128 pub is_file_template: Option<bool>,
129 /// Optional priority in suggestion list
130 #[serde(skip_serializing_if = "Option::is_none")]
131 pub priority: Option<u32>,
132}
133
134impl Snippet {
135 /// Creates a new snippet with required fields
136 pub fn new<S: Into<String>>(prefix: S, body: impl IntoIterator<Item = S>) -> Self {
137 SnippetBuilder::new()
138 .set_prefix(prefix)
139 .set_body(body.into_iter().map(Into::into).collect())
140 .build()
141 .unwrap()
142 }
143
144 /// Creates a new SnippetBuilder instance
145 pub fn builder() -> SnippetBuilder {
146 SnippetBuilder::new()
147 }
148
149 /// Converts the snippet to json string
150 pub fn to_json(&self) -> Result<String> {
151 serde_json::to_string_pretty(&self).map_err(Error::from)
152 }
153}
154
155impl From<SnippetBuilder> for Snippet {
156 fn from(value: SnippetBuilder) -> Self {
157 value.build().unwrap()
158 }
159}
160
161/// The standart snippet templates
162impl Snippet {
163 /// Creates a simple text snippet
164 pub fn text<S: Into<String>>(prefix: S, text: S) -> SnippetBuilder {
165 Self::builder()
166 .set_prefix(prefix)
167 .set_body(vec![text.into()])
168 }
169
170 /// Creates various comment templates (TODO, NOTE, etc.)
171 pub fn todo_comment<S: Into<String>>(prefix: S, comment_name: &str, comment_type: Option<&str>) -> SnippetBuilder {
172 let comment_type = comment_type.unwrap_or("//");
173
174 Self::builder()
175 .set_prefix(prefix)
176 .set_body(vec![format!("{comment_type} {comment_name}: ${{1:...}}")])
177 }
178
179 /// Creates a function alias template
180 pub fn fn_alias<S: Into<String>>(prefix: S, fn_name: &str) -> SnippetBuilder {
181 Self::builder()
182 .set_prefix(prefix)
183 .set_body(vec![format!("{fn_name}()")])
184 }
185}
186
187/// __BONUS__: The snippet templates for Rust programming language (use crate option `features = ["rust"]`)
188#[cfg(feature = "rust")]
189impl Snippet {
190 /// `[rust]`: Creates a simple text snippet
191 pub fn rust_text<S: Into<String>>(prefix: S, text: S) -> SnippetBuilder {
192 Self::text(prefix, text)
193 .set_scope("rust")
194 }
195
196 /// `[rust]`: Creates various comment templates (TODO, NOTE, etc.)
197 pub fn rust_todo_comment<S: Into<String>>(prefix: S, comment_name: &str, comment_type: Option<&str>) -> SnippetBuilder {
198 Self::todo_comment(prefix, comment_name, comment_type)
199 .set_scope("rust")
200 }
201
202 /// `[rust]`: Creates a function alias template
203 pub fn rust_fn_alias<S: Into<String>>(prefix: S, fn_name: &str) -> SnippetBuilder {
204 Self::fn_alias(prefix, fn_name)
205 .set_scope("rust")
206 }
207
208 /// `[rust]`: Creates a macro alias template
209 pub fn rust_macro_alias<S: Into<String>>(prefix: S, macro_name: &str, custom_braces: Option<(&str, &str)>) -> SnippetBuilder {
210 let (lpar, rpar) = custom_braces.unwrap_or(("(", ")"));
211
212 Self::builder()
213 .set_prefix(prefix)
214 .set_body(vec![format!("{}!{lpar}\"${{1:args}}\"{rpar}", macro_name)])
215 .set_scope("rust")
216 }
217
218 /// `[rust]`: Creates a macro attribute template
219 pub fn rust_attr<S: Into<String>>(prefix: S, attr_name: &str, attr_args: Vec<&str>) -> SnippetBuilder {
220 Self::builder()
221 .set_prefix(prefix)
222 .set_body(vec![format!("#[{attr_name}(${{1:{}}})]", attr_args.join("|"))])
223 .set_scope("rust")
224 }
225}