Merge branch 'master' into populate-config

This commit is contained in:
Clement Tsang 2020-02-29 23:16:15 -05:00 committed by GitHub
commit 735038f060
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 233 additions and 131 deletions

View file

@ -14,6 +14,7 @@ _Remove the irrelevant one:_
- [x] Bug fix (non-breaking change which fixes an issue) - [x] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality) - [x] New feature (non-breaking change which adds functionality)
- [x] Other (something else)
## Test methodology ## Test methodology
@ -24,10 +25,11 @@ _Please state how this was tested:_
_Please ensure all are ticked (and actually done):_ _Please ensure all are ticked (and actually done):_
- [ ] Change has been tested to work - [ ] Change has been tested to work
- [ ] Code has been linted using rustfmt - [ ] Areas your change affects has been linted using rustfmt
- [ ] Code has been self-reviewed - [ ] Code has been self-reviewed
- [ ] Code has been tested and no new breakage is introduced - [ ] Code has been tested and no new breakage is introduced
- [ ] Documentation has been added/updated if needed - [ ] Documentation has been added/updated if needed
- [ ] No merge conflicts arise from the change
## Other information ## Other information

1
.gitignore vendored
View file

@ -15,5 +15,6 @@ Cargo.lock
rust-unmangle rust-unmangle
*.svg *.svg
*.data *.data
.idea/
sample_configs/testing.toml sample_configs/testing.toml

View file

@ -30,7 +30,6 @@ heim = "0.0.10"
log = "0.4.8" log = "0.4.8"
regex = "1.3.4" regex = "1.3.4"
sysinfo = "0.11" sysinfo = "0.11"
winapi = "0.3.8"
crossterm = "0.16" crossterm = "0.16"
tui = {version = "0.8", features = ["crossterm"], default-features = false } tui = {version = "0.8", features = ["crossterm"], default-features = false }
lazy_static = "1.4.0" lazy_static = "1.4.0"
@ -40,6 +39,9 @@ serde = {version = "1.0", features = ["derive"] }
unicode-segmentation = "1.6.0" unicode-segmentation = "1.6.0"
unicode-width = "0.1.7" unicode-width = "0.1.7"
[target.'cfg(windows)'.dependencies]
winapi = "0.3.8"
[dev-dependencies] [dev-dependencies]
assert_cmd = "0.12" assert_cmd = "0.12"
predicates = "1" predicates = "1"

View file

@ -206,7 +206,7 @@ Note that `q` is disabled while in the search widget.
## Contribution ## Contribution
Contribution is welcome! Just submit a PR. Contribution is welcome! Just submit a PR. Note that I develop and test on stable Rust.
If you spot any issue with nobody assigned to it, or it seems like no work has started on it, feel free to try and do it! If you spot any issue with nobody assigned to it, or it seems like no work has started on it, feel free to try and do it!

View file

@ -3,12 +3,7 @@ max_width = 100
newline_style = "Unix" newline_style = "Unix"
reorder_imports = true reorder_imports = true
fn_args_layout = "Compressed" fn_args_layout = "Compressed"
empty_item_single_line = false
hard_tabs = true hard_tabs = true
merge_imports = true
merge_derives = true merge_derives = true
reorder_modules = true reorder_modules = true
reorder_impl_items = true
tab_spaces = 4 tab_spaces = 4
format_strings = true
space_after_colon = true

View file

@ -1,15 +1,16 @@
pub mod data_harvester;
use data_harvester::{processes, temperature};
use std::time::Instant; use std::time::Instant;
pub mod data_farmer; use unicode_segmentation::GraphemeCursor;
use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
use data_farmer::*; use data_farmer::*;
use data_harvester::{processes, temperature};
use crate::{canvas, constants, utils::error::Result}; use crate::{canvas, constants, utils::error::Result};
mod process_killer;
use unicode_segmentation::{GraphemeCursor}; pub mod data_farmer;
use unicode_width::UnicodeWidthStr; pub mod data_harvester;
mod process_killer;
const MAX_SEARCH_LENGTH: usize = 200; const MAX_SEARCH_LENGTH: usize = 200;
@ -33,7 +34,7 @@ pub enum ScrollDirection {
} }
#[derive(Debug)] #[derive(Debug)]
pub enum SearchDirection { pub enum CursorDirection {
LEFT, LEFT,
RIGHT, RIGHT,
} }
@ -73,6 +74,10 @@ pub struct AppSearchState {
pub is_blank_search: bool, pub is_blank_search: bool,
pub is_invalid_search: bool, pub is_invalid_search: bool,
pub grapheme_cursor: GraphemeCursor, pub grapheme_cursor: GraphemeCursor,
pub cursor_direction: CursorDirection,
pub cursor_bar: usize,
/// This represents the position in terms of CHARACTERS, not graphemes
pub char_cursor_position: usize,
} }
impl Default for AppSearchState { impl Default for AppSearchState {
@ -84,11 +89,21 @@ impl Default for AppSearchState {
is_invalid_search: false, is_invalid_search: false,
is_blank_search: true, is_blank_search: true,
grapheme_cursor: GraphemeCursor::new(0, 0, true), grapheme_cursor: GraphemeCursor::new(0, 0, true),
cursor_direction: CursorDirection::RIGHT,
cursor_bar: 0,
char_cursor_position: 0,
} }
} }
} }
impl AppSearchState { impl AppSearchState {
/// Returns a reset but still enabled app search state
pub fn reset() -> Self {
let mut app_search_state = AppSearchState::default();
app_search_state.is_enabled = true;
app_search_state
}
pub fn is_invalid_or_blank_search(&self) -> bool { pub fn is_invalid_or_blank_search(&self) -> bool {
self.is_blank_search || self.is_invalid_search self.is_blank_search || self.is_invalid_search
} }
@ -549,6 +564,10 @@ impl App {
.cur_cursor() .cur_cursor()
} }
pub fn get_char_cursor_position(&self) -> usize {
self.process_search_state.search_state.char_cursor_position
}
/// One of two functions allowed to run while in a dialog... /// One of two functions allowed to run while in a dialog...
pub fn on_enter(&mut self) { pub fn on_enter(&mut self) {
if self.delete_dialog_state.is_showing_dd { if self.delete_dialog_state.is_showing_dd {
@ -620,19 +639,14 @@ impl App {
#[allow(unused_variables)] #[allow(unused_variables)]
pub fn skip_word_backspace(&mut self) { pub fn skip_word_backspace(&mut self) {
if let WidgetPosition::ProcessSearch = self.current_widget_selected { if let WidgetPosition::ProcessSearch = self.current_widget_selected {
if self.process_search_state.search_state.is_enabled { if self.process_search_state.search_state.is_enabled {}
}
} }
} }
pub fn clear_search(&mut self) { pub fn clear_search(&mut self) {
if let WidgetPosition::ProcessSearch = self.current_widget_selected { if let WidgetPosition::ProcessSearch = self.current_widget_selected {
self.process_search_state.search_state.grapheme_cursor =
GraphemeCursor::new(0, 0, true);
self.process_search_state.search_state.current_search_query = String::default();
self.process_search_state.search_state.is_blank_search = true;
self.process_search_state.search_state.is_invalid_search = false;
self.update_process_gui = true; self.update_process_gui = true;
self.process_search_state.search_state = AppSearchState::reset();
} }
} }
@ -663,7 +677,8 @@ impl App {
if self.process_search_state.search_state.is_enabled && self.get_cursor_position() > 0 { if self.process_search_state.search_state.is_enabled && self.get_cursor_position() > 0 {
self.search_walk_back(self.get_cursor_position()); self.search_walk_back(self.get_cursor_position());
self.process_search_state let removed_char = self
.process_search_state
.search_state .search_state
.current_search_query .current_search_query
.remove(self.get_cursor_position()); .remove(self.get_cursor_position());
@ -677,6 +692,10 @@ impl App {
true, true,
); );
self.process_search_state.search_state.char_cursor_position -=
UnicodeWidthChar::width(removed_char).unwrap_or(0);
self.process_search_state.search_state.cursor_direction = CursorDirection::LEFT;
self.update_regex(); self.update_regex();
self.update_process_gui = true; self.update_process_gui = true;
} }
@ -710,7 +729,15 @@ impl App {
pub fn on_left_key(&mut self) { pub fn on_left_key(&mut self) {
if !self.is_in_dialog() { if !self.is_in_dialog() {
if let WidgetPosition::ProcessSearch = self.current_widget_selected { if let WidgetPosition::ProcessSearch = self.current_widget_selected {
let prev_cursor = self.get_cursor_position();
self.search_walk_back(self.get_cursor_position()); self.search_walk_back(self.get_cursor_position());
if self.get_cursor_position() < prev_cursor {
let str_slice = &self.process_search_state.search_state.current_search_query
[self.get_cursor_position()..prev_cursor];
self.process_search_state.search_state.char_cursor_position -=
UnicodeWidthStr::width(str_slice);
self.process_search_state.search_state.cursor_direction = CursorDirection::LEFT;
}
} }
} else if self.delete_dialog_state.is_showing_dd && !self.delete_dialog_state.is_on_yes { } else if self.delete_dialog_state.is_showing_dd && !self.delete_dialog_state.is_on_yes {
self.delete_dialog_state.is_on_yes = true; self.delete_dialog_state.is_on_yes = true;
@ -720,7 +747,16 @@ impl App {
pub fn on_right_key(&mut self) { pub fn on_right_key(&mut self) {
if !self.is_in_dialog() { if !self.is_in_dialog() {
if let WidgetPosition::ProcessSearch = self.current_widget_selected { if let WidgetPosition::ProcessSearch = self.current_widget_selected {
let prev_cursor = self.get_cursor_position();
self.search_walk_forward(self.get_cursor_position()); self.search_walk_forward(self.get_cursor_position());
if self.get_cursor_position() > prev_cursor {
let str_slice = &self.process_search_state.search_state.current_search_query
[prev_cursor..self.get_cursor_position()];
self.process_search_state.search_state.char_cursor_position +=
UnicodeWidthStr::width(str_slice);
self.process_search_state.search_state.cursor_direction =
CursorDirection::RIGHT;
}
} }
} else if self.delete_dialog_state.is_showing_dd && self.delete_dialog_state.is_on_yes { } else if self.delete_dialog_state.is_showing_dd && self.delete_dialog_state.is_on_yes {
self.delete_dialog_state.is_on_yes = false; self.delete_dialog_state.is_on_yes = false;
@ -738,6 +774,8 @@ impl App {
.len(), .len(),
true, true,
); );
self.process_search_state.search_state.char_cursor_position = 0;
self.process_search_state.search_state.cursor_direction = CursorDirection::LEFT;
} }
} }
} }
@ -756,6 +794,14 @@ impl App {
.len(), .len(),
true, true,
); );
self.process_search_state.search_state.char_cursor_position =
UnicodeWidthStr::width(
self.process_search_state
.search_state
.current_search_query
.as_str(),
);
self.process_search_state.search_state.cursor_direction = CursorDirection::RIGHT;
} }
} }
} }
@ -840,6 +886,10 @@ impl App {
true, true,
); );
self.search_walk_forward(self.get_cursor_position()); self.search_walk_forward(self.get_cursor_position());
self.process_search_state.search_state.char_cursor_position +=
UnicodeWidthChar::width(caught_char).unwrap_or(0);
self.update_regex(); self.update_regex();
self.update_process_gui = true; self.update_process_gui = true;
} }
@ -982,7 +1032,7 @@ impl App {
pub fn kill_highlighted_process(&mut self) -> Result<()> { pub fn kill_highlighted_process(&mut self) -> Result<()> {
// Technically unnecessary but this is a good check... // Technically unnecessary but this is a good check...
if let WidgetPosition::Process = self.current_widget_selected { if let WidgetPosition::Process = self.current_widget_selected {
if let Some(current_selected_processes) = &(self.to_delete_process_list) { if let Some(current_selected_processes) = &self.to_delete_process_list {
for pid in &current_selected_processes.1 { for pid in &current_selected_processes.1 {
process_killer::kill_process_given_pid(*pid)?; process_killer::kill_process_given_pid(*pid)?;
} }

View file

@ -1,4 +1,3 @@
use crate::data_harvester::{cpu, disks, mem, network, processes, temperature, Data};
/// In charge of cleaning, processing, and managing data. I couldn't think of /// In charge of cleaning, processing, and managing data. I couldn't think of
/// a better name for the file. Since I called data collection "harvesting", /// a better name for the file. Since I called data collection "harvesting",
/// then this is the farmer I guess. /// then this is the farmer I guess.
@ -16,6 +15,8 @@ use crate::data_harvester::{cpu, disks, mem, network, processes, temperature, Da
use std::time::Instant; use std::time::Instant;
use std::vec::Vec; use std::vec::Vec;
use crate::data_harvester::{cpu, disks, mem, network, processes, temperature, Data};
pub type TimeOffset = f64; pub type TimeOffset = f64;
pub type Value = f64; pub type Value = f64;
pub type JoinedDataPoints = (Value, Vec<(TimeOffset, Value)>); pub type JoinedDataPoints = (Value, Vec<(TimeOffset, Value)>);

View file

@ -1,6 +1,7 @@
//! This is the main file to house data collection functions. //! This is the main file to house data collection functions.
use std::{collections::HashMap, time::Instant}; use std::{collections::HashMap, time::Instant};
use sysinfo::{System, SystemExt}; use sysinfo::{System, SystemExt};
pub mod cpu; pub mod cpu;

View file

@ -1,7 +1,8 @@
use std::time::Instant;
use futures::StreamExt; use futures::StreamExt;
use heim::net; use heim::net;
use heim::units::information::byte; use heim::units::information::byte;
use std::time::Instant;
use sysinfo::{NetworkExt, System, SystemExt}; use sysinfo::{NetworkExt, System, SystemExt};
#[derive(Default, Clone, Debug)] #[derive(Default, Clone, Debug)]

View file

@ -1,10 +1,13 @@
use crate::utils::error;
use std::{ use std::{
collections::{hash_map::RandomState, HashMap}, collections::{hash_map::RandomState, HashMap},
process::Command, process::Command,
time::Instant, time::Instant,
}; };
use sysinfo::{ProcessExt, ProcessorExt, System, SystemExt}; use sysinfo::{ProcessExt, ProcessorExt, System, SystemExt};
use crate::utils::error;
#[derive(Clone)] #[derive(Clone)]
pub enum ProcessSorting { pub enum ProcessSorting {
CPU, CPU,

View file

@ -1,6 +1,7 @@
use std::cmp::Ordering;
use futures::StreamExt; use futures::StreamExt;
use heim::units::thermodynamic_temperature; use heim::units::thermodynamic_temperature;
use std::cmp::Ordering;
use sysinfo::{ComponentExt, System, SystemExt}; use sysinfo::{ComponentExt, System, SystemExt};
#[derive(Default, Debug, Clone)] #[derive(Default, Debug, Clone)]

View file

@ -1,5 +1,3 @@
/// This file is meant to house (OS specific) implementations on how to kill processes.
use crate::utils::error::BottomError;
use std::process::Command; use std::process::Command;
// Copied from SO: https://stackoverflow.com/a/55231715 // Copied from SO: https://stackoverflow.com/a/55231715
@ -12,6 +10,9 @@ use winapi::{
}, },
}; };
/// This file is meant to house (OS specific) implementations on how to kill processes.
use crate::utils::error::BottomError;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
struct Process(HANDLE); struct Process(HANDLE);

View file

@ -1,11 +1,6 @@
use crate::{ use std::cmp::max;
app::{self, data_harvester::processes::ProcessHarvest, WidgetPosition},
constants::*,
data_conversion::{ConvertedCpuData, ConvertedProcessData},
utils::error,
};
use std::cmp::{max, min};
use std::collections::HashMap; use std::collections::HashMap;
use tui::{ use tui::{
backend, backend,
layout::{Alignment, Constraint, Direction, Layout, Rect}, layout::{Alignment, Constraint, Direction, Layout, Rect},
@ -17,12 +12,19 @@ use tui::{
use unicode_segmentation::UnicodeSegmentation; use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthStr; use unicode_width::UnicodeWidthStr;
mod canvas_colours;
use canvas_colours::*; use canvas_colours::*;
mod drawing_utils;
use drawing_utils::*; use drawing_utils::*;
use crate::{
app::{self, data_harvester::processes::ProcessHarvest, WidgetPosition},
constants::*,
data_conversion::{ConvertedCpuData, ConvertedProcessData},
utils::error,
};
mod canvas_colours;
mod drawing_utils;
// Headers // Headers
const CPU_LEGEND_HEADER: [&str; 2] = ["CPU", "Use%"]; const CPU_LEGEND_HEADER: [&str; 2] = ["CPU", "Use%"];
const CPU_SELECT_LEGEND_HEADER: [&str; 2] = ["CPU", "Show (Space)"]; const CPU_SELECT_LEGEND_HEADER: [&str; 2] = ["CPU", "Show (Space)"];
@ -620,12 +622,12 @@ impl Painter {
fn draw_cpu_legend<B: backend::Backend>( fn draw_cpu_legend<B: backend::Backend>(
&self, f: &mut Frame<'_, B>, app_state: &mut app::App, draw_loc: Rect, &self, f: &mut Frame<'_, B>, app_state: &mut app::App, draw_loc: Rect,
) { ) {
let cpu_data: &[ConvertedCpuData] = &(app_state.canvas_data.cpu_data); let cpu_data: &[ConvertedCpuData] = &app_state.canvas_data.cpu_data;
let num_rows = max(0, i64::from(draw_loc.height) - 5) as u64; let num_rows = max(0, i64::from(draw_loc.height) - 5) as u64;
let start_position = get_start_position( let start_position = get_start_position(
num_rows, num_rows,
&(app_state.app_scroll_positions.scroll_direction), &app_state.app_scroll_positions.scroll_direction,
&mut app_state &mut app_state
.app_scroll_positions .app_scroll_positions
.cpu_scroll_state .cpu_scroll_state
@ -766,8 +768,8 @@ impl Painter {
fn draw_memory_graph<B: backend::Backend>( fn draw_memory_graph<B: backend::Backend>(
&self, f: &mut Frame<'_, B>, app_state: &app::App, draw_loc: Rect, &self, f: &mut Frame<'_, B>, app_state: &app::App, draw_loc: Rect,
) { ) {
let mem_data: &[(f64, f64)] = &(app_state.canvas_data.mem_data); let mem_data: &[(f64, f64)] = &app_state.canvas_data.mem_data;
let swap_data: &[(f64, f64)] = &(app_state.canvas_data.swap_data); let swap_data: &[(f64, f64)] = &app_state.canvas_data.swap_data;
let x_axis: Axis<'_, String> = Axis::default().bounds([0.0, TIME_STARTS_FROM as f64]); let x_axis: Axis<'_, String> = Axis::default().bounds([0.0, TIME_STARTS_FROM as f64]);
@ -839,8 +841,8 @@ impl Painter {
fn draw_network_graph<B: backend::Backend>( fn draw_network_graph<B: backend::Backend>(
&self, f: &mut Frame<'_, B>, app_state: &app::App, draw_loc: Rect, &self, f: &mut Frame<'_, B>, app_state: &app::App, draw_loc: Rect,
) { ) {
let network_data_rx: &[(f64, f64)] = &(app_state.canvas_data.network_data_rx); let network_data_rx: &[(f64, f64)] = &app_state.canvas_data.network_data_rx;
let network_data_tx: &[(f64, f64)] = &(app_state.canvas_data.network_data_tx); let network_data_tx: &[(f64, f64)] = &app_state.canvas_data.network_data_tx;
let x_axis: Axis<'_, String> = Axis::default().bounds([0.0, 60_000.0]); let x_axis: Axis<'_, String> = Axis::default().bounds([0.0, 60_000.0]);
let y_axis: Axis<'_, &str> = Axis::default() let y_axis: Axis<'_, &str> = Axis::default()
@ -938,7 +940,7 @@ impl Painter {
// Calculate widths // Calculate widths
let width_ratios: Vec<f64> = vec![0.25, 0.25, 0.25, 0.25]; let width_ratios: Vec<f64> = vec![0.25, 0.25, 0.25, 0.25];
let lens: &Vec<usize> = &NETWORK_HEADERS_LENS; let lens: &[usize] = &NETWORK_HEADERS_LENS;
let width = f64::from(draw_loc.width); let width = f64::from(draw_loc.width);
let variable_intrinsic_results = let variable_intrinsic_results =
@ -967,12 +969,12 @@ impl Painter {
fn draw_temp_table<B: backend::Backend>( fn draw_temp_table<B: backend::Backend>(
&self, f: &mut Frame<'_, B>, app_state: &mut app::App, draw_loc: Rect, &self, f: &mut Frame<'_, B>, app_state: &mut app::App, draw_loc: Rect,
) { ) {
let temp_sensor_data: &[Vec<String>] = &(app_state.canvas_data.temp_sensor_data); let temp_sensor_data: &[Vec<String>] = &app_state.canvas_data.temp_sensor_data;
let num_rows = max(0, i64::from(draw_loc.height) - 5) as u64; let num_rows = max(0, i64::from(draw_loc.height) - 5) as u64;
let start_position = get_start_position( let start_position = get_start_position(
num_rows, num_rows,
&(app_state.app_scroll_positions.scroll_direction), &app_state.app_scroll_positions.scroll_direction,
&mut app_state &mut app_state
.app_scroll_positions .app_scroll_positions
.temp_scroll_state .temp_scroll_state
@ -984,7 +986,7 @@ impl Painter {
app_state.is_resized, app_state.is_resized,
); );
let sliced_vec = &(temp_sensor_data[start_position as usize..]); let sliced_vec = &temp_sensor_data[start_position as usize..];
let mut temp_row_counter: i64 = 0; let mut temp_row_counter: i64 = 0;
let temperature_rows = sliced_vec.iter().map(|temp_row| { let temperature_rows = sliced_vec.iter().map(|temp_row| {
@ -1064,11 +1066,11 @@ impl Painter {
fn draw_disk_table<B: backend::Backend>( fn draw_disk_table<B: backend::Backend>(
&self, f: &mut Frame<'_, B>, app_state: &mut app::App, draw_loc: Rect, &self, f: &mut Frame<'_, B>, app_state: &mut app::App, draw_loc: Rect,
) { ) {
let disk_data: &[Vec<String>] = &(app_state.canvas_data.disk_data); let disk_data: &[Vec<String>] = &app_state.canvas_data.disk_data;
let num_rows = max(0, i64::from(draw_loc.height) - 5) as u64; let num_rows = max(0, i64::from(draw_loc.height) - 5) as u64;
let start_position = get_start_position( let start_position = get_start_position(
num_rows, num_rows,
&(app_state.app_scroll_positions.scroll_direction), &app_state.app_scroll_positions.scroll_direction,
&mut app_state &mut app_state
.app_scroll_positions .app_scroll_positions
.disk_scroll_state .disk_scroll_state
@ -1162,27 +1164,27 @@ impl Painter {
&self, f: &mut Frame<'_, B>, app_state: &mut app::App, draw_loc: Rect, &self, f: &mut Frame<'_, B>, app_state: &mut app::App, draw_loc: Rect,
) { ) {
let width = max(0, draw_loc.width as i64 - 34) as u64; // TODO: [REFACTOR] Hard coding this is terrible. let width = max(0, draw_loc.width as i64 - 34) as u64; // TODO: [REFACTOR] Hard coding this is terrible.
let query = app_state.get_current_search_query().as_str();
let grapheme_indices = UnicodeSegmentation::grapheme_indices(query, true).rev(); // Reverse due to us wanting to draw from back -> front
let cursor_position = app_state.get_cursor_position(); let cursor_position = app_state.get_cursor_position();
let right_border = min(UnicodeWidthStr::width(query), width as usize); let char_cursor_position = app_state.get_char_cursor_position();
let mut itx = 0; let start_position: usize = get_search_start_position(
let mut query_with_cursor: Vec<Text<'_>> = if let app::WidgetPosition::ProcessSearch = width as usize,
app_state.current_widget_selected &app_state.process_search_state.search_state.cursor_direction,
{ &mut app_state.process_search_state.search_state.cursor_bar,
let mut res = Vec::new(); char_cursor_position,
if cursor_position >= query.len() { app_state.is_resized,
res.push(Text::styled( );
" ",
self.colours.currently_selected_text_style,
))
}
res.extend( let query = app_state.get_current_search_query().as_str();
grapheme_indices let grapheme_indices = UnicodeSegmentation::grapheme_indices(query, true);
let mut current_grapheme_posn = 0;
let query_with_cursor: Vec<Text<'_>> =
if let app::WidgetPosition::ProcessSearch = app_state.current_widget_selected {
let mut res = grapheme_indices
.filter_map(|grapheme| { .filter_map(|grapheme| {
if itx >= right_border { current_grapheme_posn += UnicodeWidthStr::width(grapheme.1);
if current_grapheme_posn <= start_position {
None None
} else { } else {
let styled = if grapheme.0 == cursor_position { let styled = if grapheme.0 == cursor_position {
@ -1190,12 +1192,17 @@ impl Painter {
} else { } else {
Text::styled(grapheme.1, self.colours.text_style) Text::styled(grapheme.1, self.colours.text_style)
}; };
itx += UnicodeWidthStr::width(grapheme.1);
Some(styled) Some(styled)
} }
}) })
.collect::<Vec<_>>(), .collect::<Vec<_>>();
);
if cursor_position >= query.len() {
res.push(Text::styled(
" ",
self.colours.currently_selected_text_style,
))
}
res res
} else { } else {
@ -1203,20 +1210,17 @@ impl Painter {
// dealing with possibly inserting a cursor (as none is shown!) // dealing with possibly inserting a cursor (as none is shown!)
grapheme_indices grapheme_indices
.filter_map(|grapheme| { .filter_map(|grapheme| {
if itx >= right_border { current_grapheme_posn += UnicodeWidthStr::width(grapheme.1);
if current_grapheme_posn <= start_position {
None None
} else { } else {
let styled = Text::styled(grapheme.1, self.colours.text_style); let styled = Text::styled(grapheme.1, self.colours.text_style);
itx += UnicodeWidthStr::width(grapheme.1);
Some(styled) Some(styled)
} }
}) })
.collect::<Vec<_>>() .collect::<Vec<_>>()
}; };
// I feel like this is most definitely not the efficient way of doing this but eh
query_with_cursor.reverse();
let mut search_text = vec![if app_state.is_grouped() { let mut search_text = vec![if app_state.is_grouped() {
Text::styled("Search by Name: ", self.colours.table_header_style) Text::styled("Search by Name: ", self.colours.table_header_style)
} else if app_state.process_search_state.is_searching_with_pid { } else if app_state.process_search_state.is_searching_with_pid {
@ -1348,7 +1352,7 @@ impl Painter {
let position = get_start_position( let position = get_start_position(
num_rows, num_rows,
&(app_state.app_scroll_positions.scroll_direction), &app_state.app_scroll_positions.scroll_direction,
&mut app_state &mut app_state
.app_scroll_positions .app_scroll_positions
.process_scroll_state .process_scroll_state
@ -1367,7 +1371,7 @@ impl Painter {
position position
}; };
let sliced_vec = &(process_data[start_position as usize..]); let sliced_vec = &process_data[start_position as usize..];
let mut process_counter: i64 = 0; let mut process_counter: i64 = 0;
// Draw! // Draw!

View file

@ -1,9 +1,11 @@
mod colour_utils;
use colour_utils::*;
use tui::style::{Color, Modifier, Style}; use tui::style::{Color, Modifier, Style};
use colour_utils::*;
use crate::{constants::*, utils::error}; use crate::{constants::*, utils::error};
mod colour_utils;
pub struct CanvasColours { pub struct CanvasColours {
pub currently_selected_text_colour: Color, pub currently_selected_text_colour: Color,
pub currently_selected_bg_colour: Color, pub currently_selected_bg_colour: Color,

View file

@ -1,7 +1,9 @@
use crate::utils::{error, gen_util::*};
use std::collections::HashMap; use std::collections::HashMap;
use tui::style::{Color, Style}; use tui::style::{Color, Style};
use crate::utils::{error, gen_util::*};
const GOLDEN_RATIO: f32 = 0.618_034; // Approx, good enough for use (also Clippy gets mad if it's too long) const GOLDEN_RATIO: f32 = 0.618_034; // Approx, good enough for use (also Clippy gets mad if it's too long)
pub const STANDARD_FIRST_COLOUR: Color = Color::LightMagenta; pub const STANDARD_FIRST_COLOUR: Color = Color::LightMagenta;
pub const STANDARD_SECOND_COLOUR: Color = Color::LightYellow; pub const STANDARD_SECOND_COLOUR: Color = Color::LightYellow;

View file

@ -72,11 +72,43 @@ pub fn get_variable_intrinsic_widths(
#[allow(dead_code, unused_variables)] #[allow(dead_code, unused_variables)]
pub fn get_search_start_position( pub fn get_search_start_position(
num_rows: u64, scroll_direction: &app::ScrollDirection, scroll_position_bar: &mut u64, num_columns: usize, cursor_direction: &app::CursorDirection, cursor_bar: &mut usize,
currently_selected_position: u64, is_resized: bool, current_cursor_position: usize, is_resized: bool,
) -> u64 { ) -> usize {
//TODO: [Scroll] Gotta fix this too lol if is_resized {
*cursor_bar = 0;
}
match cursor_direction {
app::CursorDirection::RIGHT => {
if current_cursor_position < *cursor_bar + num_columns {
// If, using previous_scrolled_position, we can see the element
// (so within that and + num_rows) just reuse the current previously scrolled position
*cursor_bar
} else if current_cursor_position >= num_columns {
// Else if the current position past the last element visible in the list, omit
// until we can see that element
*cursor_bar = current_cursor_position - num_columns;
*cursor_bar
} else {
// Else, if it is not past the last element visible, do not omit anything
0 0
}
}
app::CursorDirection::LEFT => {
if current_cursor_position <= *cursor_bar {
// If it's past the first element, then show from that element downwards
*cursor_bar = current_cursor_position;
*cursor_bar
} else if current_cursor_position >= *cursor_bar + num_columns {
*cursor_bar = current_cursor_position - num_columns;
*cursor_bar
} else {
// Else, don't change what our start position is from whatever it is set to!
*cursor_bar
}
}
}
} }
pub fn get_start_position( pub fn get_start_position(

View file

@ -1,6 +1,10 @@
//! This mainly concerns converting collected data into things that the canvas //! This mainly concerns converting collected data into things that the canvas
//! can actually handle. //! can actually handle.
use std::collections::HashMap;
use constants::*;
use crate::{ use crate::{
app::{ app::{
data_farmer, data_farmer,
@ -10,8 +14,6 @@ use crate::{
constants, constants,
utils::gen_util::{get_exact_byte_values, get_simple_byte_values}, utils::gen_util::{get_exact_byte_values, get_simple_byte_values},
}; };
use constants::*;
use std::collections::HashMap;
#[derive(Default, Debug)] #[derive(Default, Debug)]
pub struct ConvertedNetworkData { pub struct ConvertedNetworkData {

View file

@ -1,15 +1,22 @@
#![warn(rust_2018_idioms)] #![warn(rust_2018_idioms)]
#[macro_use]
extern crate log;
#[macro_use] #[macro_use]
extern crate clap; extern crate clap;
#[macro_use] #[macro_use]
extern crate futures;
#[macro_use]
extern crate lazy_static; extern crate lazy_static;
#[macro_use] #[macro_use]
extern crate futures; extern crate log;
use serde::Deserialize; use std::{
boxed::Box,
io::{stdout, Write},
panic::{self, PanicInfo},
sync::mpsc,
thread,
time::{Duration, Instant},
};
use crossterm::{ use crossterm::{
event::{ event::{
@ -20,17 +27,17 @@ use crossterm::{
style::Print, style::Print,
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
}; };
use serde::Deserialize;
use std::{
boxed::Box,
io::{stdout, Write},
panic::{self, PanicInfo},
sync::mpsc,
thread,
time::{Duration, Instant},
};
use tui::{backend::CrosstermBackend, Terminal}; use tui::{backend::CrosstermBackend, Terminal};
use app::{
data_harvester::{self, processes::ProcessSorting},
App,
};
use constants::*;
use data_conversion::*;
use utils::error::{self, BottomError};
pub mod app; pub mod app;
mod utils { mod utils {
pub mod error; pub mod error;
@ -41,14 +48,6 @@ mod canvas;
mod constants; mod constants;
mod data_conversion; mod data_conversion;
use app::{
data_harvester::{self, processes::ProcessSorting},
App,
};
use constants::*;
use data_conversion::*;
use utils::error::{self, BottomError};
enum Event<I, J> { enum Event<I, J> {
KeyInput(I), KeyInput(I),
MouseInput(J), MouseInput(J),
@ -398,6 +397,8 @@ fn handle_key_event_or_break(
KeyCode::Char('u') => app.clear_search(), KeyCode::Char('u') => app.clear_search(),
KeyCode::Char('a') => app.skip_cursor_beginning(), KeyCode::Char('a') => app.skip_cursor_beginning(),
KeyCode::Char('e') => app.skip_cursor_end(), KeyCode::Char('e') => app.skip_cursor_end(),
// Can't do now, CTRL+BACKSPACE doesn't work and graphemes
// are hard to iter while truncating last (eloquently).
// KeyCode::Backspace => app.skip_word_backspace(), // KeyCode::Backspace => app.skip_word_backspace(),
_ => {} _ => {}
} }
@ -496,22 +497,22 @@ fn get_temperature_option(
} else if let Some(flags) = &config.flags { } else if let Some(flags) = &config.flags {
if let Some(temp_type) = &flags.temperature_type { if let Some(temp_type) = &flags.temperature_type {
// Give lowest priority to config. // Give lowest priority to config.
match temp_type.as_str() { return match temp_type.as_str() {
"fahrenheit" | "f" => { "fahrenheit" | "f" => {
return Ok(data_harvester::temperature::TemperatureType::Fahrenheit); Ok(data_harvester::temperature::TemperatureType::Fahrenheit)
} }
"kelvin" | "k" => { "kelvin" | "k" => {
return Ok(data_harvester::temperature::TemperatureType::Kelvin); Ok(data_harvester::temperature::TemperatureType::Kelvin)
} }
"celsius" | "c" => { "celsius" | "c" => {
return Ok(data_harvester::temperature::TemperatureType::Celsius); Ok(data_harvester::temperature::TemperatureType::Celsius)
} }
_ => { _ => {
return Err(BottomError::ConfigError( Err(BottomError::ConfigError(
"Invalid temperature type. Please have the value be of the form \ "Invalid temperature type. Please have the value be of the form \
<kelvin|k|celsius|c|fahrenheit|f>" <kelvin|k|celsius|c|fahrenheit|f>"
.to_string(), .to_string(),
)); ))
} }
} }
} }
@ -717,7 +718,7 @@ fn generate_config_colours(config: &Config, painter: &mut canvas::Painter) -> er
painter.colours.set_avg_cpu_colour(avg_cpu_color)?; painter.colours.set_avg_cpu_colour(avg_cpu_color)?;
} }
if let Some(cpu_core_colors) = &(colours.cpu_core_colors) { if let Some(cpu_core_colors) = &colours.cpu_core_colors {
painter.colours.set_cpu_colours(cpu_core_colors)?; painter.colours.set_cpu_colours(cpu_core_colors)?;
} }

View file

@ -1,6 +1,7 @@
use std::process::Command;
use assert_cmd::prelude::*; use assert_cmd::prelude::*;
use predicates::prelude::*; use predicates::prelude::*;
use std::process::Command;
// These tests are mostly here just to ensure that invalid results will be caught when passing arguments... // These tests are mostly here just to ensure that invalid results will be caught when passing arguments...