mirror of
https://github.com/alexpasmantier/television.git
synced 2025-07-20 00:50:09 +00:00
new channel git-repos and previews
This commit is contained in:
parent
d2213af480
commit
590fe14ee5
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -2459,6 +2459,7 @@ dependencies = [
|
||||
"strum",
|
||||
"syntect",
|
||||
"television-derive",
|
||||
"termtree",
|
||||
"tokio",
|
||||
"toml",
|
||||
"tracing",
|
||||
@ -2500,6 +2501,12 @@ dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termtree"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683"
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.64"
|
||||
|
11
Cargo.toml
11
Cargo.toml
@ -22,9 +22,7 @@ path = "crates/television/main.rs"
|
||||
name = "tv"
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"crates/television_derive",
|
||||
]
|
||||
members = ["crates/television_derive"]
|
||||
|
||||
[dependencies]
|
||||
television-derive = { version = "0.1.0", path = "crates/television_derive" }
|
||||
@ -68,6 +66,7 @@ tracing-subscriber = { version = "0.3.18", features = ["env-filter", "serde"] }
|
||||
unicode-width = "0.2.0"
|
||||
human-panic = "2.0.2"
|
||||
pretty_assertions = "1.4.1"
|
||||
termtree = "0.5.1"
|
||||
|
||||
|
||||
[build-dependencies]
|
||||
@ -87,13 +86,7 @@ debug = true
|
||||
|
||||
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
debug = "none"
|
||||
strip = "symbols"
|
||||
debug-assertions = false
|
||||
overflow-checks = false
|
||||
lto = "thin"
|
||||
panic = "abort"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
crossterm = { version = "0.28.1", features = ["serde", "use-dev-tty"] }
|
||||
|
1
TODO.md
1
TODO.md
@ -55,3 +55,4 @@ tv with itself?
|
||||
- [ ] have a keybind to send all current entries to stdout ... oorrrrr to another channel??
|
||||
- [ ] action menu on the bottom: send to channel, copy to clipboard, send to stdout, ... maybe with tab to navigate
|
||||
between possible actions (defined per channel, not all channels can pipe to all channels)
|
||||
- [ ] git repositories channel (crawl the filesystem for git repos)
|
||||
|
4
build.rs
4
build.rs
@ -1,7 +1,5 @@
|
||||
use anyhow::Result;
|
||||
use vergen_gix::{
|
||||
BuildBuilder, CargoBuilder, Emitter, GixBuilder, RustcBuilder,
|
||||
};
|
||||
use vergen_gix::{BuildBuilder, CargoBuilder, Emitter, RustcBuilder};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let build = BuildBuilder::default().build_date(true).build()?;
|
||||
|
@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize};
|
||||
use strum::Display;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Display, Serialize, Deserialize)]
|
||||
pub(crate) enum Action {
|
||||
pub enum Action {
|
||||
// input actions
|
||||
AddInputChar(char),
|
||||
DeletePrevChar,
|
||||
|
@ -67,7 +67,7 @@ use crate::{
|
||||
render::{render, RenderingTask},
|
||||
};
|
||||
|
||||
pub(crate) struct App {
|
||||
pub struct App {
|
||||
config: Config,
|
||||
// maybe move these two into config instead of passing them
|
||||
// via the cli?
|
||||
@ -87,7 +87,7 @@ pub(crate) struct App {
|
||||
#[derive(
|
||||
Default, Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize,
|
||||
)]
|
||||
pub(crate) enum Mode {
|
||||
pub enum Mode {
|
||||
#[default]
|
||||
Help,
|
||||
Input,
|
||||
@ -96,7 +96,7 @@ pub(crate) enum Mode {
|
||||
}
|
||||
|
||||
impl App {
|
||||
pub(crate) fn new(
|
||||
pub fn new(
|
||||
channel: CliTvChannel,
|
||||
tick_rate: f64,
|
||||
frame_rate: f64,
|
||||
|
@ -4,6 +4,7 @@ use television_derive::CliChannel;
|
||||
mod alias;
|
||||
mod env;
|
||||
mod files;
|
||||
mod git_repos;
|
||||
mod stdin;
|
||||
mod text;
|
||||
|
||||
@ -87,9 +88,10 @@ pub trait TelevisionChannel: Send {
|
||||
///
|
||||
#[allow(dead_code, clippy::module_name_repetitions)]
|
||||
#[derive(CliChannel)]
|
||||
pub(crate) enum AvailableChannels {
|
||||
pub enum AvailableChannels {
|
||||
Env(env::Channel),
|
||||
Files(files::Channel),
|
||||
GitRepos(git_repos::Channel),
|
||||
Text(text::Channel),
|
||||
Stdin(stdin::Channel),
|
||||
Alias(alias::Channel),
|
||||
|
@ -16,7 +16,7 @@ struct Alias {
|
||||
value: String,
|
||||
}
|
||||
|
||||
pub(crate) struct Channel {
|
||||
pub struct Channel {
|
||||
matcher: Nucleo<Alias>,
|
||||
last_pattern: String,
|
||||
file_icon: FileIcon,
|
||||
@ -67,7 +67,7 @@ fn get_raw_aliases(shell: &str) -> Vec<String> {
|
||||
}
|
||||
|
||||
impl Channel {
|
||||
pub(crate) fn new() -> Self {
|
||||
pub fn new() -> Self {
|
||||
let raw_shell = get_current_shell().unwrap_or("bash".to_string());
|
||||
let shell = raw_shell.split('/').last().unwrap();
|
||||
debug!("Current shell: {}", shell);
|
||||
|
@ -17,7 +17,7 @@ struct EnvVar {
|
||||
}
|
||||
|
||||
#[allow(clippy::module_name_repetitions)]
|
||||
pub(crate) struct Channel {
|
||||
pub struct Channel {
|
||||
matcher: Nucleo<EnvVar>,
|
||||
last_pattern: String,
|
||||
file_icon: FileIcon,
|
||||
@ -30,7 +30,7 @@ const NUM_THREADS: usize = 1;
|
||||
const FILE_ICON_STR: &str = "config";
|
||||
|
||||
impl Channel {
|
||||
pub(crate) fn new() -> Self {
|
||||
pub fn new() -> Self {
|
||||
let matcher = Nucleo::new(
|
||||
Config::DEFAULT,
|
||||
Arc::new(|| {}),
|
||||
|
@ -15,18 +15,18 @@ use crate::{
|
||||
};
|
||||
use crate::{fuzzy::MATCHER, utils::strings::PRINTABLE_ASCII_THRESHOLD};
|
||||
|
||||
pub(crate) struct Channel {
|
||||
pub struct Channel {
|
||||
matcher: Nucleo<DirEntry>,
|
||||
last_pattern: String,
|
||||
result_count: u32,
|
||||
total_count: u32,
|
||||
running: bool,
|
||||
// TODO: cache results (to make deleting characters smoother) but like
|
||||
// PERF: cache results (to make deleting characters smoother) but like
|
||||
// a shallow cache (maybe more like a stack actually? so we just pop result sets)
|
||||
}
|
||||
|
||||
impl Channel {
|
||||
pub(crate) fn new() -> Self {
|
||||
pub fn new() -> Self {
|
||||
let matcher = Nucleo::new(
|
||||
Config::DEFAULT.match_paths(),
|
||||
Arc::new(|| {}),
|
||||
@ -126,7 +126,8 @@ impl TelevisionChannel for Channel {
|
||||
#[allow(clippy::unused_async)]
|
||||
async fn load_files(path: PathBuf, injector: Injector<DirEntry>) {
|
||||
let current_dir = std::env::current_dir().unwrap();
|
||||
let walker = walk_builder(&path, *DEFAULT_NUM_THREADS).build_parallel();
|
||||
let walker =
|
||||
walk_builder(&path, *DEFAULT_NUM_THREADS, None).build_parallel();
|
||||
|
||||
walker.run(|| {
|
||||
let injector = injector.clone();
|
||||
@ -134,7 +135,6 @@ async fn load_files(path: PathBuf, injector: Injector<DirEntry>) {
|
||||
Box::new(move |result| {
|
||||
if let Ok(entry) = result {
|
||||
if entry.file_type().unwrap().is_file() {
|
||||
// Send the path via the async channel
|
||||
let file_name = entry.file_name();
|
||||
if proportion_of_printable_ascii_characters(
|
||||
file_name.as_bytes(),
|
||||
|
161
crates/television/channels/git_repos.rs
Normal file
161
crates/television/channels/git_repos.rs
Normal file
@ -0,0 +1,161 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use devicons::FileIcon;
|
||||
use ignore::{overrides::OverrideBuilder, DirEntry};
|
||||
use nucleo::{
|
||||
pattern::{CaseMatching, Normalization},
|
||||
Config, Nucleo,
|
||||
};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::{
|
||||
entry::Entry,
|
||||
fuzzy::MATCHER,
|
||||
previewers::PreviewType,
|
||||
utils::files::{walk_builder, DEFAULT_NUM_THREADS},
|
||||
};
|
||||
|
||||
use crate::channels::TelevisionChannel;
|
||||
|
||||
pub struct Channel {
|
||||
matcher: Nucleo<DirEntry>,
|
||||
last_pattern: String,
|
||||
result_count: u32,
|
||||
total_count: u32,
|
||||
running: bool,
|
||||
}
|
||||
|
||||
impl Channel {
|
||||
pub fn new() -> Self {
|
||||
let matcher = Nucleo::new(
|
||||
Config::DEFAULT.match_paths(),
|
||||
Arc::new(|| {}),
|
||||
None,
|
||||
1,
|
||||
);
|
||||
// start loading files in the background
|
||||
tokio::spawn(crawl_for_repos(
|
||||
std::env::home_dir().expect("Could not get home directory"),
|
||||
matcher.injector(),
|
||||
));
|
||||
Channel {
|
||||
matcher,
|
||||
last_pattern: String::new(),
|
||||
result_count: 0,
|
||||
total_count: 0,
|
||||
running: false,
|
||||
}
|
||||
}
|
||||
|
||||
const MATCHER_TICK_TIMEOUT: u64 = 10;
|
||||
}
|
||||
|
||||
impl TelevisionChannel for Channel {
|
||||
fn find(&mut self, pattern: &str) {
|
||||
if pattern != self.last_pattern {
|
||||
self.matcher.pattern.reparse(
|
||||
0,
|
||||
pattern,
|
||||
CaseMatching::Smart,
|
||||
Normalization::Smart,
|
||||
pattern.starts_with(&self.last_pattern),
|
||||
);
|
||||
self.last_pattern = pattern.to_string();
|
||||
}
|
||||
}
|
||||
|
||||
fn result_count(&self) -> u32 {
|
||||
self.result_count
|
||||
}
|
||||
|
||||
fn total_count(&self) -> u32 {
|
||||
self.total_count
|
||||
}
|
||||
|
||||
fn results(&mut self, num_entries: u32, offset: u32) -> Vec<Entry> {
|
||||
let status = self.matcher.tick(Self::MATCHER_TICK_TIMEOUT);
|
||||
let snapshot = self.matcher.snapshot();
|
||||
if status.changed {
|
||||
self.result_count = snapshot.matched_item_count();
|
||||
self.total_count = snapshot.item_count();
|
||||
}
|
||||
self.running = status.running;
|
||||
let mut indices = Vec::new();
|
||||
let mut matcher = MATCHER.lock();
|
||||
|
||||
snapshot
|
||||
.matched_items(
|
||||
offset
|
||||
..(num_entries + offset)
|
||||
.min(snapshot.matched_item_count()),
|
||||
)
|
||||
.map(move |item| {
|
||||
snapshot.pattern().column_pattern(0).indices(
|
||||
item.matcher_columns[0].slice(..),
|
||||
&mut matcher,
|
||||
&mut indices,
|
||||
);
|
||||
indices.sort_unstable();
|
||||
indices.dedup();
|
||||
let indices = indices.drain(..);
|
||||
|
||||
let path = item.matcher_columns[0].to_string();
|
||||
Entry::new(path.clone(), PreviewType::Directory)
|
||||
.with_name_match_ranges(
|
||||
indices.map(|i| (i, i + 1)).collect(),
|
||||
)
|
||||
.with_icon(FileIcon::from(&path))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn get_result(&self, index: u32) -> Option<Entry> {
|
||||
let snapshot = self.matcher.snapshot();
|
||||
snapshot.get_matched_item(index).map(|item| {
|
||||
let path = item.matcher_columns[0].to_string();
|
||||
Entry::new(path.clone(), PreviewType::Directory)
|
||||
.with_icon(FileIcon::from(&path))
|
||||
})
|
||||
}
|
||||
|
||||
fn running(&self) -> bool {
|
||||
self.running
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::unused_async)]
|
||||
async fn crawl_for_repos(
|
||||
starting_point: std::path::PathBuf,
|
||||
injector: nucleo::Injector<DirEntry>,
|
||||
) {
|
||||
let mut walker_overrides_builder = OverrideBuilder::new(&starting_point);
|
||||
walker_overrides_builder.add(".git").unwrap();
|
||||
let walker = walk_builder(
|
||||
&starting_point,
|
||||
*DEFAULT_NUM_THREADS,
|
||||
Some(walker_overrides_builder.build().unwrap()),
|
||||
)
|
||||
.build_parallel();
|
||||
|
||||
walker.run(|| {
|
||||
let injector = injector.clone();
|
||||
Box::new(move |result| {
|
||||
if let Ok(entry) = result {
|
||||
if entry.file_type().unwrap().is_dir()
|
||||
&& entry.path().ends_with(".git")
|
||||
{
|
||||
debug!("Found git repo: {:?}", entry.path());
|
||||
let _ = injector.push(entry, |e, cols| {
|
||||
cols[0] = e
|
||||
.path()
|
||||
.parent()
|
||||
.unwrap()
|
||||
.to_string_lossy()
|
||||
.into();
|
||||
});
|
||||
}
|
||||
}
|
||||
ignore::WalkState::Continue
|
||||
})
|
||||
});
|
||||
}
|
@ -11,7 +11,7 @@ use crate::previewers::PreviewType;
|
||||
|
||||
use super::TelevisionChannel;
|
||||
|
||||
pub(crate) struct Channel {
|
||||
pub struct Channel {
|
||||
matcher: Nucleo<String>,
|
||||
last_pattern: String,
|
||||
result_count: u32,
|
||||
@ -23,7 +23,7 @@ pub(crate) struct Channel {
|
||||
const NUM_THREADS: usize = 2;
|
||||
|
||||
impl Channel {
|
||||
pub(crate) fn new() -> Self {
|
||||
pub fn new() -> Self {
|
||||
let mut lines = Vec::new();
|
||||
for line in std::io::stdin().lock().lines().map_while(Result::ok) {
|
||||
debug!("Read line: {:?}", line);
|
||||
|
@ -15,7 +15,7 @@ use tracing::{debug, info};
|
||||
use super::TelevisionChannel;
|
||||
use crate::previewers::PreviewType;
|
||||
use crate::utils::{
|
||||
files::{is_not_text, is_valid_utf8, walk_builder, DEFAULT_NUM_THREADS},
|
||||
files::{is_not_text, walk_builder, DEFAULT_NUM_THREADS},
|
||||
strings::preprocess_line,
|
||||
};
|
||||
use crate::{
|
||||
@ -41,7 +41,7 @@ impl CandidateLine {
|
||||
}
|
||||
|
||||
#[allow(clippy::module_name_repetitions)]
|
||||
pub(crate) struct Channel {
|
||||
pub struct Channel {
|
||||
matcher: Nucleo<CandidateLine>,
|
||||
last_pattern: String,
|
||||
result_count: u32,
|
||||
@ -50,7 +50,7 @@ pub(crate) struct Channel {
|
||||
}
|
||||
|
||||
impl Channel {
|
||||
pub(crate) fn new() -> Self {
|
||||
pub fn new() -> Self {
|
||||
let matcher = Nucleo::new(Config::DEFAULT, Arc::new(|| {}), None, 1);
|
||||
// start loading files in the background
|
||||
tokio::spawn(load_candidates(
|
||||
@ -163,7 +163,8 @@ const MAX_FILE_SIZE: u64 = 4 * 1024 * 1024;
|
||||
#[allow(clippy::unused_async)]
|
||||
async fn load_candidates(path: PathBuf, injector: Injector<CandidateLine>) {
|
||||
let current_dir = std::env::current_dir().unwrap();
|
||||
let walker = walk_builder(&path, *DEFAULT_NUM_THREADS).build_parallel();
|
||||
let walker =
|
||||
walk_builder(&path, *DEFAULT_NUM_THREADS, None).build_parallel();
|
||||
|
||||
walker.run(|| {
|
||||
let injector = injector.clone();
|
||||
@ -218,7 +219,6 @@ async fn load_candidates(path: PathBuf, injector: Injector<CandidateLine>) {
|
||||
line,
|
||||
line_number,
|
||||
);
|
||||
// Send the line via the async channel
|
||||
let _ = injector.push(
|
||||
candidate,
|
||||
|c, cols| {
|
||||
|
@ -5,7 +5,7 @@ use crate::config::{get_config_dir, get_data_dir};
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(author, version = version(), about)]
|
||||
pub(crate) struct Cli {
|
||||
pub struct Cli {
|
||||
/// Which channel shall we watch?
|
||||
#[arg(value_enum, default_value = "files")]
|
||||
pub channel: CliTvChannel,
|
||||
@ -30,7 +30,7 @@ const VERSION_MESSAGE: &str = concat!(
|
||||
")"
|
||||
);
|
||||
|
||||
pub(crate) fn version() -> String {
|
||||
pub fn version() -> String {
|
||||
let author = clap::crate_authors!();
|
||||
|
||||
// let current_exe_path = PathBuf::from(clap::crate_name!()).display().to_string();
|
||||
|
@ -20,7 +20,7 @@ const CONFIG: &str = include_str!("../../.config/config.toml");
|
||||
|
||||
#[allow(dead_code, clippy::module_name_repetitions)]
|
||||
#[derive(Clone, Debug, Deserialize, Default)]
|
||||
pub(crate) struct AppConfig {
|
||||
pub struct AppConfig {
|
||||
#[serde(default)]
|
||||
pub data_dir: PathBuf,
|
||||
#[serde(default)]
|
||||
@ -28,7 +28,7 @@ pub(crate) struct AppConfig {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize)]
|
||||
pub(crate) struct Config {
|
||||
pub struct Config {
|
||||
#[allow(clippy::struct_field_names)]
|
||||
#[serde(default, flatten)]
|
||||
pub config: AppConfig,
|
||||
@ -52,7 +52,7 @@ lazy_static! {
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub(crate) fn new() -> Result<Self, config::ConfigError> {
|
||||
pub fn new() -> Result<Self, config::ConfigError> {
|
||||
//let default_config: Config = json5::from_str(CONFIG).unwrap();
|
||||
let default_config: Config = toml::from_str(CONFIG).unwrap();
|
||||
let data_dir = get_data_dir();
|
||||
@ -101,7 +101,7 @@ impl Config {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_data_dir() -> PathBuf {
|
||||
pub fn get_data_dir() -> PathBuf {
|
||||
let directory = if let Some(s) = DATA_FOLDER.clone() {
|
||||
s
|
||||
} else if let Some(proj_dirs) = project_directory() {
|
||||
@ -112,7 +112,7 @@ pub(crate) fn get_data_dir() -> PathBuf {
|
||||
directory
|
||||
}
|
||||
|
||||
pub(crate) fn get_config_dir() -> PathBuf {
|
||||
pub fn get_config_dir() -> PathBuf {
|
||||
let directory = if let Some(s) = CONFIG_FOLDER.clone() {
|
||||
s
|
||||
} else if let Some(proj_dirs) = project_directory() {
|
||||
@ -129,7 +129,7 @@ fn project_directory() -> Option<ProjectDirs> {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deref, DerefMut)]
|
||||
pub(crate) struct KeyBindings(pub HashMap<Mode, HashMap<Key, Action>>);
|
||||
pub struct KeyBindings(pub HashMap<Mode, HashMap<Key, Action>>);
|
||||
|
||||
impl<'de> Deserialize<'de> for KeyBindings {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
@ -237,7 +237,7 @@ fn parse_key_code_with_modifiers(
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn key_event_to_string(key_event: &KeyEvent) -> String {
|
||||
pub fn key_event_to_string(key_event: &KeyEvent) -> String {
|
||||
let char;
|
||||
let key_code = match key_event.code {
|
||||
KeyCode::Backspace => "backspace",
|
||||
@ -300,7 +300,7 @@ pub(crate) fn key_event_to_string(key_event: &KeyEvent) -> String {
|
||||
key
|
||||
}
|
||||
|
||||
pub(crate) fn parse_key(raw: &str) -> Result<Key, String> {
|
||||
pub fn parse_key(raw: &str) -> Result<Key, String> {
|
||||
if raw.chars().filter(|c| *c == '>').count()
|
||||
!= raw.chars().filter(|c| *c == '<').count()
|
||||
{
|
||||
@ -317,7 +317,7 @@ pub(crate) fn parse_key(raw: &str) -> Result<Key, String> {
|
||||
Ok(convert_raw_event_to_key(key_event))
|
||||
}
|
||||
|
||||
pub(crate) fn default_num_threads() -> NonZeroUsize {
|
||||
pub fn default_num_threads() -> NonZeroUsize {
|
||||
// default to 1 thread if we can't determine the number of available threads
|
||||
let default = NonZeroUsize::MIN;
|
||||
// never use more than 32 threads to avoid startup overhead
|
||||
@ -329,7 +329,7 @@ pub(crate) fn default_num_threads() -> NonZeroUsize {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deref, DerefMut)]
|
||||
pub(crate) struct Styles(pub HashMap<Mode, HashMap<String, Style>>);
|
||||
pub struct Styles(pub HashMap<Mode, HashMap<String, Style>>);
|
||||
|
||||
impl<'de> Deserialize<'de> for Styles {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
@ -356,7 +356,7 @@ impl<'de> Deserialize<'de> for Styles {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn parse_style(line: &str) -> Style {
|
||||
pub fn parse_style(line: &str) -> Style {
|
||||
let (foreground, background) =
|
||||
line.split_at(line.to_lowercase().find("on ").unwrap_or(line.len()));
|
||||
let foreground = process_color_string(foreground);
|
||||
|
@ -3,7 +3,7 @@ use devicons::FileIcon;
|
||||
use crate::previewers::PreviewType;
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub(crate) struct Entry {
|
||||
pub struct Entry {
|
||||
pub name: String,
|
||||
display_name: Option<String>,
|
||||
pub value: Option<String>,
|
||||
@ -15,7 +15,7 @@ pub(crate) struct Entry {
|
||||
}
|
||||
|
||||
impl Entry {
|
||||
pub(crate) fn new(name: String, preview_type: PreviewType) -> Self {
|
||||
pub fn new(name: String, preview_type: PreviewType) -> Self {
|
||||
Self {
|
||||
name,
|
||||
display_name: None,
|
||||
@ -28,17 +28,17 @@ impl Entry {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn with_display_name(mut self, display_name: String) -> Self {
|
||||
pub fn with_display_name(mut self, display_name: String) -> Self {
|
||||
self.display_name = Some(display_name);
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn with_value(mut self, value: String) -> Self {
|
||||
pub fn with_value(mut self, value: String) -> Self {
|
||||
self.value = Some(value);
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn with_name_match_ranges(
|
||||
pub fn with_name_match_ranges(
|
||||
mut self,
|
||||
name_match_ranges: Vec<(u32, u32)>,
|
||||
) -> Self {
|
||||
@ -46,7 +46,7 @@ impl Entry {
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn with_value_match_ranges(
|
||||
pub fn with_value_match_ranges(
|
||||
mut self,
|
||||
value_match_ranges: Vec<(u32, u32)>,
|
||||
) -> Self {
|
||||
@ -54,21 +54,21 @@ impl Entry {
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn with_icon(mut self, icon: FileIcon) -> Self {
|
||||
pub fn with_icon(mut self, icon: FileIcon) -> Self {
|
||||
self.icon = Some(icon);
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn with_line_number(mut self, line_number: usize) -> Self {
|
||||
pub fn with_line_number(mut self, line_number: usize) -> Self {
|
||||
self.line_number = Some(line_number);
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn display_name(&self) -> &str {
|
||||
pub fn display_name(&self) -> &str {
|
||||
self.display_name.as_ref().unwrap_or(&self.name)
|
||||
}
|
||||
|
||||
pub(crate) fn stdout_repr(&self) -> String {
|
||||
pub fn stdout_repr(&self) -> String {
|
||||
let mut repr = self.name.clone();
|
||||
if let Some(line_number) = self.line_number {
|
||||
repr.push_str(&format!(":{line_number}"));
|
||||
|
@ -3,7 +3,7 @@ use std::env;
|
||||
use color_eyre::Result;
|
||||
use tracing::error;
|
||||
|
||||
pub(crate) fn init() -> Result<()> {
|
||||
pub fn init() -> Result<()> {
|
||||
let (panic_hook, eyre_hook) = color_eyre::config::HookBuilder::default()
|
||||
.panic_section(format!(
|
||||
"This is a bug. Consider reporting it at {}",
|
||||
|
@ -17,7 +17,7 @@ use tokio::sync::mpsc;
|
||||
use tracing::warn;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub(crate) enum Event<I> {
|
||||
pub enum Event<I> {
|
||||
Closed,
|
||||
Input(I),
|
||||
FocusLost,
|
||||
@ -29,7 +29,7 @@ pub(crate) enum Event<I> {
|
||||
#[derive(
|
||||
Debug, Clone, Copy, Serialize, Deserialize, PartialEq, PartialOrd, Eq, Hash,
|
||||
)]
|
||||
pub(crate) enum Key {
|
||||
pub enum Key {
|
||||
Backspace,
|
||||
Enter,
|
||||
Left,
|
||||
@ -62,7 +62,7 @@ pub(crate) enum Key {
|
||||
}
|
||||
|
||||
#[allow(clippy::module_name_repetitions)]
|
||||
pub(crate) struct EventLoop {
|
||||
pub struct EventLoop {
|
||||
pub rx: mpsc::UnboundedReceiver<Event<Key>>,
|
||||
//tx: mpsc::UnboundedSender<Event<Key>>,
|
||||
pub abort_tx: mpsc::UnboundedSender<()>,
|
||||
@ -99,7 +99,7 @@ async fn poll_event(timeout: Duration) -> bool {
|
||||
}
|
||||
|
||||
impl EventLoop {
|
||||
pub(crate) fn new(tick_rate: f64, init: bool) -> Self {
|
||||
pub fn new(tick_rate: f64, init: bool) -> Self {
|
||||
let (tx, rx) = mpsc::unbounded_channel();
|
||||
let tx_c = tx.clone();
|
||||
let tick_interval =
|
||||
@ -162,7 +162,7 @@ impl EventLoop {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn convert_raw_event_to_key(event: KeyEvent) -> Key {
|
||||
pub fn convert_raw_event_to_key(event: KeyEvent) -> Key {
|
||||
match event.code {
|
||||
Backspace => match event.modifiers {
|
||||
KeyModifiers::CONTROL => Key::CtrlBackspace,
|
||||
|
@ -1,7 +1,7 @@
|
||||
use parking_lot::Mutex;
|
||||
use std::ops::DerefMut;
|
||||
|
||||
pub(crate) struct LazyMutex<T> {
|
||||
pub struct LazyMutex<T> {
|
||||
inner: Mutex<Option<T>>,
|
||||
init: fn() -> T,
|
||||
}
|
||||
@ -14,7 +14,7 @@ impl<T> LazyMutex<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn lock(&self) -> impl DerefMut<Target = T> + '_ {
|
||||
pub fn lock(&self) -> impl DerefMut<Target = T> + '_ {
|
||||
parking_lot::MutexGuard::map(self.inner.lock(), |val| {
|
||||
val.get_or_insert_with(self.init)
|
||||
})
|
||||
|
@ -8,7 +8,7 @@ lazy_static::lazy_static! {
|
||||
pub static ref LOG_FILE: String = format!("{}.log", env!("CARGO_PKG_NAME"));
|
||||
}
|
||||
|
||||
pub(crate) fn init() -> Result<()> {
|
||||
pub fn init() -> Result<()> {
|
||||
let directory = config::get_data_dir();
|
||||
std::fs::create_dir_all(directory.clone())?;
|
||||
let log_path = directory.join(LOG_FILE.clone());
|
||||
|
@ -20,7 +20,7 @@ mod fuzzy;
|
||||
mod logging;
|
||||
mod previewers;
|
||||
mod render;
|
||||
pub mod television;
|
||||
mod television;
|
||||
mod tui;
|
||||
mod ui;
|
||||
mod utils;
|
||||
@ -55,7 +55,7 @@ async fn main() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn is_readable_stdin() -> bool {
|
||||
pub fn is_readable_stdin() -> bool {
|
||||
use std::io::IsTerminal;
|
||||
|
||||
#[cfg(unix)]
|
||||
|
@ -9,15 +9,15 @@ mod env;
|
||||
mod files;
|
||||
|
||||
// previewer types
|
||||
pub(crate) use basic::BasicPreviewer;
|
||||
pub(crate) use directory::DirectoryPreviewer;
|
||||
pub(crate) use env::EnvVarPreviewer;
|
||||
pub(crate) use files::FilePreviewer;
|
||||
pub use basic::BasicPreviewer;
|
||||
pub use directory::DirectoryPreviewer;
|
||||
pub use env::EnvVarPreviewer;
|
||||
pub use files::FilePreviewer;
|
||||
//use ratatui_image::protocol::StatefulProtocol;
|
||||
use syntect::highlighting::Style;
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, Default)]
|
||||
pub(crate) enum PreviewType {
|
||||
pub enum PreviewType {
|
||||
#[default]
|
||||
Basic,
|
||||
Directory,
|
||||
@ -26,10 +26,10 @@ pub(crate) enum PreviewType {
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) enum PreviewContent {
|
||||
pub enum PreviewContent {
|
||||
Empty,
|
||||
FileTooLarge,
|
||||
HighlightedText(Vec<Vec<(Style, String)>>),
|
||||
SyntectHighlightedText(Vec<Vec<(Style, String)>>),
|
||||
//Image(Box<dyn StatefulProtocol>),
|
||||
Loading,
|
||||
NotSupported,
|
||||
@ -47,7 +47,7 @@ pub const FILE_TOO_LARGE_MSG: &str = "File too large";
|
||||
/// - `title`: The title of the preview.
|
||||
/// - `content`: The content of the preview.
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct Preview {
|
||||
pub struct Preview {
|
||||
pub title: String,
|
||||
pub content: PreviewContent,
|
||||
}
|
||||
@ -62,19 +62,24 @@ impl Default for Preview {
|
||||
}
|
||||
|
||||
impl Preview {
|
||||
pub(crate) fn new(title: String, content: PreviewContent) -> Self {
|
||||
pub fn new(title: String, content: PreviewContent) -> Self {
|
||||
Preview { title, content }
|
||||
}
|
||||
|
||||
pub(crate) fn total_lines(&self) -> u16 {
|
||||
pub fn total_lines(&self) -> u16 {
|
||||
match &self.content {
|
||||
PreviewContent::HighlightedText(lines) => lines.len() as u16,
|
||||
PreviewContent::SyntectHighlightedText(lines) => {
|
||||
lines.len().try_into().unwrap_or(u16::MAX)
|
||||
}
|
||||
PreviewContent::PlainText(lines) => {
|
||||
lines.len().try_into().unwrap_or(u16::MAX)
|
||||
}
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct Previewer {
|
||||
pub struct Previewer {
|
||||
basic: BasicPreviewer,
|
||||
directory: DirectoryPreviewer,
|
||||
file: FilePreviewer,
|
||||
@ -82,7 +87,7 @@ pub(crate) struct Previewer {
|
||||
}
|
||||
|
||||
impl Previewer {
|
||||
pub(crate) fn new() -> Self {
|
||||
pub fn new() -> Self {
|
||||
Previewer {
|
||||
basic: BasicPreviewer::new(),
|
||||
directory: DirectoryPreviewer::new(),
|
||||
|
@ -3,14 +3,14 @@ use std::sync::Arc;
|
||||
use crate::entry::Entry;
|
||||
use crate::previewers::{Preview, PreviewContent};
|
||||
|
||||
pub(crate) struct BasicPreviewer {}
|
||||
pub struct BasicPreviewer {}
|
||||
|
||||
impl BasicPreviewer {
|
||||
pub(crate) fn new() -> Self {
|
||||
pub fn new() -> Self {
|
||||
BasicPreviewer {}
|
||||
}
|
||||
|
||||
pub(crate) fn preview(&self, entry: &Entry) -> Arc<Preview> {
|
||||
pub fn preview(&self, entry: &Entry) -> Arc<Preview> {
|
||||
Arc::new(Preview {
|
||||
title: entry.name.clone(),
|
||||
content: PreviewContent::PlainTextWrapped(entry.name.clone()),
|
||||
|
@ -55,7 +55,7 @@ where
|
||||
T: Eq + std::hash::Hash + Clone + std::fmt::Debug,
|
||||
{
|
||||
/// Create a new `RingSet` with the given capacity.
|
||||
pub(crate) fn with_capacity(capacity: usize) -> Self {
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
RingSet {
|
||||
ring_buffer: VecDeque::with_capacity(capacity),
|
||||
known_keys: HashSet::with_capacity(capacity),
|
||||
@ -66,7 +66,7 @@ where
|
||||
/// Push a new item to the back of the buffer, removing the oldest item if the buffer is full.
|
||||
/// Returns the item that was removed, if any.
|
||||
/// If the item is already in the buffer, do nothing and return None.
|
||||
pub(crate) fn push(&mut self, item: T) -> Option<T> {
|
||||
pub fn push(&mut self, item: T) -> Option<T> {
|
||||
// If the key is already in the buffer, do nothing
|
||||
if self.contains(&item) {
|
||||
debug!("Key already in ring buffer: {:?}", item);
|
||||
@ -107,28 +107,28 @@ const DEFAULT_PREVIEW_CACHE_SIZE: usize = 100;
|
||||
|
||||
/// A cache for previews.
|
||||
/// The cache is implemented as an LRU cache with a fixed size.
|
||||
pub(crate) struct PreviewCache {
|
||||
pub struct PreviewCache {
|
||||
entries: HashMap<String, Arc<Preview>>,
|
||||
ring_set: RingSet<String>,
|
||||
}
|
||||
|
||||
impl PreviewCache {
|
||||
/// Create a new preview cache with the given capacity.
|
||||
pub(crate) fn new(capacity: usize) -> Self {
|
||||
pub fn new(capacity: usize) -> Self {
|
||||
PreviewCache {
|
||||
entries: HashMap::new(),
|
||||
ring_set: RingSet::with_capacity(capacity),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get(&self, key: &str) -> Option<Arc<Preview>> {
|
||||
pub fn get(&self, key: &str) -> Option<Arc<Preview>> {
|
||||
self.entries.get(key).cloned()
|
||||
}
|
||||
|
||||
/// Insert a new preview into the cache.
|
||||
/// If the cache is full, the oldest entry will be removed.
|
||||
/// If the key is already in the cache, the preview will be updated.
|
||||
pub(crate) fn insert(&mut self, key: String, preview: Arc<Preview>) {
|
||||
pub fn insert(&mut self, key: String, preview: Arc<Preview>) {
|
||||
debug!("Inserting preview into cache: {}", key);
|
||||
self.entries.insert(key.clone(), preview.clone());
|
||||
if let Some(oldest_key) = self.ring_set.push(key) {
|
||||
@ -138,7 +138,7 @@ impl PreviewCache {
|
||||
}
|
||||
|
||||
/// Get the preview for the given key, or insert a new preview if it doesn't exist.
|
||||
pub(crate) fn get_or_insert<F>(
|
||||
pub fn get_or_insert<F>(
|
||||
&mut self,
|
||||
key: String,
|
||||
f: F,
|
||||
|
@ -2,51 +2,78 @@ use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
|
||||
use devicons::FileIcon;
|
||||
use termtree::Tree;
|
||||
|
||||
use crate::entry::Entry;
|
||||
|
||||
use crate::previewers::cache::PreviewCache;
|
||||
use crate::previewers::{Preview, PreviewContent};
|
||||
use crate::utils::files::walk_builder;
|
||||
|
||||
pub(crate) struct DirectoryPreviewer {
|
||||
pub struct DirectoryPreviewer {
|
||||
cache: PreviewCache,
|
||||
}
|
||||
|
||||
impl DirectoryPreviewer {
|
||||
pub(crate) fn new() -> Self {
|
||||
pub fn new() -> Self {
|
||||
DirectoryPreviewer {
|
||||
cache: PreviewCache::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn preview(&mut self, entry: &Entry) -> Arc<Preview> {
|
||||
pub fn preview(&mut self, entry: &Entry) -> Arc<Preview> {
|
||||
if let Some(preview) = self.cache.get(&entry.name) {
|
||||
return preview;
|
||||
}
|
||||
let preview = Arc::new(build_preview(entry));
|
||||
let preview = Arc::new(build_tree_preview(entry));
|
||||
self.cache.insert(entry.name.clone(), preview.clone());
|
||||
preview
|
||||
}
|
||||
}
|
||||
|
||||
fn build_preview(entry: &Entry) -> Preview {
|
||||
let dir_path = Path::new(&entry.name);
|
||||
// get the list of files in the directory
|
||||
let mut lines = vec![];
|
||||
if let Ok(entries) = std::fs::read_dir(dir_path) {
|
||||
for entry in entries.flatten() {
|
||||
if let Ok(file_name) = entry.file_name().into_string() {
|
||||
lines.push(format!(
|
||||
"{} {}",
|
||||
FileIcon::from(&file_name),
|
||||
&file_name
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn build_tree_preview(entry: &Entry) -> Preview {
|
||||
let path = Path::new(&entry.name);
|
||||
let tree = tree(path).unwrap();
|
||||
let tree_string = tree.to_string();
|
||||
Preview {
|
||||
title: entry.name.clone(),
|
||||
content: PreviewContent::PlainText(lines),
|
||||
content: PreviewContent::PlainText(
|
||||
tree_string
|
||||
.lines()
|
||||
.map(std::borrow::ToOwned::to_owned)
|
||||
.collect(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn label<P: AsRef<Path>>(p: P, strip: &str) -> String {
|
||||
//let path = p.as_ref().file_name().unwrap().to_str().unwrap().to_owned();
|
||||
let path = p.as_ref().strip_prefix(strip).unwrap();
|
||||
let icon = FileIcon::from(&path);
|
||||
format!("{} {}", icon, path.display())
|
||||
}
|
||||
|
||||
/// PERF: (urgent) change to use the ignore crate here
|
||||
fn tree<P: AsRef<Path>>(p: P) -> std::io::Result<Tree<String>> {
|
||||
let result = std::fs::read_dir(&p)?
|
||||
.filter_map(std::result::Result::ok)
|
||||
.fold(
|
||||
Tree::new(label(
|
||||
p.as_ref(),
|
||||
p.as_ref().parent().unwrap().to_str().unwrap(),
|
||||
)),
|
||||
|mut root, entry| {
|
||||
let m = entry.metadata().unwrap();
|
||||
if m.is_dir() {
|
||||
root.push(tree(entry.path()).unwrap());
|
||||
} else {
|
||||
root.push(Tree::new(label(
|
||||
entry.path(),
|
||||
p.as_ref().to_str().unwrap(),
|
||||
)));
|
||||
}
|
||||
root
|
||||
},
|
||||
);
|
||||
Ok(result)
|
||||
}
|
||||
|
@ -4,18 +4,18 @@ use std::sync::Arc;
|
||||
use crate::entry;
|
||||
use crate::previewers::{Preview, PreviewContent};
|
||||
|
||||
pub(crate) struct EnvVarPreviewer {
|
||||
pub struct EnvVarPreviewer {
|
||||
cache: HashMap<entry::Entry, Arc<Preview>>,
|
||||
}
|
||||
|
||||
impl EnvVarPreviewer {
|
||||
pub(crate) fn new() -> Self {
|
||||
pub fn new() -> Self {
|
||||
EnvVarPreviewer {
|
||||
cache: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn preview(&mut self, entry: &entry::Entry) -> Arc<Preview> {
|
||||
pub fn preview(&mut self, entry: &entry::Entry) -> Arc<Preview> {
|
||||
// check if we have that preview in the cache
|
||||
if let Some(preview) = self.cache.get(entry) {
|
||||
return preview.clone();
|
||||
|
@ -12,7 +12,6 @@ use syntect::{
|
||||
highlighting::{Style, Theme, ThemeSet},
|
||||
parsing::SyntaxSet,
|
||||
};
|
||||
//use tracing::{debug, info, warn};
|
||||
use tracing::{debug, warn};
|
||||
|
||||
use crate::entry;
|
||||
@ -26,7 +25,7 @@ use crate::utils::strings::{
|
||||
|
||||
use super::cache::PreviewCache;
|
||||
|
||||
pub(crate) struct FilePreviewer {
|
||||
pub struct FilePreviewer {
|
||||
cache: Arc<Mutex<PreviewCache>>,
|
||||
syntax_set: Arc<SyntaxSet>,
|
||||
syntax_theme: Arc<Theme>,
|
||||
@ -34,7 +33,7 @@ pub(crate) struct FilePreviewer {
|
||||
}
|
||||
|
||||
impl FilePreviewer {
|
||||
pub(crate) fn new() -> Self {
|
||||
pub fn new() -> Self {
|
||||
let syntax_set = SyntaxSet::load_defaults_nonewlines();
|
||||
let theme_set = ThemeSet::load_defaults();
|
||||
//info!("getting image picker");
|
||||
@ -184,7 +183,9 @@ impl FilePreviewer {
|
||||
entry_c.name.clone(),
|
||||
Arc::new(Preview::new(
|
||||
entry_c.name,
|
||||
PreviewContent::HighlightedText(highlighted_lines),
|
||||
PreviewContent::SyntectHighlightedText(
|
||||
highlighted_lines,
|
||||
),
|
||||
)),
|
||||
);
|
||||
debug!("Inserted highlighted preview into cache");
|
||||
@ -292,6 +293,7 @@ fn file_too_large(title: &str) -> Arc<Preview> {
|
||||
))
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn loading(title: &str) -> Arc<Preview> {
|
||||
Arc::new(Preview::new(title.to_string(), PreviewContent::Loading))
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ use crate::television::Television;
|
||||
use crate::{action::Action, config::Config, tui::Tui};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum RenderingTask {
|
||||
pub enum RenderingTask {
|
||||
ClearScreen,
|
||||
Render,
|
||||
Resize(u16, u16),
|
||||
|
@ -42,7 +42,7 @@ enum Pane {
|
||||
|
||||
static PANES: [Pane; 3] = [Pane::Input, Pane::Results, Pane::Preview];
|
||||
|
||||
pub(crate) struct Television {
|
||||
pub struct Television {
|
||||
action_tx: Option<UnboundedSender<Action>>,
|
||||
config: Config,
|
||||
channel: Box<dyn TelevisionChannel>,
|
||||
@ -54,8 +54,8 @@ pub(crate) struct Television {
|
||||
picker_view_offset: usize,
|
||||
results_area_height: u32,
|
||||
previewer: Previewer,
|
||||
pub(crate) preview_scroll: Option<u16>,
|
||||
pub(crate) preview_pane_height: u16,
|
||||
pub preview_scroll: Option<u16>,
|
||||
pub preview_pane_height: u16,
|
||||
current_preview_total_lines: u16,
|
||||
/// A cache for meta paragraphs (i.e. previews like "Not Supported", etc.).
|
||||
///
|
||||
@ -63,15 +63,14 @@ pub(crate) struct Television {
|
||||
/// preview pane. This is a little extra security to ensure meta previews
|
||||
/// are rendered correctly even when resizing the terminal while still
|
||||
/// benefiting from a cache mechanism.
|
||||
pub(crate) meta_paragraph_cache:
|
||||
HashMap<(String, u16, u16), Paragraph<'static>>,
|
||||
pub meta_paragraph_cache: HashMap<(String, u16, u16), Paragraph<'static>>,
|
||||
spinner: Spinner,
|
||||
spinner_state: SpinnerState,
|
||||
}
|
||||
|
||||
impl Television {
|
||||
#[must_use]
|
||||
pub(crate) fn new(cli_channel: CliTvChannel) -> Self {
|
||||
pub fn new(cli_channel: CliTvChannel) -> Self {
|
||||
let mut tv_channel = cli_channel.to_channel();
|
||||
tv_channel.find(EMPTY_STRING);
|
||||
|
||||
@ -106,13 +105,13 @@ impl Television {
|
||||
#[must_use]
|
||||
/// # Panics
|
||||
/// This method will panic if the index doesn't fit into an u32.
|
||||
pub(crate) fn get_selected_entry(&self) -> Option<Entry> {
|
||||
pub fn get_selected_entry(&self) -> Option<Entry> {
|
||||
self.picker_state
|
||||
.selected()
|
||||
.and_then(|i| self.channel.get_result(u32::try_from(i).unwrap()))
|
||||
}
|
||||
|
||||
pub(crate) fn select_prev_entry(&mut self) {
|
||||
pub fn select_prev_entry(&mut self) {
|
||||
if self.channel.result_count() == 0 {
|
||||
return;
|
||||
}
|
||||
@ -142,7 +141,7 @@ impl Television {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn select_next_entry(&mut self) {
|
||||
pub fn select_next_entry(&mut self) {
|
||||
if self.channel.result_count() == 0 {
|
||||
return;
|
||||
}
|
||||
@ -175,7 +174,7 @@ impl Television {
|
||||
self.preview_scroll = None;
|
||||
}
|
||||
|
||||
pub(crate) fn scroll_preview_down(&mut self, offset: u16) {
|
||||
pub fn scroll_preview_down(&mut self, offset: u16) {
|
||||
if self.preview_scroll.is_none() {
|
||||
self.preview_scroll = Some(0);
|
||||
}
|
||||
@ -189,7 +188,7 @@ impl Television {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn scroll_preview_up(&mut self, offset: u16) {
|
||||
pub fn scroll_preview_up(&mut self, offset: u16) {
|
||||
if let Some(scroll) = self.preview_scroll {
|
||||
self.preview_scroll = Some(scroll.saturating_sub(offset));
|
||||
}
|
||||
@ -202,13 +201,13 @@ impl Television {
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn next_pane(&mut self) {
|
||||
pub fn next_pane(&mut self) {
|
||||
let current_index = self.get_current_pane_index();
|
||||
let next_index = (current_index + 1) % PANES.len();
|
||||
self.current_pane = PANES[next_index];
|
||||
}
|
||||
|
||||
pub(crate) fn previous_pane(&mut self) {
|
||||
pub fn previous_pane(&mut self) {
|
||||
let current_index = self.get_current_pane_index();
|
||||
let previous_index = if current_index == 0 {
|
||||
PANES.len() - 1
|
||||
@ -227,7 +226,7 @@ impl Television {
|
||||
/// ┌───────────────────┐│ │
|
||||
/// │ Search x ││ │
|
||||
/// └───────────────────┘└─────────────┘
|
||||
pub(crate) fn move_to_pane_on_top(&mut self) {
|
||||
pub fn move_to_pane_on_top(&mut self) {
|
||||
if self.current_pane == Pane::Input {
|
||||
self.current_pane = Pane::Results;
|
||||
}
|
||||
@ -242,7 +241,7 @@ impl Television {
|
||||
/// ┌───────────────────┐│ │
|
||||
/// │ Search ││ │
|
||||
/// └───────────────────┘└─────────────┘
|
||||
pub(crate) fn move_to_pane_below(&mut self) {
|
||||
pub fn move_to_pane_below(&mut self) {
|
||||
if self.current_pane == Pane::Results {
|
||||
self.current_pane = Pane::Input;
|
||||
}
|
||||
@ -257,7 +256,7 @@ impl Television {
|
||||
/// ┌───────────────────┐│ │
|
||||
/// │ Search x ││ │
|
||||
/// └───────────────────┘└─────────────┘
|
||||
pub(crate) fn move_to_pane_right(&mut self) {
|
||||
pub fn move_to_pane_right(&mut self) {
|
||||
match self.current_pane {
|
||||
Pane::Results | Pane::Input => {
|
||||
self.current_pane = Pane::Preview;
|
||||
@ -275,14 +274,14 @@ impl Television {
|
||||
/// ┌───────────────────┐│ │
|
||||
/// │ Search ││ │
|
||||
/// └───────────────────┘└─────────────┘
|
||||
pub(crate) fn move_to_pane_left(&mut self) {
|
||||
pub fn move_to_pane_left(&mut self) {
|
||||
if self.current_pane == Pane::Preview {
|
||||
self.current_pane = Pane::Results;
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub(crate) fn is_input_focused(&self) -> bool {
|
||||
pub fn is_input_focused(&self) -> bool {
|
||||
Pane::Input == self.current_pane
|
||||
}
|
||||
}
|
||||
@ -302,7 +301,7 @@ impl Television {
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Result<()>` - An Ok result or an error.
|
||||
pub(crate) fn register_action_handler(
|
||||
pub fn register_action_handler(
|
||||
&mut self,
|
||||
tx: UnboundedSender<Action>,
|
||||
) -> Result<()> {
|
||||
@ -319,10 +318,7 @@ impl Television {
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Result<()>` - An Ok result or an error.
|
||||
pub(crate) fn register_config_handler(
|
||||
&mut self,
|
||||
config: Config,
|
||||
) -> Result<()> {
|
||||
pub fn register_config_handler(&mut self, config: Config) -> Result<()> {
|
||||
self.config = config;
|
||||
Ok(())
|
||||
}
|
||||
@ -411,11 +407,7 @@ impl Television {
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Result<()>` - An Ok result or an error.
|
||||
pub(crate) fn draw(
|
||||
&mut self,
|
||||
frame: &mut Frame,
|
||||
area: Rect,
|
||||
) -> Result<()> {
|
||||
pub fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
|
||||
let layout = Layout::all_panes_centered(Dimensions::default(), area);
|
||||
//let layout =
|
||||
//Layout::results_only_centered(Dimensions::new(40, 60), area);
|
||||
|
@ -16,7 +16,7 @@ use tokio::task::JoinHandle;
|
||||
use tracing::debug;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) struct Tui<W>
|
||||
pub struct Tui<W>
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
@ -30,7 +30,7 @@ impl<W> Tui<W>
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
pub(crate) fn new(writer: W) -> Result<Self> {
|
||||
pub fn new(writer: W) -> Result<Self> {
|
||||
Ok(Self {
|
||||
task: tokio::spawn(async {}),
|
||||
frame_rate: 60.0,
|
||||
@ -38,16 +38,16 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn frame_rate(mut self, frame_rate: f64) -> Self {
|
||||
pub fn frame_rate(mut self, frame_rate: f64) -> Self {
|
||||
self.frame_rate = frame_rate;
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn size(&self) -> Result<Size> {
|
||||
pub fn size(&self) -> Result<Size> {
|
||||
Ok(self.terminal.size()?)
|
||||
}
|
||||
|
||||
pub(crate) fn enter(&mut self) -> Result<()> {
|
||||
pub fn enter(&mut self) -> Result<()> {
|
||||
enable_raw_mode()?;
|
||||
let mut buffered_stderr = LineWriter::new(stderr());
|
||||
execute!(buffered_stderr, EnterAlternateScreen)?;
|
||||
@ -56,7 +56,7 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn exit(&mut self) -> Result<()> {
|
||||
pub fn exit(&mut self) -> Result<()> {
|
||||
if is_raw_mode_enabled()? {
|
||||
debug!("Exiting terminal");
|
||||
|
||||
@ -69,14 +69,14 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn suspend(&mut self) -> Result<()> {
|
||||
pub fn suspend(&mut self) -> Result<()> {
|
||||
self.exit()?;
|
||||
#[cfg(not(windows))]
|
||||
signal_hook::low_level::raise(signal_hook::consts::signal::SIGTSTP)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn resume(&mut self) -> Result<()> {
|
||||
pub fn resume(&mut self) -> Result<()> {
|
||||
self.enter()?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ pub mod spinner;
|
||||
//const DEFAULT_PREVIEW_GUTTER_FG: Color = Color::Rgb(70, 70, 70);
|
||||
//const DEFAULT_PREVIEW_GUTTER_SELECTED_FG: Color = Color::Rgb(255, 150, 150);
|
||||
|
||||
pub(crate) fn get_border_style(focused: bool) -> Style {
|
||||
pub fn get_border_style(focused: bool) -> Style {
|
||||
Style::default().fg(Color::Blue)
|
||||
|
||||
// NOTE: do we want to change the border color based on focus? Are we
|
||||
|
@ -6,7 +6,7 @@ pub mod backend;
|
||||
/// Different backends can be used to convert events into requests.
|
||||
#[allow(clippy::module_name_repetitions)]
|
||||
#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
|
||||
pub(crate) enum InputRequest {
|
||||
pub enum InputRequest {
|
||||
SetCursor(usize),
|
||||
InsertChar(char),
|
||||
GoToPrevChar,
|
||||
@ -24,7 +24,7 @@ pub(crate) enum InputRequest {
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
|
||||
pub(crate) struct StateChanged {
|
||||
pub struct StateChanged {
|
||||
pub value: bool,
|
||||
pub cursor: bool,
|
||||
}
|
||||
@ -45,7 +45,7 @@ pub type InputResponse = Option<StateChanged>;
|
||||
/// assert_eq!(input.to_string(), "Hello World");
|
||||
/// ```
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub(crate) struct Input {
|
||||
pub struct Input {
|
||||
value: String,
|
||||
cursor: usize,
|
||||
}
|
||||
@ -53,14 +53,14 @@ pub(crate) struct Input {
|
||||
impl Input {
|
||||
/// Initialize a new instance with a given value
|
||||
/// Cursor will be set to the given value's length.
|
||||
pub(crate) fn new(value: String) -> Self {
|
||||
pub fn new(value: String) -> Self {
|
||||
let len = value.chars().count();
|
||||
Self { value, cursor: len }
|
||||
}
|
||||
|
||||
/// Set the value manually.
|
||||
/// Cursor will be set to the given value's length.
|
||||
pub(crate) fn with_value(mut self, value: String) -> Self {
|
||||
pub fn with_value(mut self, value: String) -> Self {
|
||||
self.cursor = value.chars().count();
|
||||
self.value = value;
|
||||
self
|
||||
@ -68,20 +68,20 @@ impl Input {
|
||||
|
||||
/// Set the cursor manually.
|
||||
/// If the input is larger than the value length, it'll be auto adjusted.
|
||||
pub(crate) fn with_cursor(mut self, cursor: usize) -> Self {
|
||||
pub fn with_cursor(mut self, cursor: usize) -> Self {
|
||||
self.cursor = cursor.min(self.value.chars().count());
|
||||
self
|
||||
}
|
||||
|
||||
// Reset the cursor and value to default
|
||||
pub(crate) fn reset(&mut self) {
|
||||
pub fn reset(&mut self) {
|
||||
self.cursor = Default::default();
|
||||
self.value = String::default();
|
||||
}
|
||||
|
||||
/// Handle request and emit response.
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub(crate) fn handle(&mut self, req: InputRequest) -> InputResponse {
|
||||
pub fn handle(&mut self, req: InputRequest) -> InputResponse {
|
||||
use InputRequest::{
|
||||
DeleteLine, DeleteNextChar, DeleteNextWord, DeletePrevChar,
|
||||
DeletePrevWord, DeleteTillEnd, GoToEnd, GoToNextChar,
|
||||
@ -328,17 +328,17 @@ impl Input {
|
||||
}
|
||||
|
||||
/// Get a reference to the current value.
|
||||
pub(crate) fn value(&self) -> &str {
|
||||
pub fn value(&self) -> &str {
|
||||
self.value.as_str()
|
||||
}
|
||||
|
||||
/// Get the currect cursor placement.
|
||||
pub(crate) fn cursor(&self) -> usize {
|
||||
pub fn cursor(&self) -> usize {
|
||||
self.cursor
|
||||
}
|
||||
|
||||
/// Get the current cursor position with account for multispace characters.
|
||||
pub(crate) fn visual_cursor(&self) -> usize {
|
||||
pub fn visual_cursor(&self) -> usize {
|
||||
if self.cursor == 0 {
|
||||
return 0;
|
||||
}
|
||||
@ -356,7 +356,7 @@ impl Input {
|
||||
}
|
||||
|
||||
/// Get the scroll position with account for multispace characters.
|
||||
pub(crate) fn visual_scroll(&self, width: usize) -> usize {
|
||||
pub fn visual_scroll(&self, width: usize) -> usize {
|
||||
let scroll = self.visual_cursor().max(width) - width;
|
||||
let mut uscroll = 0;
|
||||
let mut chars = self.value().chars();
|
||||
|
@ -5,7 +5,7 @@ use ratatui::crossterm::event::{
|
||||
|
||||
/// Converts crossterm event into input requests.
|
||||
/// TODO: make these keybindings configurable.
|
||||
pub(crate) fn to_input_request(evt: &CrosstermEvent) -> Option<InputRequest> {
|
||||
pub fn to_input_request(evt: &CrosstermEvent) -> Option<InputRequest> {
|
||||
use InputRequest::*;
|
||||
use KeyCode::*;
|
||||
match evt {
|
||||
|
@ -1,13 +1,13 @@
|
||||
use ratatui::layout;
|
||||
use ratatui::layout::{Constraint, Direction, Rect};
|
||||
|
||||
pub(crate) struct Dimensions {
|
||||
pub struct Dimensions {
|
||||
pub x: u16,
|
||||
pub y: u16,
|
||||
}
|
||||
|
||||
impl Dimensions {
|
||||
pub(crate) fn new(x: u16, y: u16) -> Self {
|
||||
pub fn new(x: u16, y: u16) -> Self {
|
||||
Self { x, y }
|
||||
}
|
||||
}
|
||||
@ -18,7 +18,7 @@ impl Default for Dimensions {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct Layout {
|
||||
pub struct Layout {
|
||||
pub results: Rect,
|
||||
pub input: Rect,
|
||||
pub preview_title: Option<Rect>,
|
||||
@ -26,7 +26,7 @@ pub(crate) struct Layout {
|
||||
}
|
||||
|
||||
impl Layout {
|
||||
pub(crate) fn new(
|
||||
pub fn new(
|
||||
results: Rect,
|
||||
input: Rect,
|
||||
preview_title: Option<Rect>,
|
||||
@ -42,7 +42,7 @@ impl Layout {
|
||||
|
||||
/// TODO: add diagram
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn all_panes_centered(
|
||||
pub fn all_panes_centered(
|
||||
dimensions: Dimensions,
|
||||
area: Rect,
|
||||
) -> Self {
|
||||
@ -78,7 +78,7 @@ impl Layout {
|
||||
|
||||
/// TODO: add diagram
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn results_only_centered(
|
||||
pub fn results_only_centered(
|
||||
dimensions: Dimensions,
|
||||
area: Rect,
|
||||
) -> Self {
|
||||
|
@ -20,7 +20,7 @@ impl Television {
|
||||
const FILL_CHAR_SLANTED: char = '╱';
|
||||
const FILL_CHAR_EMPTY: char = ' ';
|
||||
|
||||
pub(crate) fn build_preview_paragraph<'b>(
|
||||
pub fn build_preview_paragraph<'b>(
|
||||
&'b mut self,
|
||||
preview_block: Block<'b>,
|
||||
inner: Rect,
|
||||
@ -76,7 +76,7 @@ impl Television {
|
||||
.block(preview_block)
|
||||
.wrap(Wrap { trim: true })
|
||||
}
|
||||
PreviewContent::HighlightedText(highlighted_lines) => {
|
||||
PreviewContent::SyntectHighlightedText(highlighted_lines) => {
|
||||
compute_paragraph_from_highlighted_lines(
|
||||
highlighted_lines,
|
||||
target_line.map(|l| l as usize),
|
||||
@ -119,7 +119,7 @@ impl Television {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn maybe_init_preview_scroll(
|
||||
pub fn maybe_init_preview_scroll(
|
||||
&mut self,
|
||||
target_line: Option<u16>,
|
||||
height: u16,
|
||||
@ -130,7 +130,7 @@ impl Television {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn build_meta_preview_paragraph<'a>(
|
||||
pub fn build_meta_preview_paragraph<'a>(
|
||||
&mut self,
|
||||
inner: Rect,
|
||||
message: &str,
|
||||
|
@ -9,7 +9,7 @@ const DEFAULT_RESULT_NAME_FG: Color = Color::Blue;
|
||||
const DEFAULT_RESULT_PREVIEW_FG: Color = Color::Rgb(150, 150, 150);
|
||||
const DEFAULT_RESULT_LINE_NUMBER_FG: Color = Color::Yellow;
|
||||
|
||||
pub(crate) fn build_results_list<'a, 'b>(
|
||||
pub fn build_results_list<'a, 'b>(
|
||||
results_block: Block<'b>,
|
||||
entries: &'a [Entry],
|
||||
) -> List<'a>
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::collections::HashSet;
|
||||
use std::path::Path;
|
||||
|
||||
use ignore::{types::TypesBuilder, WalkBuilder};
|
||||
use ignore::{overrides::Override, types::TypesBuilder, WalkBuilder};
|
||||
use infer::Infer;
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
@ -11,7 +11,11 @@ lazy_static::lazy_static! {
|
||||
pub static ref DEFAULT_NUM_THREADS: usize = default_num_threads().into();
|
||||
}
|
||||
|
||||
pub(crate) fn walk_builder(path: &Path, n_threads: usize) -> WalkBuilder {
|
||||
pub fn walk_builder(
|
||||
path: &Path,
|
||||
n_threads: usize,
|
||||
overrides: Option<Override>,
|
||||
) -> WalkBuilder {
|
||||
let mut builder = WalkBuilder::new(path);
|
||||
|
||||
// ft-based filtering
|
||||
@ -20,22 +24,25 @@ pub(crate) fn walk_builder(path: &Path, n_threads: usize) -> WalkBuilder {
|
||||
builder.types(types_builder.build().unwrap());
|
||||
|
||||
builder.threads(n_threads);
|
||||
if let Some(ov) = overrides {
|
||||
builder.overrides(ov);
|
||||
}
|
||||
builder
|
||||
}
|
||||
|
||||
pub(crate) fn get_file_size(path: &Path) -> Option<u64> {
|
||||
pub fn get_file_size(path: &Path) -> Option<u64> {
|
||||
std::fs::metadata(path).ok().map(|m| m.len())
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum FileType {
|
||||
pub enum FileType {
|
||||
Text,
|
||||
Image,
|
||||
Other,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
pub(crate) fn is_not_text(bytes: &[u8]) -> Option<bool> {
|
||||
pub fn is_not_text(bytes: &[u8]) -> Option<bool> {
|
||||
let infer = Infer::new();
|
||||
match infer.get(bytes) {
|
||||
Some(t) => {
|
||||
@ -56,11 +63,11 @@ pub(crate) fn is_not_text(bytes: &[u8]) -> Option<bool> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn is_valid_utf8(bytes: &[u8]) -> bool {
|
||||
pub fn is_valid_utf8(bytes: &[u8]) -> bool {
|
||||
std::str::from_utf8(bytes).is_ok()
|
||||
}
|
||||
|
||||
pub(crate) fn is_known_text_extension(path: &Path) -> bool {
|
||||
pub fn is_known_text_extension(path: &Path) -> bool {
|
||||
path.extension()
|
||||
.and_then(|ext| ext.to_str())
|
||||
.is_some_and(|ext| KNOWN_TEXT_FILE_EXTENSIONS.contains(ext))
|
||||
|
@ -1,4 +1,4 @@
|
||||
pub(crate) fn sep_name_and_value_indices(
|
||||
pub fn sep_name_and_value_indices(
|
||||
indices: &mut Vec<u32>,
|
||||
name_len: u32,
|
||||
) -> (Vec<u32>, Vec<u32>, bool, bool) {
|
||||
|
@ -1,7 +1,7 @@
|
||||
use lazy_static::lazy_static;
|
||||
use std::fmt::Write;
|
||||
|
||||
pub(crate) fn next_char_boundary(s: &str, start: usize) -> usize {
|
||||
pub fn next_char_boundary(s: &str, start: usize) -> usize {
|
||||
let mut i = start;
|
||||
while !s.is_char_boundary(i) {
|
||||
i += 1;
|
||||
@ -9,7 +9,7 @@ pub(crate) fn next_char_boundary(s: &str, start: usize) -> usize {
|
||||
i
|
||||
}
|
||||
|
||||
pub(crate) fn prev_char_boundary(s: &str, start: usize) -> usize {
|
||||
pub fn prev_char_boundary(s: &str, start: usize) -> usize {
|
||||
let mut i = start;
|
||||
while !s.is_char_boundary(i) {
|
||||
i -= 1;
|
||||
@ -17,7 +17,7 @@ pub(crate) fn prev_char_boundary(s: &str, start: usize) -> usize {
|
||||
i
|
||||
}
|
||||
|
||||
pub(crate) fn slice_at_char_boundaries(
|
||||
pub fn slice_at_char_boundaries(
|
||||
s: &str,
|
||||
start_byte_index: usize,
|
||||
end_byte_index: usize,
|
||||
@ -26,7 +26,7 @@ pub(crate) fn slice_at_char_boundaries(
|
||||
..next_char_boundary(s, end_byte_index)]
|
||||
}
|
||||
|
||||
pub(crate) fn slice_up_to_char_boundary(s: &str, byte_index: usize) -> &str {
|
||||
pub fn slice_up_to_char_boundary(s: &str, byte_index: usize) -> &str {
|
||||
let mut char_index = byte_index;
|
||||
while !s.is_char_boundary(char_index) {
|
||||
char_index -= 1;
|
||||
@ -65,7 +65,7 @@ const NULL_CHARACTER: char = '\x00';
|
||||
const UNIT_SEPARATOR_CHARACTER: char = '\u{001F}';
|
||||
const APPLICATION_PROGRAM_COMMAND_CHARACTER: char = '\u{009F}';
|
||||
|
||||
pub(crate) fn replace_nonprintable(input: &[u8], tab_width: usize) -> String {
|
||||
pub fn replace_nonprintable(input: &[u8], tab_width: usize) -> String {
|
||||
let mut output = String::new();
|
||||
|
||||
let mut idx = 0;
|
||||
@ -84,12 +84,10 @@ pub(crate) fn replace_nonprintable(input: &[u8], tab_width: usize) -> String {
|
||||
output.push_str("␊\x0A");
|
||||
}
|
||||
// ASCII control characters from 0x00 to 0x1F
|
||||
NULL_CHARACTER..=UNIT_SEPARATOR_CHARACTER => {
|
||||
output.push(*NULL_SYMBOL)
|
||||
}
|
||||
// control characters from \u{007F} to \u{009F}
|
||||
DELETE_CHARACTER..=APPLICATION_PROGRAM_COMMAND_CHARACTER => {
|
||||
output.push(*NULL_SYMBOL)
|
||||
// + control characters from \u{007F} to \u{009F}
|
||||
NULL_CHARACTER..=UNIT_SEPARATOR_CHARACTER
|
||||
| DELETE_CHARACTER..=APPLICATION_PROGRAM_COMMAND_CHARACTER => {
|
||||
output.push(*NULL_SYMBOL);
|
||||
}
|
||||
// don't print BOMs
|
||||
BOM_CHARACTER => {}
|
||||
@ -115,7 +113,7 @@ pub(crate) fn replace_nonprintable(input: &[u8], tab_width: usize) -> String {
|
||||
/// based on a sample of its contents.
|
||||
pub const PRINTABLE_ASCII_THRESHOLD: f32 = 0.7;
|
||||
|
||||
pub(crate) fn proportion_of_printable_ascii_characters(buffer: &[u8]) -> f32 {
|
||||
pub fn proportion_of_printable_ascii_characters(buffer: &[u8]) -> f32 {
|
||||
let mut printable = 0;
|
||||
for &byte in buffer {
|
||||
if byte > 32 && byte < 127 {
|
||||
@ -127,7 +125,7 @@ pub(crate) fn proportion_of_printable_ascii_characters(buffer: &[u8]) -> f32 {
|
||||
|
||||
const MAX_LINE_LENGTH: usize = 500;
|
||||
|
||||
pub(crate) fn preprocess_line(line: &str) -> String {
|
||||
pub fn preprocess_line(line: &str) -> String {
|
||||
replace_nonprintable(
|
||||
{
|
||||
if line.len() > MAX_LINE_LENGTH {
|
||||
@ -142,7 +140,7 @@ pub(crate) fn preprocess_line(line: &str) -> String {
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn shrink_with_ellipsis(s: &str, max_length: usize) -> String {
|
||||
pub fn shrink_with_ellipsis(s: &str, max_length: usize) -> String {
|
||||
if s.len() <= max_length {
|
||||
return s.to_string();
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ fn impl_cli_channel(ast: &syn::DeriveInput) -> TokenStream {
|
||||
use clap::ValueEnum;
|
||||
|
||||
#[derive(Debug, Clone, ValueEnum, Default, Copy)]
|
||||
pub(crate) enum CliTvChannel {
|
||||
pub enum CliTvChannel {
|
||||
#[default]
|
||||
#(#cli_enum_variants),*
|
||||
}
|
||||
@ -67,7 +67,7 @@ fn impl_cli_channel(ast: &syn::DeriveInput) -> TokenStream {
|
||||
#cli_enum
|
||||
|
||||
impl CliTvChannel {
|
||||
pub(crate) fn to_channel(self) -> Box<dyn TelevisionChannel> {
|
||||
pub fn to_channel(self) -> Box<dyn TelevisionChannel> {
|
||||
match self {
|
||||
#(#arms),*
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user