feat: highlight the inputs when highlighting mode turns on (#25)

This commit is contained in:
三咲雅 · Misaki Masa 2023-08-06 17:32:34 +08:00 committed by GitHub
parent 2f32ca17cb
commit dadc408a8e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 129 additions and 68 deletions

View File

@ -1,6 +1,7 @@
use core::input::InputMode;
use ratatui::{buffer::Buffer, layout::Rect, style::{Color, Style}, text::Line, widgets::{Block, BorderType, Borders, Clear, Paragraph, Widget}};
use ansi_to_tui::IntoText;
use ratatui::{buffer::Buffer, layout::Rect, style::{Color, Style}, text::{Line, Text}, widgets::{Block, BorderType, Borders, Clear, Paragraph, Widget}};
use shared::Term;
use crate::Ctx;
@ -18,8 +19,14 @@ impl<'a> Widget for Input<'a> {
let input = &self.cx.input;
let area = input.area();
let value = if let Ok(v) = input.value_pretty() {
v.into_text().unwrap()
} else {
Text::from(input.value())
};
Clear.render(area, buf);
Paragraph::new(input.value())
Paragraph::new(value)
.block(
Block::new()
.borders(Borders::ALL)

24
core/src/highlighter.rs Normal file
View File

@ -0,0 +1,24 @@
use std::{fs::File, io::BufReader, sync::OnceLock};
use anyhow::Result;
use config::THEME;
use syntect::{dumps::from_uncompressed_data, highlighting::{Theme, ThemeSet}, parsing::SyntaxSet};
static SYNTECT_SYNTAX: OnceLock<SyntaxSet> = OnceLock::new();
static SYNTECT_THEME: OnceLock<Theme> = OnceLock::new();
#[inline]
pub fn highlighter() -> (&'static SyntaxSet, &'static Theme) {
let syntaxes =
SYNTECT_SYNTAX.get_or_init(|| from_uncompressed_data(yazi_prebuild::syntaxes()).unwrap());
let theme = SYNTECT_THEME.get_or_init(|| {
let from_file = || -> Result<Theme> {
let file = File::open(&THEME.preview.syntect_theme)?;
Ok(ThemeSet::load_from_reader(&mut BufReader::new(file))?)
};
from_file().unwrap_or_else(|_| ThemeSet::load_defaults().themes["base16-ocean.dark"].clone())
});
(syntaxes, theme)
}

View File

@ -6,39 +6,37 @@ use shared::CharKind;
use tokio::sync::oneshot::Sender;
use unicode_width::UnicodeWidthStr;
use super::{mode::InputMode, op::InputOp, InputSnap, InputSnaps};
use super::{mode::InputMode, op::InputOp, InputOpt, InputSnap, InputSnaps};
use crate::{external, Position};
#[derive(Default)]
pub struct Input {
snaps: InputSnaps,
snaps: InputSnaps,
pub visible: bool,
title: String,
position: (u16, u16),
callback: Option<Sender<Result<String>>>,
pub visible: bool,
}
pub struct InputOpt {
pub title: String,
pub value: String,
pub position: Position,
// Shell
pub(super) highlight: bool,
}
impl Input {
pub fn show(&mut self, opt: InputOpt, tx: Sender<Result<String>>) {
self.close(false);
self.snaps.reset(opt.value);
self.visible = true;
self.title = opt.title;
self.position = match opt.position {
Position::Coords(x, y) => (x, y),
_ => unimplemented!(),
_ => unreachable!(),
};
self.callback = Some(tx);
self.visible = true;
// Shell
self.highlight = opt.highlight;
}
pub fn close(&mut self, submit: bool) -> bool {

View File

@ -1,11 +1,15 @@
mod input;
mod mode;
mod op;
mod option;
mod shell;
mod snap;
mod snaps;
pub use input::*;
pub use mode::*;
use op::*;
pub use option::*;
pub use shell::*;
use snap::*;
use snaps::*;

38
core/src/input/option.rs Normal file
View File

@ -0,0 +1,38 @@
use crate::Position;
pub struct InputOpt {
pub title: String,
pub value: String,
pub position: Position,
pub highlight: bool,
}
impl InputOpt {
pub fn top(title: impl AsRef<str>) -> Self {
Self {
title: title.as_ref().to_owned(),
value: String::new(),
position: Position::Top,
highlight: false,
}
}
pub fn hovered(title: impl AsRef<str>) -> Self {
Self {
title: title.as_ref().to_owned(),
value: String::new(),
position: Position::Hovered,
highlight: false,
}
}
pub fn with_value(mut self, value: impl AsRef<str>) -> Self {
self.value = value.as_ref().to_owned();
self
}
pub fn with_highlight(mut self) -> Self {
self.highlight = true;
self
}
}

22
core/src/input/shell.rs Normal file
View File

@ -0,0 +1,22 @@
use anyhow::{bail, Result};
use syntect::{easy::HighlightLines, util::as_24_bit_terminal_escaped};
use super::Input;
use crate::highlighter;
impl Input {
pub fn value_pretty(&self) -> Result<String> {
if !self.highlight {
bail!("Highlighting is disabled")
}
let (syntaxes, theme) = highlighter();
if let Some(syntax) = syntaxes.find_syntax_by_name("Bourne Again Shell (bash)") {
let mut h = HighlightLines::new(syntax, theme);
let regions = h.highlight_line(self.value(), syntaxes)?;
return Ok(as_24_bit_terminal_escaped(&regions, false));
}
bail!("Failed to find syntax")
}
}

View File

@ -2,6 +2,7 @@ mod blocker;
mod event;
pub mod external;
pub mod files;
mod highlighter;
pub mod input;
pub mod manager;
pub mod position;
@ -11,4 +12,5 @@ pub mod which;
pub use blocker::*;
pub use event::*;
pub use highlighter::*;
pub use position::*;

View File

@ -98,11 +98,8 @@ impl Manager {
}
tokio::spawn(async move {
let result = emit!(Input(InputOpt {
title: format!("There are {tasks} tasks running, sure to quit? (y/N)"),
value: "".to_string(),
position: Position::Top,
}));
let result =
emit!(Input(InputOpt::top("There are {tasks} tasks running, sure to quit? (y/N)")));
if let Ok(choice) = result.await {
if choice.to_lowercase() == "y" {
@ -178,11 +175,7 @@ impl Manager {
pub fn create(&self) -> bool {
let cwd = self.current().cwd.clone();
tokio::spawn(async move {
let result = emit!(Input(InputOpt {
title: "Create:".to_string(),
value: "".to_string(),
position: Position::Top,
}));
let result = emit!(Input(InputOpt::top("Create:")));
if let Ok(name) = result.await {
let path = cwd.join(&name);
@ -217,11 +210,9 @@ impl Manager {
};
tokio::spawn(async move {
let result = emit!(Input(InputOpt {
title: "Rename:".to_string(),
value: hovered.file_name().unwrap().to_string_lossy().to_string(),
position: Position::Hovered,
}));
let result = emit!(Input(
InputOpt::hovered("Rename:").with_value(hovered.file_name().unwrap().to_string_lossy())
));
if let Ok(new) = result.await {
let to = hovered.parent().unwrap().join(new);
@ -235,11 +226,7 @@ impl Manager {
pub fn shell(&self, block: bool) -> bool {
tokio::spawn(async move {
let result = emit!(Input(InputOpt {
title: "Shell:".to_string(),
value: "".to_string(),
position: Position::Top,
}));
let result = emit!(Input(InputOpt::top("Shell:").with_highlight()));
if let Ok(cmd) = result.await {
emit!(Open(

View File

@ -1,18 +1,15 @@
use std::{fs::File, io::{BufRead, BufReader}, mem, path::{Path, PathBuf}, sync::{atomic::{AtomicUsize, Ordering}, Arc, OnceLock}};
use std::{io::BufRead, mem, path::{Path, PathBuf}, sync::{atomic::{AtomicUsize, Ordering}, Arc}};
use adaptor::{Adaptor, Image};
use anyhow::{anyhow, bail, Result};
use config::{PREVIEW, THEME};
use config::PREVIEW;
use ratatui::prelude::Rect;
use shared::{tty_size, MimeKind};
use syntect::{dumps::from_uncompressed_data, easy::HighlightFile, highlighting::{Theme, ThemeSet}, parsing::SyntaxSet, util::as_24_bit_terminal_escaped};
use syntect::{easy::HighlightFile, util::as_24_bit_terminal_escaped};
use tokio::{fs, task::JoinHandle};
use super::{ALL_RATIO, CURRENT_RATIO, PARENT_RATIO, PREVIEW_BORDER, PREVIEW_MARGIN, PREVIEW_RATIO};
use crate::{emit, external, files::{Files, FilesOp}};
static SYNTECT_SYNTAX: OnceLock<SyntaxSet> = OnceLock::new();
static SYNTECT_THEME: OnceLock<Theme> = OnceLock::new();
use crate::{emit, external, files::{Files, FilesOp}, highlighter};
#[derive(Default)]
pub struct Preview {
@ -157,21 +154,12 @@ impl Preview {
pub async fn highlight(path: &Path, incr: Arc<AtomicUsize>) -> Result<String> {
let tick = incr.load(Ordering::Relaxed);
let syntax =
SYNTECT_SYNTAX.get_or_init(|| from_uncompressed_data(yazi_prebuild::syntaxes()).unwrap());
let theme = SYNTECT_THEME.get_or_init(|| {
let from_file = || -> Result<Theme> {
let file = File::open(&THEME.preview.syntect_theme)?;
Ok(ThemeSet::load_from_reader(&mut BufReader::new(file))?)
};
from_file().unwrap_or_else(|_| ThemeSet::load_defaults().themes["base16-ocean.dark"].clone())
});
let path = path.to_path_buf();
let spaces = " ".repeat(PREVIEW.tab_size as usize);
let (syntaxes, theme) = highlighter();
tokio::task::spawn_blocking(move || -> Result<String> {
let mut h = HighlightFile::new(path, syntax, theme)?;
let mut h = HighlightFile::new(path, syntaxes, theme)?;
let mut line = String::new();
let mut buf = String::new();
@ -183,8 +171,8 @@ impl Preview {
i -= 1;
line = line.replace('\t', &spaces);
let regions = h.highlight_lines.highlight_line(&line, syntax)?;
buf.push_str(&as_24_bit_terminal_escaped(&regions[..], false));
let regions = h.highlight_lines.highlight_line(&line, syntaxes)?;
buf.push_str(&as_24_bit_terminal_escaped(&regions, false));
line.clear();
}

View File

@ -5,7 +5,7 @@ use shared::Defer;
use tokio::task::JoinHandle;
use super::{Folder, Mode, Preview};
use crate::{emit, external::{self, FzfOpt, ZoxideOpt}, files::{File, Files, FilesOp}, input::InputOpt, Event, Position, BLOCKER};
use crate::{emit, external::{self, FzfOpt, ZoxideOpt}, files::{File, Files, FilesOp}, input::InputOpt, Event, BLOCKER};
pub struct Tab {
pub(super) mode: Mode,
@ -186,12 +186,7 @@ impl Tab {
let hidden = self.current.files.show_hidden;
self.search = Some(tokio::spawn(async move {
let subject = emit!(Input(InputOpt {
title: "Search:".to_string(),
value: "".to_string(),
position: Position::Top,
}))
.await?;
let subject = emit!(Input(InputOpt::top("Search:"))).await?;
let mut rx = if grep {
external::rg(external::RgOpt { cwd: cwd.clone(), hidden, subject })

View File

@ -33,7 +33,7 @@ impl Select {
self.items = opt.items;
self.position = match opt.position {
Position::Coords(x, y) => (x, y),
_ => unimplemented!(),
_ => unreachable!(),
};
self.callback = Some(tx);
self.visible = true;

View File

@ -7,7 +7,7 @@ use tokio::{io::AsyncReadExt, select, sync::mpsc, time};
use tracing::trace;
use super::{task::TaskSummary, Scheduler, TASKS_PADDING, TASKS_PERCENT};
use crate::{emit, files::{File, Files}, input::InputOpt, Position, BLOCKER};
use crate::{emit, files::{File, Files}, input::InputOpt, BLOCKER};
pub struct Tasks {
scheduler: Arc<Scheduler>,
@ -185,11 +185,7 @@ impl Tasks {
pub fn file_remove(&self, targets: Vec<PathBuf>, permanently: bool) -> bool {
let scheduler = self.scheduler.clone();
tokio::spawn(async move {
let result = emit!(Input(InputOpt {
title: "Are you sure delete these files? (y/N)".to_string(),
value: "".to_string(),
position: Position::Hovered,
}));
let result = emit!(Input(InputOpt::hovered("Are you sure delete these files? (y/N)")));
if let Ok(choice) = result.await {
if choice.to_lowercase() != "y" {