intelli_shell/process/
completion_new.rs1use std::time::Duration;
2
3use color_eyre::Result;
4use indicatif::{ProgressBar, ProgressStyle};
5use tokio_util::sync::CancellationToken;
6
7use super::{InteractiveProcess, Process, ProcessOutput};
8use crate::{
9 cli::CompletionNewProcess,
10 component::{
11 Component,
12 completion_edit::{EditCompletionComponent, EditCompletionComponentMode},
13 },
14 config::Config,
15 errors::{AppError, UserFacingError},
16 format_error, format_msg,
17 model::{SOURCE_USER, VariableCompletion},
18 service::IntelliShellService,
19 widgets::SPINNER_CHARS,
20};
21
22impl Process for CompletionNewProcess {
23 async fn execute(
24 self,
25 config: Config,
26 service: IntelliShellService,
27 cancellation_token: CancellationToken,
28 ) -> Result<ProcessOutput> {
29 let root_cmd = self.command.unwrap_or_default();
30 let suggestions_provider = self.provider.unwrap_or_default();
31 let Some(variable_name) = self.variable.filter(|v| !v.trim().is_empty()) else {
32 return Ok(ProcessOutput::fail().stderr(format_error!(
33 config.theme,
34 "{}",
35 UserFacingError::CompletionEmptyVariable
36 )));
37 };
38
39 let mut completion = VariableCompletion::new(SOURCE_USER, root_cmd, variable_name, suggestions_provider);
40
41 if self.ai {
43 let pb = ProgressBar::new_spinner();
45 pb.set_style(
46 ProgressStyle::with_template("{spinner:.blue} {wide_msg}")
47 .unwrap()
48 .tick_strings(&SPINNER_CHARS),
49 );
50 pb.enable_steady_tick(Duration::from_millis(100));
51 pb.set_message("Thinking ...");
52
53 let res = service
55 .suggest_completion(
56 &completion.root_cmd,
57 &completion.variable,
58 &completion.suggestions_provider,
59 cancellation_token,
60 )
61 .await;
62
63 pb.finish_and_clear();
65
66 match res {
68 Ok(p) if p.is_empty() => {
69 return Ok(
70 ProcessOutput::fail().stderr(format_error!(config.theme, "AI generated an empty response"))
71 );
72 }
73 Ok(provider) => {
74 completion.suggestions_provider = provider;
75 }
76 Err(AppError::UserFacing(err)) => {
77 return Ok(ProcessOutput::fail().stderr(format_error!(config.theme, "{err}")));
78 }
79 Err(AppError::Unexpected(report)) => return Err(report),
80 }
81 }
82
83 match service.create_variable_completion(completion).await {
84 Ok(c) if c.is_global() => Ok(ProcessOutput::success().stderr(format_msg!(
85 config.theme,
86 "Completion for global {} variable stored: {}",
87 config.theme.secondary.apply(&c.variable),
88 config.theme.secondary.apply(&c.suggestions_provider)
89 ))),
90 Ok(c) => Ok(ProcessOutput::success().stderr(format_msg!(
91 config.theme,
92 "Completion for {} variable within {} commands stored: {}",
93 config.theme.secondary.apply(&c.variable),
94 config.theme.secondary.apply(&c.root_cmd),
95 config.theme.secondary.apply(&c.suggestions_provider)
96 ))),
97 Err(AppError::UserFacing(err)) => Ok(ProcessOutput::fail().stderr(format_error!(config.theme, "{err}"))),
98 Err(AppError::Unexpected(report)) => Err(report),
99 }
100 }
101}
102
103impl InteractiveProcess for CompletionNewProcess {
104 fn into_component(
105 self,
106 config: Config,
107 service: IntelliShellService,
108 inline: bool,
109 cancellation_token: CancellationToken,
110 ) -> Result<Box<dyn Component>> {
111 let root_cmd = self.command.unwrap_or_default();
112 let suggestions_provider = self.provider.unwrap_or_default();
113 let variable_name = self.variable.unwrap_or_default();
114
115 let completion = VariableCompletion::new(SOURCE_USER, root_cmd, variable_name, suggestions_provider);
116
117 let component = EditCompletionComponent::new(
118 service,
119 config.theme,
120 inline,
121 completion,
122 EditCompletionComponentMode::New { ai: self.ai },
123 cancellation_token,
124 );
125 Ok(Box::new(component))
126 }
127}