forked from aws/amazon-q-developer-cli
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinput_source.rs
More file actions
178 lines (156 loc) · 5.16 KB
/
Copy pathinput_source.rs
File metadata and controls
178 lines (156 loc) · 5.16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
use eyre::Result;
use rustyline::error::ReadlineError;
use super::prompt::{
PasteState,
PromptQueryResponseReceiver,
PromptQuerySender,
rl,
};
#[cfg(unix)]
use super::skim_integration::SkimCommandSelector;
use crate::os::Os;
#[derive(Debug)]
pub struct InputSource {
inner: inner::Inner,
paste_state: PasteState,
}
mod inner {
use rustyline::Editor;
use rustyline::history::FileHistory;
use super::super::prompt::ChatHelper;
#[allow(clippy::large_enum_variant)]
#[derive(Debug)]
pub enum Inner {
Readline(Editor<ChatHelper, FileHistory>),
#[allow(dead_code)]
Mock {
index: usize,
lines: Vec<String>,
},
}
}
impl Drop for InputSource {
fn drop(&mut self) {
self.save_history().unwrap();
}
}
impl InputSource {
pub fn new(os: &Os, sender: PromptQuerySender, receiver: PromptQueryResponseReceiver) -> Result<Self> {
let paste_state = PasteState::new();
Ok(Self {
inner: inner::Inner::Readline(rl(os, sender, receiver, paste_state.clone())?),
paste_state,
})
}
/// Save history to file
pub fn save_history(&mut self) -> Result<()> {
if let inner::Inner::Readline(rl) = &mut self.inner {
if let Some(helper) = rl.helper() {
let history_path = helper.get_history_path();
// Create directory if it doesn't exist
if let Some(parent) = history_path.parent() {
std::fs::create_dir_all(parent)?;
}
rl.append_history(&history_path)?;
}
}
Ok(())
}
#[cfg(unix)]
pub fn put_skim_command_selector(
&mut self,
os: &Os,
context_manager: std::sync::Arc<super::context::ContextManager>,
tool_names: Vec<String>,
) {
use rustyline::{
EventHandler,
KeyEvent,
};
use crate::database::settings::Setting;
if let inner::Inner::Readline(rl) = &mut self.inner {
let key_char = match os.database.settings.get_string(Setting::SkimCommandKey) {
Some(key) if key.len() == 1 => key.chars().next().unwrap_or('s'),
_ => 's', // Default to 's' if setting is missing or invalid
};
rl.bind_sequence(
KeyEvent::ctrl(key_char),
EventHandler::Conditional(Box::new(SkimCommandSelector::new(
os.clone(),
context_manager,
tool_names,
))),
);
}
}
#[allow(dead_code)]
pub fn new_mock(lines: Vec<String>) -> Self {
Self {
inner: inner::Inner::Mock { index: 0, lines },
paste_state: PasteState::new(),
}
}
pub fn read_line(&mut self, prompt: Option<&str>) -> Result<Option<String>, ReadlineError> {
match &mut self.inner {
inner::Inner::Readline(rl) => {
let prompt = prompt.unwrap_or_default();
let curr_line = rl.readline(prompt);
match curr_line {
Ok(line) => {
if Self::should_append_history(&line) {
let _ = rl.add_history_entry(line.as_str());
}
Ok(Some(line))
},
Err(ReadlineError::Interrupted | ReadlineError::Eof) => Ok(None),
Err(err) => Err(err),
}
},
inner::Inner::Mock { index, lines } => {
*index += 1;
Ok(lines.get(*index - 1).cloned())
},
}
}
fn should_append_history(line: &str) -> bool {
let trimmed = line.trim().to_lowercase();
if trimmed.is_empty() {
return false;
}
if matches!(trimmed.as_str(), "y" | "n" | "t") {
return false;
}
true
}
// We're keeping this method for potential future use
#[allow(dead_code)]
pub fn set_buffer(&mut self, content: &str) {
if let inner::Inner::Readline(rl) = &mut self.inner {
// Add to history so user can access it with up arrow
let _ = rl.add_history_entry(content);
}
}
/// Check if clipboard pastes were triggered and return all paths
pub fn take_clipboard_pastes(&mut self) -> Vec<std::path::PathBuf> {
self.paste_state.take_all()
}
/// Reset the paste counter (called after submitting a message)
pub fn reset_paste_count(&mut self) {
self.paste_state.reset_count();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mock_input_source() {
let l1 = "Hello,".to_string();
let l2 = "Line 2".to_string();
let l3 = "World!".to_string();
let mut input = InputSource::new_mock(vec![l1.clone(), l2.clone(), l3.clone()]);
assert_eq!(input.read_line(None).unwrap().unwrap(), l1);
assert_eq!(input.read_line(None).unwrap().unwrap(), l2);
assert_eq!(input.read_line(None).unwrap().unwrap(), l3);
assert!(input.read_line(None).unwrap().is_none());
}
}