feature: Add mem_as_value flag (#309)

Adds a new flag, --mem_as_value (and its corresponding config option, mem_as_value = true), which defaults to showing process memory values by their amount rather than percentage.
This commit is contained in:
Clement Tsang 2020-11-19 00:28:04 -05:00 committed by GitHub
parent c0a8c347e1
commit 92636f3bf9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 186 additions and 22 deletions

View file

@ -31,6 +31,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [#296](https://github.com/ClementTsang/bottom/pull/296): Built-in colour themes. - [#296](https://github.com/ClementTsang/bottom/pull/296): Built-in colour themes.
- [#309](https://github.com/ClementTsang/bottom/pull/309): Adds a `mem_as_value` flag to default displaying process memory as value rather than percentage.
### Changes ### Changes
- [#213](https://github.com/ClementTsang/bottom/pull/213), [#214](https://github.com/ClementTsang/bottom/pull/214): Updated help descriptions, added auto-complete generation. - [#213](https://github.com/ClementTsang/bottom/pull/213), [#214](https://github.com/ClementTsang/bottom/pull/214): Updated help descriptions, added auto-complete generation.

View file

@ -233,6 +233,7 @@ Run using `btm`.
--hide_time Completely hides the time scaling. --hide_time Completely hides the time scaling.
-k, --kelvin Sets the temperature type to Kelvin. -k, --kelvin Sets the temperature type to Kelvin.
-l, --left_legend Puts the CPU chart legend to the left side. -l, --left_legend Puts the CPU chart legend to the left side.
--mem_as_value Defaults to showing process memory usage by value.
-r, --rate <MS> Sets a refresh rate in ms. -r, --rate <MS> Sets a refresh rate in ms.
-R, --regex Enables regex by default. -R, --regex Enables regex by default.
-d, --time_delta <MS> The amount in ms changed upon zooming. -d, --time_delta <MS> The amount in ms changed upon zooming.
@ -547,6 +548,7 @@ These are the following supported flag config values, which correspond to the fl
| `default_widget_count` | Unsigned Int (represents which `default_widget_type`) | | `default_widget_count` | Unsigned Int (represents which `default_widget_type`) |
| `disable_click` | Boolean | | `disable_click` | Boolean |
| `color` | String (one of ["default", "default-light", "gruvbox", "gruvbox-light"]) | | `color` | String (one of ["default", "default-light", "gruvbox", "gruvbox-light"]) |
| `mem_as_value` | Boolean |
#### Theming #### Theming

View file

@ -1,4 +1,5 @@
use std::{collections::HashMap, io::Write, path::PathBuf, time::Instant}; // use std::io::Write;
use std::{collections::HashMap, path::PathBuf, time::Instant};
use unicode_segmentation::GraphemeCursor; use unicode_segmentation::GraphemeCursor;
use unicode_width::{UnicodeWidthChar, UnicodeWidthStr}; use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
@ -1405,23 +1406,25 @@ impl App {
/// Call this whenever the config value is updated! /// Call this whenever the config value is updated!
fn update_config_file(&mut self) -> anyhow::Result<()> { fn update_config_file(&mut self) -> anyhow::Result<()> {
if self.app_config_fields.no_write { // TODO: Disabled.
// debug!("No write enabled. Config will not be written."); // if self.app_config_fields.no_write {
// Don't write! // // debug!("No write enabled. Config will not be written.");
// FIXME: [CONFIG] This should be made VERY clear to the user... make a thing saying "it will not write due to no_write option" // // Don't write!
// // FIXME: [CONFIG] This should be made VERY clear to the user... make a thing saying "it will not write due to no_write option"
// Ok(())
// } else if let Some(config_path) = &self.config_path {
// // Update
// // debug!("Updating config file - writing to: {:?}", config_path);
// std::fs::File::create(config_path)?
// .write_all(self.config.get_config_as_bytes()?.as_ref())?;
// Ok(())
// } else {
// // FIXME: [CONFIG] Put an actual error message?
// Err(anyhow::anyhow!(
// "Config path was missing, please try restarting bottom..."
// ))
// }
Ok(()) Ok(())
} else if let Some(config_path) = &self.config_path {
// Update
// debug!("Updating config file - writing to: {:?}", config_path);
std::fs::File::create(config_path)?
.write_all(self.config.get_config_as_bytes()?.as_ref())?;
Ok(())
} else {
// FIXME: [CONFIG] Put an actual error message?
Err(anyhow::anyhow!(
"Config path was missing, please try restarting bottom..."
))
}
} }
pub fn kill_highlighted_process(&mut self) -> Result<()> { pub fn kill_highlighted_process(&mut self) -> Result<()> {

View file

@ -391,6 +391,7 @@ pub struct ProcWidgetState {
impl ProcWidgetState { impl ProcWidgetState {
pub fn init( pub fn init(
is_case_sensitive: bool, is_match_whole_word: bool, is_use_regex: bool, is_grouped: bool, is_case_sensitive: bool, is_match_whole_word: bool, is_use_regex: bool, is_grouped: bool,
show_memory_as_values: bool,
) -> Self { ) -> Self {
let mut process_search_state = ProcessSearchState::default(); let mut process_search_state = ProcessSearchState::default();
if is_case_sensitive { if is_case_sensitive {
@ -410,9 +411,15 @@ impl ProcWidgetState {
let mut columns = ProcColumn::default(); let mut columns = ProcColumn::default();
columns.set_to_sorted_index(&process_sorting_type); columns.set_to_sorted_index(&process_sorting_type);
if is_grouped { if is_grouped {
// Normally defaults to showing by PID, toggle count on instead.
columns.toggle(&ProcessSorting::Count); columns.toggle(&ProcessSorting::Count);
columns.toggle(&ProcessSorting::Pid); columns.toggle(&ProcessSorting::Pid);
} }
if show_memory_as_values {
// Normally defaults to showing by percent, toggle value on instead.
columns.toggle(&ProcessSorting::Mem);
columns.toggle(&ProcessSorting::MemPercent);
}
ProcWidgetState { ProcWidgetState {
process_search_state, process_search_state,

View file

@ -227,6 +227,13 @@ Defaults to \"default\".
"nord", "nord",
]) ])
.hide_possible_values(true); .hide_possible_values(true);
let mem_as_value = Arg::with_name("mem_as_value")
.long("mem_as_value")
.help("Defaults to showing process memory usage by value.")
.long_help(
"\
Defaults to showing process memory usage by value. Otherwise, it defaults to showing it by percentage.\n\n",
);
let default_time_value = Arg::with_name("default_time_value") let default_time_value = Arg::with_name("default_time_value")
.short("t") .short("t")
.long("default_time_value") .long("default_time_value")
@ -345,6 +352,7 @@ The minimum is 1s (1000), and defaults to 15s (15000).\n\n\n",
.arg(config_location) .arg(config_location)
.arg(color) .arg(color)
.arg(debug) .arg(debug)
.arg(mem_as_value)
.arg(default_time_value) .arg(default_time_value)
.arg(default_widget_count) .arg(default_widget_count)
.arg(default_widget_type) .arg(default_widget_type)

View file

@ -365,9 +365,133 @@ pub const DEFAULT_BATTERY_LAYOUT: &str = r##"
// Config and flags // Config and flags
pub const DEFAULT_CONFIG_FILE_PATH: &str = "bottom/bottom.toml"; pub const DEFAULT_CONFIG_FILE_PATH: &str = "bottom/bottom.toml";
pub const CONFIG_TOP_HEAD: &str = r##"# This is bottom's config file. Values in this config file will change when changed in the pub const OLD_CONFIG_TEXT: &str = r##"# This is a default config file for bottom. All of the settings are commented
# interface. You can also manually change these values. Be aware that contents of this file will be overwritten if something is # out by default; if you wish to change them uncomment and modify as you see
# changed in the application; you can disable writing via the --no_write flag or no_write config option. # fit.
# This group of options represents a command-line flag/option. Flags explicitly
# added when running (ie: btm -a) will override this config file if an option
# is also set here.
[flags]
# Whether to hide the average cpu entry.
#hide_avg_cpu = false
# Whether to use dot markers rather than braille.
#dot_marker = false
# The update rate of the application.
#rate = 1000
# Whether to put the CPU legend to the left.
#left_legend = false
# Whether to set CPU% on a process to be based on the total CPU or just current usage.
#current_usage = false
# Whether to group processes with the same name together by default.
#group_processes = false
# Whether to make process searching case sensitive by default.
#case_sensitive = false
# Whether to make process searching look for matching the entire word by default.
#whole_word = false
# Whether to make process searching use regex by default.
#regex = false
# Defaults to Celsius. Temperature is one of:
#temperature_type = "k"
#temperature_type = "f"
#temperature_type = "c"
#temperature_type = "kelvin"
#temperature_type = "fahrenheit"
#temperature_type = "celsius"
# The default time interval (in milliseconds).
#default_time_value = 60000
# The time delta on each zoom in/out action (in milliseconds).
#time_delta = 15000
# Override layout default widget
#default_widget_type = "proc"
#default_widget_count = 1
# Use basic mode
#basic = false
# Use the old network legend style
#use_old_network_legend = false
# Remove space in tables
#hide_table_gap = false
# Disable mouse clicks
#disable_click = false
# Built-in themes. Valid values are "default", "default-light", "gruvbox", "gruvbox-light"
#color = "default"
# Show memory values in the processes widget as values by default
# mem_as_value = false
# These are all the components that support custom theming. Note that colour support
# will depend on terminal support.
[colors]
# Represents the colour of table headers (processes, CPU, disks, temperature).
#table_header_color="LightBlue"
# Represents the colour of the label each widget has.
#widget_title_color="Gray"
# Represents the average CPU color.
#avg_cpu_color="Red"
# Represents the colour the core will use in the CPU legend and graph.
#cpu_core_colors=["LightMagenta", "LightYellow", "LightCyan", "LightGreen", "LightBlue", "LightRed", "Cyan", "Green", "Blue", "Red"]
# Represents the colour RAM will use in the memory legend and graph.
#ram_color="LightMagenta"
# Represents the colour SWAP will use in the memory legend and graph.
#swap_color="LightYellow"
# Represents the colour rx will use in the network legend and graph.
#rx_color="LightCyan"
# Represents the colour tx will use in the network legend and graph.
#tx_color="LightGreen"
# Represents the colour of the border of unselected widgets.
#border_color="Gray"
# Represents the colour of the border of selected widgets.
#highlighted_border_color="LightBlue"
# Represents the colour of most text.
#text_color="Gray"
# Represents the colour of text that is selected.
#selected_text_color="Black"
# Represents the background colour of text that is selected.
#selected_bg_color="LightBlue"
# Represents the colour of the lines and text of the graph.
#graph_color="Gray"
# Represents the colours of the battery based on charge
#high_battery_color="green"
#medium_battery_color="yellow"
#low_battery_color="red"
# Layout - layouts follow a pattern like this:
# [[row]] represents a row in the application.
# [[row.child]] represents either a widget or a column.
# [[row.child.child]] represents a widget.
#
# All widgets must have the type value set to one of ["cpu", "mem", "proc", "net", "temp", "disk", "empty"].
# All layout components have a ratio value - if this is not set, then it defaults to 1.
# The default widget layout:
#[[row]]
# ratio=30
# [[row.child]]
# type="cpu"
#[[row]]
# ratio=40
# [[row.child]]
# ratio=4
# type="mem"
# [[row.child]]
# ratio=3
# [[row.child.child]]
# type="temp"
# [[row.child.child]]
# type="disk"
#[[row]]
# ratio=30
# [[row.child]]
# type="net"
# [[row.child]]
# type="proc"
# default=true
"##;
pub const CONFIG_TOP_HEAD: &str = r##"# This is bottom's config file.
# Values in this config file will change when changed in the interface.
# You can also manually change these values.
# Be aware that contents of this file will be overwritten if something is
# changed in the application; you can disable writing via the
# --no_write flag or no_write config option.
"##; "##;

View file

@ -225,7 +225,8 @@ pub fn create_or_get_config(config_path: &Option<PathBuf>) -> error::Result<Conf
if let Some(parent_path) = path.parent() { if let Some(parent_path) = path.parent() {
fs::create_dir_all(parent_path)?; fs::create_dir_all(parent_path)?;
} }
fs::File::create(path)?.write_all(CONFIG_TOP_HEAD.as_bytes())?; // fs::File::create(path)?.write_all(CONFIG_TOP_HEAD.as_bytes())?;
fs::File::create(path)?.write_all(OLD_CONFIG_TEXT.as_bytes())?;
Ok(Config::default()) Ok(Config::default())
} }
} else { } else {

View file

@ -141,6 +141,9 @@ pub struct ConfigFlags {
#[builder(default, setter(strip_option))] #[builder(default, setter(strip_option))]
pub search_regex_enabled_widgets: Option<Vec<WidgetIdEnabled>>, pub search_regex_enabled_widgets: Option<Vec<WidgetIdEnabled>>,
#[builder(default, setter(strip_option))]
pub mem_as_value: Option<bool>,
} }
#[derive(Clone, Default, Debug, Deserialize, Serialize)] #[derive(Clone, Default, Debug, Deserialize, Serialize)]
@ -231,6 +234,8 @@ pub fn build_app(
let is_custom_layout = config.row.is_some(); let is_custom_layout = config.row.is_some();
let mut used_widget_set = HashSet::new(); let mut used_widget_set = HashSet::new();
let show_memory_as_values = get_mem_as_value(matches, config);
for row in &widget_layout.rows { for row in &widget_layout.rows {
for col in &row.children { for col in &row.children {
for col_row in &col.children { for col_row in &col.children {
@ -296,6 +301,7 @@ pub fn build_app(
is_match_whole_word, is_match_whole_word,
is_use_regex, is_use_regex,
is_grouped, is_grouped,
show_memory_as_values,
), ),
); );
} }
@ -920,3 +926,14 @@ pub fn get_color_scheme(
// And lastly, the final case is just "default". // And lastly, the final case is just "default".
Ok(ColourScheme::Default) Ok(ColourScheme::Default)
} }
fn get_mem_as_value(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
if matches.is_present("mem_as_value") {
return true;
} else if let Some(flags) = &config.flags {
if let Some(mem_as_value) = flags.mem_as_value {
return mem_as_value;
}
}
false
}