mirror of
https://github.com/sxyazi/yazi.git
synced 2024-12-18 22:31:35 +03:00
feat: highlight the inputs when highlighting mode turns on (#25)
This commit is contained in:
parent
2f32ca17cb
commit
dadc408a8e
@ -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
24
core/src/highlighter.rs
Normal 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)
|
||||
}
|
@ -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 {
|
||||
|
@ -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
38
core/src/input/option.rs
Normal 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
22
core/src/input/shell.rs
Normal 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(®ions, false));
|
||||
}
|
||||
|
||||
bail!("Failed to find syntax")
|
||||
}
|
||||
}
|
@ -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::*;
|
||||
|
@ -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(
|
||||
|
@ -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(®ions[..], false));
|
||||
let regions = h.highlight_lines.highlight_line(&line, syntaxes)?;
|
||||
buf.push_str(&as_24_bit_terminal_escaped(®ions, false));
|
||||
line.clear();
|
||||
}
|
||||
|
||||
|
@ -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 })
|
||||
|
@ -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;
|
||||
|
@ -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" {
|
||||
|
Loading…
Reference in New Issue
Block a user