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}