v1.0.0: tab path autocomplete, malvim features, terminal history
various fixes, docs, some kanji and romaji font chars
This commit is contained in:
@@ -17,7 +17,7 @@ use ming_wm_lib::window_manager_types::{ DrawInstructions, WindowLike, WindowLik
|
||||
use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse };
|
||||
use ming_wm_lib::framebuffer_types::Dimensions;
|
||||
use ming_wm_lib::themes::ThemeInfo;
|
||||
use ming_wm_lib::utils::{ concat_paths, format_seconds, Substring };
|
||||
use ming_wm_lib::utils::{ concat_paths, path_autocomplete, format_seconds, Substring };
|
||||
use ming_wm_lib::dirs::home;
|
||||
use ming_wm_lib::ipc::listen;
|
||||
use ming_wm::fs::get_all_files;
|
||||
@@ -98,6 +98,18 @@ impl WindowLike for AudioPlayer {
|
||||
if self.command.len() > 0 {
|
||||
self.command = self.command.remove_last();
|
||||
}
|
||||
} else if key_press.key == '\t' { //tab
|
||||
let mut parts = self.command.split(" ");
|
||||
let parts_len = parts.clone().count();
|
||||
if parts_len == 2 {
|
||||
if let Some(add) = path_autocomplete(&self.base_directory, parts.nth(1).unwrap()) {
|
||||
self.command += &add;
|
||||
} else {
|
||||
return WindowMessageResponse::DoNothing;
|
||||
}
|
||||
} else {
|
||||
return WindowMessageResponse::DoNothing;
|
||||
}
|
||||
} else {
|
||||
self.command += &key_press.key.to_string();
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ use ming_wm_lib::framebuffer_types::Dimensions;
|
||||
use ming_wm_lib::window_manager_types::{ DrawInstructions, WindowLike, WindowLikeType };
|
||||
use ming_wm_lib::utils::{ calc_actual_lines, calc_new_cursor_pos, Substring };
|
||||
use ming_wm_lib::dirs::home;
|
||||
use ming_wm_lib::utils::path_autocomplete;
|
||||
use ming_wm_lib::ipc::listen;
|
||||
|
||||
const MONO_WIDTH: u8 = 10;
|
||||
@@ -175,11 +176,17 @@ impl WindowLike for Malvim {
|
||||
let new_length = current_file.content[current_file.line_pos].chars().count();
|
||||
current_file.cursor_pos = calc_new_cursor_pos(current_file.cursor_pos, new_length);
|
||||
} else if key_press.key == 'w' {
|
||||
//todo: currently doesn't work on a single space?
|
||||
let line = ¤t_file.content[current_file.line_pos];
|
||||
if line.len() > 0 {
|
||||
let line_len = line.chars().count();
|
||||
if line_len > 0 && current_file.cursor_pos < line_len {
|
||||
//offset until space or eol
|
||||
let offset = line.chars().skip(current_file.cursor_pos).position(|c| c == ' ').unwrap_or(line.chars().count() - current_file.cursor_pos);
|
||||
let mut line_chars = line.chars().skip(current_file.cursor_pos).peekable();
|
||||
let current_char = line_chars.peek().unwrap().clone();
|
||||
let offset = line_chars.position(|c| if current_char == ' ' {
|
||||
c != ' '
|
||||
} else {
|
||||
c == ' '
|
||||
}).unwrap_or(line_len - current_file.cursor_pos);
|
||||
current_file.content[current_file.line_pos] = line.remove(current_file.cursor_pos, offset);
|
||||
let new_length = current_file.content[current_file.line_pos].chars().count();
|
||||
current_file.cursor_pos = calc_new_cursor_pos(current_file.cursor_pos, new_length);
|
||||
@@ -235,23 +242,23 @@ impl WindowLike for Malvim {
|
||||
}
|
||||
}
|
||||
} else if key_press.key == 'h' {
|
||||
current_file.cursor_pos = current_file.cursor_pos.checked_sub(1).unwrap_or(0);
|
||||
current_file.cursor_pos = current_file.cursor_pos.checked_sub(self.maybe_num.unwrap_or(1)).unwrap_or(0);
|
||||
changed = false;
|
||||
} else if key_press.key == 'j' || key_press.key == 'k' {
|
||||
if key_press.key == 'j' {
|
||||
current_file.line_pos += 1;
|
||||
if current_file.line_pos == current_file.content.len() {
|
||||
current_file.line_pos += self.maybe_num.unwrap_or(1);
|
||||
if current_file.line_pos >= current_file.content.len() {
|
||||
current_file.line_pos = current_file.content.len() - 1;
|
||||
}
|
||||
} else {
|
||||
current_file.line_pos = current_file.line_pos.checked_sub(1).unwrap_or(0);
|
||||
current_file.line_pos = current_file.line_pos.checked_sub(self.maybe_num.unwrap_or(1)).unwrap_or(0);
|
||||
}
|
||||
let new_length = current_file.content[current_file.line_pos].chars().count();
|
||||
current_file.cursor_pos = calc_new_cursor_pos(current_file.cursor_pos, new_length);
|
||||
changed = false;
|
||||
} else if key_press.key == 'l' {
|
||||
if current_length > 0 {
|
||||
current_file.cursor_pos += 1;
|
||||
current_file.cursor_pos += self.maybe_num.unwrap_or(1);
|
||||
let line_len = current_file.content[current_file.line_pos].chars().count();
|
||||
if current_file.cursor_pos > line_len {
|
||||
current_file.cursor_pos = line_len;
|
||||
@@ -295,10 +302,10 @@ impl WindowLike for Malvim {
|
||||
} else {
|
||||
changed = false;
|
||||
}
|
||||
//reset maybe_num if not num
|
||||
if !numbered && self.state != State::Maybeg {
|
||||
self.maybe_num = None;
|
||||
}
|
||||
//
|
||||
} else if self.mode == Mode::Command {
|
||||
self.bottom_message = None;
|
||||
let command = self.command.clone().unwrap_or("".to_string());
|
||||
@@ -306,6 +313,23 @@ impl WindowLike for Malvim {
|
||||
new = self.process_command();
|
||||
self.command = None;
|
||||
self.mode = Mode::Normal;
|
||||
} else if key_press.key == '\t' { //tab
|
||||
let mut parts = command.split(" ").skip(1);
|
||||
let parts_len = parts.clone().count();
|
||||
if parts_len == 1 { //caused one skipped
|
||||
if let Some(second) = parts.next() {
|
||||
let base_path = if self.files.len() > 0 {
|
||||
//this is a file path, not a directory,
|
||||
//but path_autocomplete's concat_path will sort it out for us
|
||||
&self.files[self.current_file_index].path
|
||||
} else {
|
||||
&home().unwrap_or(PathBuf::from("/")).to_string_lossy().to_string()
|
||||
};
|
||||
if let Some(add) = path_autocomplete(&base_path, second) {
|
||||
self.command = Some(command + &add);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if key_press.key == '𐘁' { //backspace
|
||||
if command.len() > 0 {
|
||||
self.command = Some(command[..command.len() - 1].to_string());
|
||||
@@ -388,7 +412,7 @@ impl WindowLike for Malvim {
|
||||
theme_info.alt_secondary
|
||||
};
|
||||
instructions.extend(vec![
|
||||
DrawInstructions::Rect([used_width, 2], [future_used_width, BAND_HEIGHT - 2], background),
|
||||
DrawInstructions::Rect([used_width, 2], [future_used_width - used_width, BAND_HEIGHT - 2], background),
|
||||
DrawInstructions::Text([used_width + 2, 2], vec!["nimbus-romono".to_string()], if file_info.changed { "+ ".to_string() } else { String::new() } + &file_info.name, theme_info.alt_text, background, Some(0), Some(MONO_WIDTH)),
|
||||
]);
|
||||
used_width = future_used_width;
|
||||
@@ -550,12 +574,12 @@ impl Malvim {
|
||||
}
|
||||
} else {
|
||||
//t(abe)
|
||||
self.current_file_index += 1;
|
||||
if self.current_file_index == self.files.len() - 1 {
|
||||
self.files.push(file_info);
|
||||
} else {
|
||||
self.files.insert(self.current_file_index, file_info);
|
||||
self.files.insert(self.current_file_index + 1, file_info);
|
||||
}
|
||||
self.current_file_index += 1;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
@@ -577,15 +601,18 @@ impl Malvim {
|
||||
current_file.cursor_pos = 0;
|
||||
}
|
||||
}
|
||||
} else if first == "w" || first == "write" {
|
||||
let current_file = &self.files[self.current_file_index];
|
||||
let _ = write(¤t_file.path, ¤t_file.content.join("\n"));
|
||||
self.files[self.current_file_index].changed = false;
|
||||
self.bottom_message = Some("Written".to_string());
|
||||
} else if first == "q" || first == "quit" {
|
||||
self.files.remove(self.current_file_index);
|
||||
self.current_file_index = self.current_file_index.checked_sub(1).unwrap_or(0);
|
||||
return true;
|
||||
} else if first == "x" || first == "w" || first == "write" || first == "q" || first == "quit" {
|
||||
if first == "x" || first == "w" || first == "write" {
|
||||
let current_file = &self.files[self.current_file_index];
|
||||
let _ = write(¤t_file.path, ¤t_file.content.join("\n"));
|
||||
self.files[self.current_file_index].changed = false;
|
||||
self.bottom_message = Some("Written".to_string());
|
||||
}
|
||||
if first == "x" || first == "q" || first == "quit" {
|
||||
self.files.remove(self.current_file_index);
|
||||
self.current_file_index = self.current_file_index.checked_sub(1).unwrap_or(0);
|
||||
return true;
|
||||
}
|
||||
} else if first == "p" || first == "tabp" {
|
||||
self.current_file_index = self.current_file_index.checked_sub(1).unwrap_or(self.files.len() - 1);
|
||||
return true;
|
||||
|
||||
@@ -7,7 +7,6 @@ use std::io::{ Read, Write };
|
||||
use std::time::Duration;
|
||||
use std::path::PathBuf;
|
||||
use std::fmt;
|
||||
use std::fs::read_dir;
|
||||
|
||||
use pty_process::blocking;
|
||||
|
||||
@@ -15,12 +14,10 @@ use ming_wm_lib::window_manager_types::{ DrawInstructions, WindowLike, WindowLik
|
||||
use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse, ShortcutType };
|
||||
use ming_wm_lib::framebuffer_types::Dimensions;
|
||||
use ming_wm_lib::themes::ThemeInfo;
|
||||
use ming_wm_lib::utils::{ concat_paths, Substring };
|
||||
use ming_wm_lib::utils::{ concat_paths, path_autocomplete, Substring };
|
||||
use ming_wm_lib::dirs::home;
|
||||
use ming_wm_lib::ipc::listen;
|
||||
|
||||
//todo: support copy and paste
|
||||
|
||||
const MONO_WIDTH: u8 = 10;
|
||||
const LINE_HEIGHT: usize = 15;
|
||||
const PADDING: usize = 4;
|
||||
@@ -83,7 +80,8 @@ pub struct Terminal {
|
||||
process_current_line: Vec<u8>, //bytes of line
|
||||
pty_outerr_rx: Option<Receiver<u8>>,
|
||||
pty_in_tx: Option<Sender<String>>,
|
||||
last_command: Option<String>,
|
||||
history: Vec<String>,
|
||||
history_index: Option<usize>,
|
||||
}
|
||||
|
||||
//for some reason key presses, then moving the window leaves the old window still there, behind it. weird
|
||||
@@ -114,39 +112,21 @@ impl WindowLike for Terminal {
|
||||
}
|
||||
} else if key_press.key == '𐘂' { //the enter key
|
||||
self.lines.push("$ ".to_string() + &self.current_input);
|
||||
self.last_command = Some(self.current_input.clone());
|
||||
self.history.push(self.current_input.clone());
|
||||
self.history_index = None;
|
||||
self.mode = self.process_command();
|
||||
self.current_input = String::new();
|
||||
} else if key_press.key == '\t' { //tab
|
||||
//autocomplete assuming it's a file system path
|
||||
//...mostly working
|
||||
let mut useless_tab = true;
|
||||
if self.current_input.len() > 0 {
|
||||
let partial_path = self.current_input.split(" ").last().unwrap();
|
||||
if let Ok(new_path) = concat_paths(&self.current_path, partial_path) {
|
||||
let partial_name;
|
||||
let parent;
|
||||
if self.current_input.ends_with("/") {
|
||||
partial_name = "".to_string();
|
||||
parent = new_path.as_path();
|
||||
} else {
|
||||
//this is just silly
|
||||
partial_name = new_path.clone().file_name().unwrap().to_os_string().to_string_lossy().to_string();
|
||||
parent = new_path.parent().unwrap();
|
||||
};
|
||||
for entry in read_dir(parent).unwrap() {
|
||||
let name = entry.unwrap().path().file_name().unwrap().to_os_string().to_string_lossy().to_string();
|
||||
if name.starts_with(&partial_name) {
|
||||
self.current_input += &name[partial_name.len()..];
|
||||
useless_tab = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if let Some(add) = path_autocomplete(&self.current_path, partial_path) {
|
||||
self.current_input += &add;
|
||||
} else {
|
||||
return WindowMessageResponse::DoNothing;
|
||||
}
|
||||
}
|
||||
if useless_tab {
|
||||
return WindowMessageResponse::DoNothing;
|
||||
}
|
||||
} else {
|
||||
self.current_input += &key_press.key.to_string();
|
||||
}
|
||||
@@ -229,12 +209,25 @@ impl WindowLike for Terminal {
|
||||
WindowMessageResponse::JustRedraw
|
||||
} else if self.mode == Mode::Input && (key_press.key == 'p' || key_press.key == 'n') {
|
||||
//only the last command is saved unlike other terminals. good enough for me
|
||||
if key_press.key == 'p' && self.last_command.is_some() {
|
||||
self.current_input = self.last_command.clone().unwrap();
|
||||
if key_press.key == 'p' && self.history.len() > 0 {
|
||||
if let Some(history_index) = self.history_index {
|
||||
if history_index > 0 {
|
||||
self.history_index = Some(history_index - 1);
|
||||
}
|
||||
} else {
|
||||
self.history_index = Some(self.history.len() - 1);
|
||||
}
|
||||
self.current_input = self.history[self.history_index.unwrap()].clone();
|
||||
self.calc_actual_lines();
|
||||
WindowMessageResponse::JustRedraw
|
||||
} else if key_press.key == 'n' {
|
||||
self.current_input = String::new();
|
||||
if self.history_index.is_none() || self.history_index.unwrap() == self.history.len() - 1 {
|
||||
self.history_index = None;
|
||||
self.current_input = String::new();
|
||||
} else {
|
||||
self.history_index = Some(self.history_index.unwrap() + 1);
|
||||
self.current_input = self.history[self.history_index.unwrap()].clone();
|
||||
}
|
||||
self.calc_actual_lines();
|
||||
WindowMessageResponse::JustRedraw
|
||||
} else {
|
||||
|
||||
@@ -23,7 +23,12 @@ impl WindowLike for Help {
|
||||
match message {
|
||||
WindowMessage::Init(dimensions) => {
|
||||
self.dimensions = dimensions;
|
||||
self.paragraph = Some(Box::new(Paragraph::new("help".to_string(), [2, 22], [self.dimensions[0] - 4, self.dimensions[1] - 24], "Press the 'h' and 'l' keys to read the different help pages".to_string(), ())));
|
||||
let first_content = if self.files.len() > 0 {
|
||||
read_to_string(self.files[0].clone()).unwrap()
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
self.paragraph = Some(Box::new(Paragraph::new("help".to_string(), [2, 22], [self.dimensions[0] - 4, self.dimensions[1] - 24], "Press the 'h' and 'l' keys to read the different help pages".to_string() + &first_content, ())));
|
||||
WindowMessageResponse::JustRedraw
|
||||
},
|
||||
WindowMessage::KeyPress(key_press) => {
|
||||
@@ -97,4 +102,3 @@ impl Help {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,8 +30,8 @@ pub fn get_font_char_from_fonts(fonts: &[String], c: char) -> (char, Vec<Vec<u8>
|
||||
}
|
||||
}
|
||||
let p = dirs::exe_dir(Some(&("ming_bmps/".to_string() + &fonts[0]))).to_string_lossy().to_string();
|
||||
//so a ? char must be in every font
|
||||
get_font_char(&p, '?').unwrap()
|
||||
//so a ? char should be in every font. otherwise will just return blank
|
||||
get_font_char(&p, '?').unwrap_or(('?', vec![vec![0]], 0))
|
||||
}
|
||||
|
||||
pub fn get_all_files(dir: PathBuf) -> Vec<PathBuf> {
|
||||
|
||||
Reference in New Issue
Block a user