Skip to main content

spring_lsp/
completion.rs

1//! 智能补全引擎模块
2
3use lsp_types::{
4    CompletionItem, CompletionItemKind, Documentation, MarkupContent, MarkupKind, Position, Range,
5};
6
7use crate::macro_analyzer::SpringMacro;
8use crate::schema::SchemaProvider;
9use crate::toml_analyzer::{TomlAnalyzer, TomlDocument};
10
11/// 补全上下文
12///
13/// 提供补全请求的上下文信息,用于确定补全类型
14#[derive(Debug, Clone)]
15pub enum CompletionContext {
16    /// TOML 配置文件补全
17    Toml,
18    /// Rust 宏补全
19    Macro,
20    /// 未知上下文
21    Unknown,
22}
23
24/// 补全引擎
25///
26/// 提供智能补全功能,支持 TOML 配置文件和 Rust 宏的补全
27pub struct CompletionEngine {
28    /// TOML 分析器
29    toml_analyzer: TomlAnalyzer,
30}
31
32impl CompletionEngine {
33    /// 创建新的补全引擎
34    ///
35    /// # 参数
36    ///
37    /// * `schema_provider` - Schema 提供者,用于 TOML 配置补全
38    pub fn new(schema_provider: SchemaProvider) -> Self {
39        Self {
40            toml_analyzer: TomlAnalyzer::new(schema_provider),
41        }
42    }
43
44    /// 提供补全
45    ///
46    /// 根据文档类型和位置提供相应的补全项
47    ///
48    /// # 参数
49    ///
50    /// * `context` - 补全上下文,指示补全类型
51    /// * `position` - 光标位置
52    /// * `toml_doc` - TOML 文档(可选,用于 TOML 补全)
53    /// * `macro_info` - 宏信息(可选,用于宏补全)
54    ///
55    /// # 返回
56    ///
57    /// 补全项列表
58    pub fn complete(
59        &self,
60        context: CompletionContext,
61        position: Position,
62        toml_doc: Option<&TomlDocument>,
63        macro_info: Option<&SpringMacro>,
64    ) -> Vec<CompletionItem> {
65        match context {
66            CompletionContext::Toml => {
67                if let Some(doc) = toml_doc {
68                    self.complete_toml(doc, position)
69                } else {
70                    Vec::new()
71                }
72            }
73            CompletionContext::Macro => {
74                if let Some(macro_info) = macro_info {
75                    self.complete_macro(macro_info, None)
76                } else {
77                    Vec::new()
78                }
79            }
80            CompletionContext::Unknown => Vec::new(),
81        }
82    }
83
84    /// TOML 配置补全(公共方法)
85    ///
86    /// 为 TOML 配置文件提供补全,支持:
87    /// - 配置前缀补全(在 `[` 后)
88    /// - 配置项补全(在配置节内)
89    /// - 枚举值补全
90    /// - 环境变量补全(在 `${` 后)
91    ///
92    /// # 参数
93    ///
94    /// * `doc` - TOML 文档
95    /// * `position` - 光标位置
96    ///
97    /// # 返回
98    ///
99    /// 补全项列表
100    pub fn complete_toml_document(
101        &self,
102        doc: &TomlDocument,
103        position: Position,
104    ) -> Vec<CompletionItem> {
105        self.complete_toml(doc, position)
106    }
107
108    /// TOML 配置补全(内部方法)
109    ///
110    /// 为 TOML 配置文件提供补全,支持:
111    /// - 配置前缀补全(在 `[` 后)
112    /// - 配置项补全(在配置节内)
113    /// - 枚举值补全
114    /// - 环境变量补全(在 `${` 后)
115    ///
116    /// # 参数
117    ///
118    /// * `doc` - TOML 文档
119    /// * `position` - 光标位置
120    ///
121    /// # 返回
122    ///
123    /// 补全项列表
124    fn complete_toml(&self, doc: &TomlDocument, position: Position) -> Vec<CompletionItem> {
125        // 1. 检查是否在配置前缀位置([之后)
126        if self.is_prefix_position(doc, position) {
127            return self.complete_config_prefix();
128        }
129
130        // 2. 检查是否在环境变量位置(${之后)
131        if self.is_env_var_position(doc, position) {
132            return self.complete_env_var();
133        }
134
135        // 3. 检查是否在配置节内
136        if let Some(section) = self.find_section_at_position(doc, position) {
137            // 检查是否在值位置(可能需要枚举值补全)
138            if let Some(property_name) = self.find_property_at_position(section, position) {
139                if let Some(property_schema) = self
140                    .toml_analyzer
141                    .schema_provider()
142                    .get_property_schema(&section.prefix, &property_name)
143                {
144                    // 提供枚举值补全
145                    if let crate::schema::TypeInfo::String {
146                        enum_values: Some(ref values),
147                        ..
148                    } = property_schema.type_info
149                    {
150                        return self.complete_enum_values(values);
151                    }
152                }
153            }
154
155            // 提供配置项补全
156            return self.complete_config_properties(section);
157        }
158
159        Vec::new()
160    }
161
162    /// 检查是否在配置前缀位置
163    ///
164    /// 判断光标是否在 `[` 字符之后,需要补全配置前缀
165    fn is_prefix_position(&self, doc: &TomlDocument, position: Position) -> bool {
166        // 获取光标所在行的内容
167        let lines: Vec<&str> = doc.content.lines().collect();
168
169        if position.line as usize >= lines.len() {
170            return false;
171        }
172
173        let line = lines[position.line as usize];
174        let char_pos = position.character as usize;
175
176        // 光标位置必须在行内或行尾
177        if char_pos > line.len() {
178            return false;
179        }
180
181        // 检查光标前的字符
182        let before_cursor = if char_pos > 0 { &line[..char_pos] } else { "" };
183
184        // 如果光标前是 `[` 或 `[` 后跟一些字符,则认为是前缀位置
185        // 但必须确保还没有闭合括号
186        let trimmed = before_cursor.trim_start();
187
188        // 只有在输入 `[` 后且还没有完成节名输入时才提供前缀补全
189        // 如果已经有完整的节名(包含 `]`),则不是前缀位置
190        trimmed.starts_with('[') && !trimmed.contains(']') && !line.contains(']')
191    }
192
193    /// 检查是否在环境变量位置
194    ///
195    /// 判断光标是否在 `${` 之后,需要补全环境变量名
196    fn is_env_var_position(&self, _doc: &TomlDocument, _position: Position) -> bool {
197        // 简化实现:这里需要检查光标前的字符是否是 `${`
198        // 在实际实现中,应该解析文档内容来判断
199        false
200    }
201
202    /// 查找光标所在的配置节
203    ///
204    /// 根据光标位置查找对应的配置节
205    fn find_section_at_position<'a>(
206        &self,
207        doc: &'a TomlDocument,
208        position: Position,
209    ) -> Option<&'a crate::toml_analyzer::ConfigSection> {
210        doc.config_sections
211            .values()
212            .find(|&section| self.position_in_range(position, section.range))
213    }
214
215    /// 查找光标所在的属性名
216    ///
217    /// 在配置节中查找光标位置对应的属性名(用于枚举值补全)
218    fn find_property_at_position(
219        &self,
220        section: &crate::toml_analyzer::ConfigSection,
221        position: Position,
222    ) -> Option<String> {
223        for (key, property) in &section.properties {
224            // 检查位置是否在属性值范围内
225            if self.position_in_range(position, property.range) {
226                return Some(key.clone());
227            }
228        }
229        None
230    }
231
232    /// 检查位置是否在范围内
233    fn position_in_range(&self, position: Position, range: Range) -> bool {
234        if position.line < range.start.line || position.line > range.end.line {
235            return false;
236        }
237        if position.line == range.start.line && position.character < range.start.character {
238            return false;
239        }
240        if position.line == range.end.line && position.character > range.end.character {
241            return false;
242        }
243        true
244    }
245
246    /// 补全配置前缀
247    ///
248    /// 提供所有可用的配置前缀(插件名称)
249    fn complete_config_prefix(&self) -> Vec<CompletionItem> {
250        let prefixes = self.toml_analyzer.schema_provider().get_all_prefixes();
251
252        prefixes
253            .into_iter()
254            .map(|prefix: String| {
255                let plugin_schema = self
256                    .toml_analyzer
257                    .schema_provider()
258                    .get_plugin_schema(&prefix);
259                let description = plugin_schema
260                    .as_ref()
261                    .and_then(|s| s.properties.values().next())
262                    .map(|p| p.description.clone())
263                    .unwrap_or_else(|| format!("{} 插件配置", prefix));
264
265                CompletionItem {
266                    label: prefix.clone(),
267                    kind: Some(CompletionItemKind::MODULE),
268                    detail: Some(format!("[{}] 配置节", prefix)),
269                    documentation: Some(Documentation::MarkupContent(MarkupContent {
270                        kind: MarkupKind::Markdown,
271                        value: format!(
272                            "**{}** 插件配置节\n\n{}\n\n\
273                             **使用方式**:\n\
274                             ```toml\n\
275                             [{}]\n\
276                             # 配置项...\n\
277                             ```",
278                            prefix, description, prefix
279                        ),
280                    })),
281                    insert_text: Some(format!("[{}]\n", prefix)),
282                    insert_text_format: Some(lsp_types::InsertTextFormat::SNIPPET),
283                    ..Default::default()
284                }
285            })
286            .collect()
287    }
288
289    /// 补全配置项
290    ///
291    /// 在配置节内提供配置项补全,自动去重已存在的配置项
292    fn complete_config_properties(
293        &self,
294        section: &crate::toml_analyzer::ConfigSection,
295    ) -> Vec<CompletionItem> {
296        let plugin_schema = match self
297            .toml_analyzer
298            .schema_provider()
299            .get_plugin_schema(&section.prefix)
300        {
301            Some(schema) => schema,
302            None => return Vec::new(),
303        };
304
305        let mut completions = Vec::new();
306
307        for (key, property_schema) in &plugin_schema.properties {
308            // 去重:如果配置项已存在,不提供补全
309            if section.properties.contains_key(key.as_str()) {
310                continue;
311            }
312
313            let type_hint = self.type_info_to_hint(&property_schema.type_info);
314            let default_value = property_schema
315                .default
316                .as_ref()
317                .map(|v| self.value_to_string(v))
318                .unwrap_or_else(|| self.type_info_to_default(&property_schema.type_info));
319
320            let insert_text = format!("{} = {}  # {}", key, default_value, type_hint);
321
322            completions.push(CompletionItem {
323                label: key.clone(),
324                kind: Some(CompletionItemKind::PROPERTY),
325                detail: Some(format!("{} ({})", property_schema.description, type_hint)),
326                documentation: Some(Documentation::MarkupContent(MarkupContent {
327                    kind: MarkupKind::Markdown,
328                    value: format!(
329                        "**{}**\n\n{}\n\n\
330                         **类型**: {}\n\
331                         **默认值**: {}\n\
332                         {}",
333                        key,
334                        property_schema.description,
335                        type_hint,
336                        property_schema
337                            .default
338                            .as_ref()
339                            .map(|v| self.value_to_string(v))
340                            .unwrap_or_else(|| "无".to_string()),
341                        if property_schema.required {
342                            "**必需**: 是"
343                        } else {
344                            ""
345                        }
346                    ),
347                })),
348                insert_text: Some(insert_text),
349                insert_text_format: Some(lsp_types::InsertTextFormat::PLAIN_TEXT),
350                ..Default::default()
351            });
352        }
353
354        completions
355    }
356
357    /// 补全枚举值
358    ///
359    /// 为具有枚举类型的配置项提供值补全
360    fn complete_enum_values(&self, values: &[String]) -> Vec<CompletionItem> {
361        values
362            .iter()
363            .map(|value| CompletionItem {
364                label: value.clone(),
365                kind: Some(CompletionItemKind::ENUM_MEMBER),
366                detail: Some(format!("枚举值: {}", value)),
367                documentation: Some(Documentation::MarkupContent(MarkupContent {
368                    kind: MarkupKind::Markdown,
369                    value: format!("枚举值 `{}`", value),
370                })),
371                insert_text: Some(format!("\"{}\"", value)),
372                insert_text_format: Some(lsp_types::InsertTextFormat::PLAIN_TEXT),
373                ..Default::default()
374            })
375            .collect()
376    }
377
378    /// 补全环境变量
379    ///
380    /// 提供常见的环境变量名称补全
381    pub fn complete_env_var(&self) -> Vec<CompletionItem> {
382        let common_vars = vec![
383            ("HOST", "主机地址"),
384            ("PORT", "端口号"),
385            ("DATABASE_URL", "数据库连接 URL"),
386            ("REDIS_URL", "Redis 连接 URL"),
387            ("LOG_LEVEL", "日志级别"),
388            ("ENV", "运行环境"),
389            ("DEBUG", "调试模式"),
390        ];
391
392        common_vars
393            .into_iter()
394            .map(|(name, description)| CompletionItem {
395                label: name.to_string(),
396                kind: Some(CompletionItemKind::VARIABLE),
397                detail: Some(description.to_string()),
398                documentation: Some(Documentation::MarkupContent(MarkupContent {
399                    kind: MarkupKind::Markdown,
400                    value: format!(
401                        "**{}**\n\n{}\n\n\
402                         **使用方式**:\n\
403                         ```toml\n\
404                         value = \"${{{}:default}}\"\n\
405                         ```",
406                        name, description, name
407                    ),
408                })),
409                insert_text: Some(format!("{}:${{1:default}}}}", name)),
410                insert_text_format: Some(lsp_types::InsertTextFormat::SNIPPET),
411                ..Default::default()
412            })
413            .collect()
414    }
415
416    /// 将类型信息转换为类型提示字符串
417    fn type_info_to_hint(&self, type_info: &crate::schema::TypeInfo) -> String {
418        match type_info {
419            crate::schema::TypeInfo::String {
420                enum_values: Some(values),
421                ..
422            } => {
423                format!("enum: {:?}", values)
424            }
425            crate::schema::TypeInfo::String { .. } => "string".to_string(),
426            crate::schema::TypeInfo::Integer { min, max } => {
427                if let (Some(min), Some(max)) = (min, max) {
428                    format!("integer ({} - {})", min, max)
429                } else {
430                    "integer".to_string()
431                }
432            }
433            crate::schema::TypeInfo::Float { .. } => "float".to_string(),
434            crate::schema::TypeInfo::Boolean => "boolean".to_string(),
435            crate::schema::TypeInfo::Array { .. } => "array".to_string(),
436            crate::schema::TypeInfo::Object { .. } => "object".to_string(),
437        }
438    }
439
440    /// 将类型信息转换为默认值字符串
441    fn type_info_to_default(&self, type_info: &crate::schema::TypeInfo) -> String {
442        match type_info {
443            crate::schema::TypeInfo::String {
444                enum_values: Some(values),
445                ..
446            } => {
447                if let Some(first) = values.first() {
448                    format!("\"{}\"", first)
449                } else {
450                    "\"\"".to_string()
451                }
452            }
453            crate::schema::TypeInfo::String { .. } => "\"\"".to_string(),
454            crate::schema::TypeInfo::Integer { .. } => "0".to_string(),
455            crate::schema::TypeInfo::Float { .. } => "0.0".to_string(),
456            crate::schema::TypeInfo::Boolean => "false".to_string(),
457            crate::schema::TypeInfo::Array { .. } => "[]".to_string(),
458            crate::schema::TypeInfo::Object { .. } => "{}".to_string(),
459        }
460    }
461
462    /// 将 Schema 值转换为字符串
463    fn value_to_string(&self, value: &crate::schema::Value) -> String {
464        match value {
465            crate::schema::Value::String(s) => format!("\"{}\"", s),
466            crate::schema::Value::Integer(i) => i.to_string(),
467            crate::schema::Value::Float(f) => f.to_string(),
468            crate::schema::Value::Boolean(b) => b.to_string(),
469            crate::schema::Value::Array(_) => "[]".to_string(),
470            crate::schema::Value::Table(_) => "{}".to_string(),
471        }
472    }
473
474    /// 为宏参数提供补全
475    ///
476    /// 根据宏的类型提供相应的参数补全项
477    ///
478    /// # Arguments
479    ///
480    /// * `macro_info` - 宏信息
481    /// * `cursor_position` - 光标位置(用于上下文感知补全)
482    ///
483    /// # Returns
484    ///
485    /// 返回补全项列表
486    pub fn complete_macro(
487        &self,
488        macro_info: &SpringMacro,
489        _cursor_position: Option<&str>,
490    ) -> Vec<CompletionItem> {
491        match macro_info {
492            SpringMacro::DeriveService(_) => self.complete_service_macro(),
493            SpringMacro::Inject(_) => self.complete_inject_macro(),
494            SpringMacro::AutoConfig(_) => self.complete_auto_config_macro(),
495            SpringMacro::Route(_) => self.complete_route_macro(),
496            SpringMacro::Job(_) => self.complete_job_macro(),
497        }
498    }
499
500    /// 为 Service 宏提供补全
501    ///
502    /// 提供 inject 属性的参数补全
503    fn complete_service_macro(&self) -> Vec<CompletionItem> {
504        vec![
505            // inject(component) 补全
506            CompletionItem {
507                label: "inject(component)".to_string(),
508                kind: Some(CompletionItemKind::PROPERTY),
509                detail: Some("注入组件".to_string()),
510                documentation: Some(Documentation::MarkupContent(MarkupContent {
511                    kind: MarkupKind::Markdown,
512                    value: "从应用上下文中注入已注册的组件实例。\n\n\
513                            **示例**:\n\
514                            ```rust\n\
515                            #[inject(component)]\n\
516                            db: ConnectPool,\n\
517                            ```"
518                        .to_string(),
519                })),
520                insert_text: Some("inject(component)".to_string()),
521                ..Default::default()
522            },
523            // inject(component = "name") 补全
524            CompletionItem {
525                label: "inject(component = \"name\")".to_string(),
526                kind: Some(CompletionItemKind::PROPERTY),
527                detail: Some("注入指定名称的组件".to_string()),
528                documentation: Some(Documentation::MarkupContent(MarkupContent {
529                    kind: MarkupKind::Markdown,
530                    value: "使用指定名称从应用上下文中注入组件,适用于多实例场景(如多数据源)。\n\n\
531                            **示例**:\n\
532                            ```rust\n\
533                            #[inject(component = \"primary\")]\n\
534                            primary_db: ConnectPool,\n\
535                            ```"
536                        .to_string(),
537                })),
538                insert_text: Some("inject(component = \"$1\")".to_string()),
539                insert_text_format: Some(lsp_types::InsertTextFormat::SNIPPET),
540                ..Default::default()
541            },
542            // inject(config) 补全
543            CompletionItem {
544                label: "inject(config)".to_string(),
545                kind: Some(CompletionItemKind::PROPERTY),
546                detail: Some("注入配置".to_string()),
547                documentation: Some(Documentation::MarkupContent(MarkupContent {
548                    kind: MarkupKind::Markdown,
549                    value: "从配置文件中加载配置项。配置项通过 `#[config_prefix]` 指定的前缀从 `config/app.toml` 中读取。\n\n\
550                            **示例**:\n\
551                            ```rust\n\
552                            #[inject(config)]\n\
553                            config: MyConfig,\n\
554                            ```"
555                        .to_string(),
556                })),
557                insert_text: Some("inject(config)".to_string()),
558                ..Default::default()
559            },
560        ]
561    }
562
563    /// 为 Inject 宏提供补全
564    ///
565    /// 提供注入类型的补全(component, config)
566    fn complete_inject_macro(&self) -> Vec<CompletionItem> {
567        vec![
568            // component 补全
569            CompletionItem {
570                label: "component".to_string(),
571                kind: Some(CompletionItemKind::KEYWORD),
572                detail: Some("注入组件".to_string()),
573                documentation: Some(Documentation::MarkupContent(MarkupContent {
574                    kind: MarkupKind::Markdown,
575                    value: "从应用上下文中注入已注册的组件实例。\n\n\
576                            **使用方式**:\n\
577                            - `#[inject(component)]` - 按类型自动查找\n\
578                            - `#[inject(component = \"name\")]` - 按名称查找"
579                        .to_string(),
580                })),
581                insert_text: Some("component".to_string()),
582                ..Default::default()
583            },
584            // config 补全
585            CompletionItem {
586                label: "config".to_string(),
587                kind: Some(CompletionItemKind::KEYWORD),
588                detail: Some("注入配置".to_string()),
589                documentation: Some(Documentation::MarkupContent(MarkupContent {
590                    kind: MarkupKind::Markdown,
591                    value: "从配置文件中加载配置项。\n\n\
592                            **使用方式**:\n\
593                            - `#[inject(config)]` - 从 config/app.toml 加载配置"
594                        .to_string(),
595                })),
596                insert_text: Some("config".to_string()),
597                ..Default::default()
598            },
599        ]
600    }
601
602    /// 为 AutoConfig 宏提供补全
603    ///
604    /// 提供常见的配置器类型补全
605    fn complete_auto_config_macro(&self) -> Vec<CompletionItem> {
606        vec![
607            CompletionItem {
608                label: "WebConfigurator".to_string(),
609                kind: Some(CompletionItemKind::CLASS),
610                detail: Some("Web 路由配置器".to_string()),
611                documentation: Some(Documentation::MarkupContent(MarkupContent {
612                    kind: MarkupKind::Markdown,
613                    value: "自动注册 Web 路由处理器。\n\n\
614                            **示例**:\n\
615                            ```rust\n\
616                            #[auto_config(WebConfigurator)]\n\
617                            #[tokio::main]\n\
618                            async fn main() {\n\
619                                App::new().add_plugin(WebPlugin).run().await\n\
620                            }\n\
621                            ```"
622                    .to_string(),
623                })),
624                insert_text: Some("WebConfigurator".to_string()),
625                ..Default::default()
626            },
627            CompletionItem {
628                label: "JobConfigurator".to_string(),
629                kind: Some(CompletionItemKind::CLASS),
630                detail: Some("任务调度配置器".to_string()),
631                documentation: Some(Documentation::MarkupContent(MarkupContent {
632                    kind: MarkupKind::Markdown,
633                    value: "自动注册定时任务。\n\n\
634                            **示例**:\n\
635                            ```rust\n\
636                            #[auto_config(JobConfigurator)]\n\
637                            #[tokio::main]\n\
638                            async fn main() {\n\
639                                App::new().add_plugin(JobPlugin).run().await\n\
640                            }\n\
641                            ```"
642                    .to_string(),
643                })),
644                insert_text: Some("JobConfigurator".to_string()),
645                ..Default::default()
646            },
647            CompletionItem {
648                label: "StreamConfigurator".to_string(),
649                kind: Some(CompletionItemKind::CLASS),
650                detail: Some("流处理配置器".to_string()),
651                documentation: Some(Documentation::MarkupContent(MarkupContent {
652                    kind: MarkupKind::Markdown,
653                    value: "自动注册流消费者。\n\n\
654                            **示例**:\n\
655                            ```rust\n\
656                            #[auto_config(StreamConfigurator)]\n\
657                            #[tokio::main]\n\
658                            async fn main() {\n\
659                                App::new().add_plugin(StreamPlugin).run().await\n\
660                            }\n\
661                            ```"
662                    .to_string(),
663                })),
664                insert_text: Some("StreamConfigurator".to_string()),
665                ..Default::default()
666            },
667        ]
668    }
669
670    /// 为路由宏提供补全
671    ///
672    /// 提供 HTTP 方法和路径参数的补全
673    fn complete_route_macro(&self) -> Vec<CompletionItem> {
674        let mut completions = Vec::new();
675
676        // HTTP 方法补全
677        let methods = vec![
678            ("GET", "获取资源"),
679            ("POST", "创建资源"),
680            ("PUT", "更新资源(完整)"),
681            ("DELETE", "删除资源"),
682            ("PATCH", "更新资源(部分)"),
683            ("HEAD", "获取资源头信息"),
684            ("OPTIONS", "获取支持的方法"),
685        ];
686
687        for (method, description) in methods {
688            completions.push(CompletionItem {
689                label: method.to_string(),
690                kind: Some(CompletionItemKind::CONSTANT),
691                detail: Some(description.to_string()),
692                documentation: Some(Documentation::MarkupContent(MarkupContent {
693                    kind: MarkupKind::Markdown,
694                    value: format!(
695                        "HTTP {} 方法\n\n\
696                         **示例**:\n\
697                         ```rust\n\
698                         #[{}(\"/path\")]\n\
699                         async fn handler() -> impl IntoResponse {{\n\
700                         }}\n\
701                         ```",
702                        method,
703                        method.to_lowercase()
704                    ),
705                })),
706                insert_text: Some(method.to_string()),
707                ..Default::default()
708            });
709        }
710
711        // 路径参数模板补全
712        completions.push(CompletionItem {
713            label: "{id}".to_string(),
714            kind: Some(CompletionItemKind::SNIPPET),
715            detail: Some("路径参数".to_string()),
716            documentation: Some(Documentation::MarkupContent(MarkupContent {
717                kind: MarkupKind::Markdown,
718                value: "路径参数占位符,用于捕获 URL 中的动态部分。\n\n\
719                        **示例**:\n\
720                        ```rust\n\
721                        #[get(\"/users/{id}\")]\n\
722                        async fn get_user(Path(id): Path<i64>) -> impl IntoResponse {\n\
723                        }\n\
724                        ```"
725                .to_string(),
726            })),
727            insert_text: Some("{${1:id}}".to_string()),
728            insert_text_format: Some(lsp_types::InsertTextFormat::SNIPPET),
729            ..Default::default()
730        });
731
732        completions
733    }
734
735    /// 为任务调度宏提供补全
736    ///
737    /// 提供 cron 表达式、延迟和频率值的补全
738    fn complete_job_macro(&self) -> Vec<CompletionItem> {
739        vec![
740            // Cron 表达式示例
741            CompletionItem {
742                label: "0 0 * * * *".to_string(),
743                kind: Some(CompletionItemKind::SNIPPET),
744                detail: Some("每小时执行".to_string()),
745                documentation: Some(Documentation::MarkupContent(MarkupContent {
746                    kind: MarkupKind::Markdown,
747                    value: "Cron 表达式:每小时的第 0 分 0 秒执行\n\n\
748                            **格式**: 秒 分 时 日 月 星期\n\n\
749                            **示例**:\n\
750                            ```rust\n\
751                            #[cron(\"0 0 * * * *\")]\n\
752                            async fn hourly_job() {\n\
753                            }\n\
754                            ```"
755                    .to_string(),
756                })),
757                insert_text: Some("\"0 0 * * * *\"".to_string()),
758                ..Default::default()
759            },
760            CompletionItem {
761                label: "0 0 0 * * *".to_string(),
762                kind: Some(CompletionItemKind::SNIPPET),
763                detail: Some("每天午夜执行".to_string()),
764                documentation: Some(Documentation::MarkupContent(MarkupContent {
765                    kind: MarkupKind::Markdown,
766                    value: "Cron 表达式:每天午夜 00:00:00 执行\n\n\
767                            **格式**: 秒 分 时 日 月 星期\n\n\
768                            **示例**:\n\
769                            ```rust\n\
770                            #[cron(\"0 0 0 * * *\")]\n\
771                            async fn daily_job() {\n\
772                            }\n\
773                            ```"
774                    .to_string(),
775                })),
776                insert_text: Some("\"0 0 0 * * *\"".to_string()),
777                ..Default::default()
778            },
779            CompletionItem {
780                label: "0 */5 * * * *".to_string(),
781                kind: Some(CompletionItemKind::SNIPPET),
782                detail: Some("每 5 分钟执行".to_string()),
783                documentation: Some(Documentation::MarkupContent(MarkupContent {
784                    kind: MarkupKind::Markdown,
785                    value: "Cron 表达式:每 5 分钟执行一次\n\n\
786                            **格式**: 秒 分 时 日 月 星期\n\n\
787                            **示例**:\n\
788                            ```rust\n\
789                            #[cron(\"0 */5 * * * *\")]\n\
790                            async fn every_five_minutes() {\n\
791                            }\n\
792                            ```"
793                    .to_string(),
794                })),
795                insert_text: Some("\"0 */5 * * * *\"".to_string()),
796                ..Default::default()
797            },
798            // fix_delay 值示例
799            CompletionItem {
800                label: "5".to_string(),
801                kind: Some(CompletionItemKind::VALUE),
802                detail: Some("延迟 5 秒".to_string()),
803                documentation: Some(Documentation::MarkupContent(MarkupContent {
804                    kind: MarkupKind::Markdown,
805                    value: "任务完成后延迟 5 秒再次执行\n\n\
806                            **示例**:\n\
807                            ```rust\n\
808                            #[fix_delay(5)]\n\
809                            async fn delayed_job() {\n\
810                            }\n\
811                            ```"
812                    .to_string(),
813                })),
814                insert_text: Some("5".to_string()),
815                ..Default::default()
816            },
817            CompletionItem {
818                label: "10".to_string(),
819                kind: Some(CompletionItemKind::VALUE),
820                detail: Some("延迟/频率 10 秒".to_string()),
821                documentation: Some(Documentation::MarkupContent(MarkupContent {
822                    kind: MarkupKind::Markdown,
823                    value: "延迟或频率为 10 秒\n\n\
824                            **fix_delay 示例**:\n\
825                            ```rust\n\
826                            #[fix_delay(10)]\n\
827                            async fn delayed_job() {\n\
828                            }\n\
829                            ```\n\n\
830                            **fix_rate 示例**:\n\
831                            ```rust\n\
832                            #[fix_rate(10)]\n\
833                            async fn periodic_job() {\n\
834                            }\n\
835                            ```"
836                    .to_string(),
837                })),
838                insert_text: Some("10".to_string()),
839                ..Default::default()
840            },
841            CompletionItem {
842                label: "60".to_string(),
843                kind: Some(CompletionItemKind::VALUE),
844                detail: Some("延迟/频率 60 秒(1 分钟)".to_string()),
845                documentation: Some(Documentation::MarkupContent(MarkupContent {
846                    kind: MarkupKind::Markdown,
847                    value: "延迟或频率为 60 秒(1 分钟)\n\n\
848                            **fix_delay 示例**:\n\
849                            ```rust\n\
850                            #[fix_delay(60)]\n\
851                            async fn delayed_job() {\n\
852                            }\n\
853                            ```\n\n\
854                            **fix_rate 示例**:\n\
855                            ```rust\n\
856                            #[fix_rate(60)]\n\
857                            async fn periodic_job() {\n\
858                            }\n\
859                            ```"
860                    .to_string(),
861                })),
862                insert_text: Some("60".to_string()),
863                ..Default::default()
864            },
865        ]
866    }
867}
868
869impl Default for CompletionEngine {
870    fn default() -> Self {
871        Self::new(SchemaProvider::default())
872    }
873}
874
875#[cfg(test)]
876mod tests;