File size: 3,099 Bytes
d90101d | 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 | use std::path::{Path, PathBuf};
use std::sync::Arc;
use forge_select::{ForgeWidget, PreviewLayout, PreviewPlacement, SelectRow};
use forge_walker::Walker;
use crate::completer::CommandCompleter;
use crate::completer::search_term::{SearchTerm, Span};
use crate::model::ForgeCommandManager;
pub fn select_workspace_file(cwd: &Path, query: Option<String>) -> anyhow::Result<Option<String>> {
let files: Vec<String> = Walker::max_all()
.cwd(cwd.to_path_buf())
.get_blocking()
.unwrap_or_default()
.into_iter()
.map(|file| file.path)
.collect();
if files.is_empty() {
return Ok(None);
}
let has_bat = std::process::Command::new("bat")
.arg("--version")
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.status()
.is_ok();
let cat_cmd = if has_bat {
"bat --color=always --style=numbers,changes --line-range=:500"
} else {
"cat"
};
let preview_cmd = format!(
"if [ -d {{}} ]; then ls -la --color=always {{}} 2>/dev/null || ls -la {{}}; else {cat_cmd} {{}}; fi"
);
let rows: Vec<SelectRow> = files
.into_iter()
.map(|path| SelectRow {
raw: path.clone(),
display: path.clone(),
search: path.clone(),
fields: vec![path],
})
.collect();
Ok(ForgeWidget::select_rows("File ❯ ", rows)
.query(Some(query.unwrap_or_default()))
.preview(Some(preview_cmd))
.preview_layout(PreviewLayout { placement: PreviewPlacement::Bottom, percent: 75 })
.prompt()?
.map(|row| row.raw))
}
pub struct InputCompleter {
cwd: PathBuf,
command: CommandCompleter,
}
pub struct InputSuggestion {
pub value: String,
pub span: Span,
pub append_whitespace: bool,
}
impl InputCompleter {
pub fn new(cwd: PathBuf, command_manager: Arc<ForgeCommandManager>) -> Self {
Self { cwd, command: CommandCompleter::new(command_manager) }
}
pub fn complete(&mut self, line: &str, pos: usize) -> Vec<InputSuggestion> {
if line.starts_with('/') || line.starts_with(':') {
// if the line starts with '/' or ':' it's probably a command, so we delegate to
// the command completer.
let result = self.command.complete(line, pos);
if !result.is_empty() {
return result;
}
}
if let Some(query) = SearchTerm::new(line, pos).process() {
let initial_text = if !query.term.is_empty() {
Some(query.term.to_string())
} else {
None
};
if let Ok(Some(selected)) = select_workspace_file(&self.cwd, initial_text) {
let value = format!("[{}]", selected);
return vec![InputSuggestion {
value,
span: Span::new(query.span.start, query.span.end),
append_whitespace: true,
}];
}
}
vec![]
}
}
|