use crate::irust::IRust;
use crate::utils::StringTools;
use crossterm::ClearType;
use std::env::temp_dir;
use std::io::{self, Read, Write};
use std::process::{Child, Command, Stdio};
#[derive(Debug)]
pub struct Racer {
process: Child,
main_file: String,
pub cursor: (usize, usize),
pub suggestions: Vec<String>,
suggestion_idx: usize,
pub needs_update: bool,
cmds: [String; 7],
}
impl Racer {
pub fn start() -> io::Result<Racer> {
let process = Command::new("racer")
.arg("daemon")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::null())
.spawn()?;
let main_file = temp_dir()
.join("irust/src/main.rs")
.to_str()
.unwrap()
.to_owned();
let cursor = (2, 5);
let cmds = ["show", "help", "pop", "del", "add", "reset", "load"];
Ok(Racer {
process,
main_file,
cursor,
suggestions: vec![],
suggestion_idx: 0,
needs_update: true,
cmds,
})
}
pub fn complete(&mut self) -> io::Result<()> {
let stdin = self.process.stdin.as_mut().unwrap();
let stdout = self.process.stdout.as_mut().unwrap();
writeln!(
stdin,
"complete {} {} {}",
self.cursor.0, self.cursor.1, self.main_file
)?;
// read till END
let mut raw_output = [0; 100_000];
'outer: loop {
let _ = stdout.read(&mut raw_output)?;
let mut count = 0;
for e in raw_output.iter().rev() {
if *e == b"D"[0] {
count += 1;
continue;
}
if count == 1 && *e == b"N"[0] {
count += 1;
continue;
}
if count == 2 && *e == b"E"[0] {
break 'outer;
}
count = 0;
}
}
let mut raw_output = String::from_utf8(raw_output.to_vec()).unwrap();
let mut completions = vec![];
while let Some(match_idx) = raw_output.find("H ") {
// if MATCH exists than , exists we can unwrap safly
let comman_idx = raw_output[match_idx..].find(',').unwrap() + match_idx;
completions.push(raw_output[match_idx + 2..comman_idx].to_owned());
raw_output = raw_output[comman_idx..].to_string();
}
self.suggestions = completions;
self.needs_update = false;
Ok(())
}
pub fn next_suggestion(&mut self) -> Option<&String> {
if self.suggestion_idx >= self.suggestions.len() {
self.suggestion_idx = 0
}
if self.suggestions.is_empty() {
return None;
}
let suggestion = &self.suggestions[self.suggestion_idx];
self.suggestion_idx += 1;
Some(suggestion)
}
pub fn current_suggestion(&self) -> Option<String> {
if self.suggestion_idx > 1 {
self.suggestions
.get(self.suggestion_idx - 1)
.map(ToOwned::to_owned)
} else {
self.suggestions.get(0).map(ToOwned::to_owned)
}
}
}
impl IRust {
pub fn start_racer(&mut self) {
self.racer = if self.options.enable_racer {
match Racer::start() {
Ok(r) => Some(r),
Err(e) => {
eprintln!("Error while starting racer: {}", e);
None
}
}
} else {
None
};
}
pub fn show_suggestions(&mut self) {
let racer = self.racer.take();
if let Some(mut racer) = racer {
if self.show_suggestions_inner(&mut racer).is_err() {
eprintln!("Something happened while fetching suggestions");
}
self.racer = Some(racer);
}
}
fn show_suggestions_inner(&mut self, mut racer: &mut Racer) -> std::io::Result<()> {
// return if we're not at the end of the line
if self.buffer.len() != self.internal_cursor.x {
return Ok(())
}
// dont autocomlete shell commands
if self.buffer.starts_with("::") {
return Ok(())
}
// Autocomlete irust commands
if self.buffer.starts_with(':') {
self.write_sggestion(self.next_irust_cmd_suggestion());
return Ok(())
}
let mut tmp_repl = self.repl.clone();
let y_pos = tmp_repl.body.len();
tmp_repl.insert(self.buffer.clone());
tmp_repl.write()?;
if racer.needs_update {
racer.cursor.0 = y_pos;
racer.cursor.1 = self.buffer.len() + 1;
self.update_racer()?;
}
if let Some(suggestion) = racer.next_suggestion() {
self.write_sggestion(suggestion);
}
Ok(())
}
pub fn racer_needs_update(&mut self, value: bool) {
let racer = self.racer.take();
if let Some(mut racer) = racer {
racer.needs_update = value;
self.racer = Some(racer);
}
}
pub fn update_racer(&mut self) -> std::io::Result<()> {
if self.buffer.starts_with(':') {
self.racer.suggestions = self.racer.cmds.iter().filter(|c| c.contains(self.buffer[1..])).collect();
} else {
self.racer.complete()?;
}
Ok(())
}
pub fn write_sggestion(&mut self, mut suggestion: String) {
self.color.set_fg(self.options.racer_color)?;
self.cursor.save_position()?;
self.terminal.clear(ClearType::UntilNewLine)?;
StringTools::strings_unique(&self.buffer, &mut suggestion);
self.terminal.write(suggestion)?;
self.cursor.reset_position()?;
self.color.reset()?;
}
}