mirror of
https://github.com/ChrisTitusTech/linutil.git
synced 2024-11-05 13:15:21 +00:00
feat: implement syntax highlighting for preview text (#568)
* implement syntax highlighting for preview text * remove errant lifetimes Co-authored-by: Adam Perkowski <adas1per@protonmail.com> * remove errant lifetimes Co-authored-by: Adam Perkowski <adas1per@protonmail.com> * update Cargo.lock * only iterate over visible lines * break lines up on init * implement side scrolling * Remove unnecessary #![soft_unstable] This was accidentally left in from benching --------- Co-authored-by: Adam Perkowski <adas1per@protonmail.com>
This commit is contained in:
parent
006326afa9
commit
76a4ffc70e
129
Cargo.lock
generated
129
Cargo.lock
generated
|
@ -14,6 +14,15 @@ dependencies = [
|
||||||
"zerocopy",
|
"zerocopy",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "1.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "allocator-api2"
|
name = "allocator-api2"
|
||||||
version = "0.2.18"
|
version = "0.2.18"
|
||||||
|
@ -35,6 +44,19 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ansi-to-tui"
|
||||||
|
version = "6.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "00c4af0bef1b514c9b6a32a773caf604c1390fa7913f4eaa23bfe76f251d6a42"
|
||||||
|
dependencies = [
|
||||||
|
"nom",
|
||||||
|
"ratatui",
|
||||||
|
"simdutf8",
|
||||||
|
"smallvec",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstream"
|
name = "anstream"
|
||||||
version = "0.6.15"
|
version = "0.6.15"
|
||||||
|
@ -483,6 +505,8 @@ dependencies = [
|
||||||
name = "linutil_tui"
|
name = "linutil_tui"
|
||||||
version = "24.9.19"
|
version = "24.9.19"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"ansi-to-tui",
|
||||||
|
"anstyle",
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
"crossterm",
|
"crossterm",
|
||||||
|
@ -492,8 +516,11 @@ dependencies = [
|
||||||
"portable-pty",
|
"portable-pty",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"ratatui",
|
"ratatui",
|
||||||
|
"tree-sitter-bash",
|
||||||
|
"tree-sitter-highlight",
|
||||||
"tui-term",
|
"tui-term",
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
|
"zips",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -542,6 +569,12 @@ dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "minimal-lexical"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mio"
|
name = "mio"
|
||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
|
@ -569,6 +602,16 @@ dependencies = [
|
||||||
"pin-utils",
|
"pin-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nom"
|
||||||
|
version = "7.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"minimal-lexical",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.19"
|
version = "0.2.19"
|
||||||
|
@ -770,6 +813,35 @@ dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.6.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.10.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-automata",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.4.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.8.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "remove_dir_all"
|
name = "remove_dir_all"
|
||||||
version = "0.5.3"
|
version = "0.5.3"
|
||||||
|
@ -933,6 +1005,12 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "simdutf8"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smallvec"
|
name = "smallvec"
|
||||||
version = "1.13.2"
|
version = "1.13.2"
|
||||||
|
@ -1057,6 +1135,46 @@ dependencies = [
|
||||||
"winnow",
|
"winnow",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tree-sitter"
|
||||||
|
version = "0.23.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "20f4cd3642c47a85052a887d86704f4eac272969f61b686bdd3f772122aabaff"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"regex",
|
||||||
|
"regex-syntax",
|
||||||
|
"tree-sitter-language",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tree-sitter-bash"
|
||||||
|
version = "0.23.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3aa5e1c6bd02c0053f3f68edcf5d8866b38a8640584279e30fca88149ce14dda"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"tree-sitter-language",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tree-sitter-highlight"
|
||||||
|
version = "0.23.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "395d7a477a4504fd7d5e4d003e0dd41bd5b9c4985d53592a943a8354ec452dae"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
"regex",
|
||||||
|
"thiserror",
|
||||||
|
"tree-sitter",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tree-sitter-language"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2545046bd1473dac6c626659cc2567c6c0ff302fc8b84a56c4243378276f7f57"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tui-term"
|
name = "tui-term"
|
||||||
version = "0.1.13"
|
version = "0.1.13"
|
||||||
|
@ -1362,3 +1480,14 @@ dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zips"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba09194204fda6b1e206faf9096a3c0658ddf7606560f6edce112da3fcc9b111"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
|
@ -25,6 +25,11 @@ tui-term = "0.1.12"
|
||||||
unicode-width = "0.1.13"
|
unicode-width = "0.1.13"
|
||||||
rand = { version = "0.8.5", optional = true }
|
rand = { version = "0.8.5", optional = true }
|
||||||
linutil_core = { path = "../core", version = "24.9.19" }
|
linutil_core = { path = "../core", version = "24.9.19" }
|
||||||
|
tree-sitter-highlight = "0.23.0"
|
||||||
|
tree-sitter-bash = "0.23.1"
|
||||||
|
anstyle = "1.0.8"
|
||||||
|
ansi-to-tui = "6.0.0"
|
||||||
|
zips = "0.1.7"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
chrono = "0.4.33"
|
chrono = "0.4.33"
|
||||||
|
|
|
@ -1,9 +1,18 @@
|
||||||
|
use std::{
|
||||||
|
borrow::Cow,
|
||||||
|
collections::VecDeque,
|
||||||
|
io::{Cursor, Read as _, Seek, SeekFrom, Write as _},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
float::FloatContent,
|
float::FloatContent,
|
||||||
hint::{Shortcut, ShortcutList},
|
hint::{Shortcut, ShortcutList},
|
||||||
};
|
};
|
||||||
use crossterm::event::{KeyCode, KeyEvent};
|
|
||||||
use linutil_core::Command;
|
use linutil_core::Command;
|
||||||
|
|
||||||
|
use crossterm::event::{KeyCode, KeyEvent};
|
||||||
|
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
layout::Rect,
|
layout::Rect,
|
||||||
style::{Style, Stylize},
|
style::{Style, Stylize},
|
||||||
|
@ -11,53 +20,187 @@ use ratatui::{
|
||||||
widgets::{Block, Borders, Clear, List},
|
widgets::{Block, Borders, Clear, List},
|
||||||
Frame,
|
Frame,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use ansi_to_tui::IntoText;
|
||||||
|
|
||||||
|
use tree_sitter_bash as hl_bash;
|
||||||
|
use tree_sitter_highlight::{self as hl, HighlightEvent};
|
||||||
|
use zips::zip_result;
|
||||||
|
|
||||||
pub enum FloatingTextMode {
|
pub enum FloatingTextMode {
|
||||||
Preview,
|
Preview,
|
||||||
Description,
|
Description,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FloatingText {
|
pub struct FloatingText {
|
||||||
text: Vec<String>,
|
pub src: Vec<String>,
|
||||||
|
max_line_width: usize,
|
||||||
|
v_scroll: usize,
|
||||||
|
h_scroll: usize,
|
||||||
mode: FloatingTextMode,
|
mode: FloatingTextMode,
|
||||||
scroll: usize,
|
}
|
||||||
|
|
||||||
|
macro_rules! style {
|
||||||
|
($r:literal, $g:literal, $b:literal) => {{
|
||||||
|
use anstyle::{Color, RgbColor, Style};
|
||||||
|
Style::new().fg_color(Some(Color::Rgb(RgbColor($r, $g, $b))))
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
const SYNTAX_HIGHLIGHT_STYLES: [(&str, anstyle::Style); 8] = [
|
||||||
|
("function", style!(220, 220, 170)), // yellow
|
||||||
|
("string", style!(206, 145, 120)), // brown
|
||||||
|
("property", style!(156, 220, 254)), // light blue
|
||||||
|
("comment", style!(92, 131, 75)), // green
|
||||||
|
("embedded", style!(206, 145, 120)), // blue (string expansions)
|
||||||
|
("constant", style!(79, 193, 255)), // dark blue
|
||||||
|
("keyword", style!(197, 134, 192)), // magenta
|
||||||
|
("number", style!(181, 206, 168)), // light green
|
||||||
|
];
|
||||||
|
|
||||||
|
fn get_highlighted_string(s: &str) -> Option<String> {
|
||||||
|
let mut hl_conf = hl::HighlightConfiguration::new(
|
||||||
|
hl_bash::LANGUAGE.into(),
|
||||||
|
"bash",
|
||||||
|
hl_bash::HIGHLIGHT_QUERY,
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
|
let matched_tokens = &SYNTAX_HIGHLIGHT_STYLES
|
||||||
|
.iter()
|
||||||
|
.map(|hl| hl.0)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
hl_conf.configure(matched_tokens);
|
||||||
|
|
||||||
|
let mut hl = hl::Highlighter::new();
|
||||||
|
|
||||||
|
let mut style_stack = vec![anstyle::Style::new()];
|
||||||
|
let src = s.as_bytes();
|
||||||
|
|
||||||
|
let events = hl.highlight(&hl_conf, src, None, |_| None).ok()?;
|
||||||
|
|
||||||
|
let mut buf = Cursor::new(vec![]);
|
||||||
|
|
||||||
|
for event in events {
|
||||||
|
match event.unwrap() {
|
||||||
|
HighlightEvent::HighlightStart(h) => {
|
||||||
|
style_stack.push(SYNTAX_HIGHLIGHT_STYLES.get(h.0)?.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
HighlightEvent::HighlightEnd => {
|
||||||
|
style_stack.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
HighlightEvent::Source { start, end } => {
|
||||||
|
let style = style_stack.last()?;
|
||||||
|
zip_result!(
|
||||||
|
write!(&mut buf, "{}", style),
|
||||||
|
buf.write_all(&src[start..end]),
|
||||||
|
write!(&mut buf, "{style:#}"),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut output = String::new();
|
||||||
|
|
||||||
|
zip_result!(
|
||||||
|
buf.seek(SeekFrom::Start(0)),
|
||||||
|
buf.read_to_string(&mut output),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Some(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! max_width {
|
||||||
|
($($lines:tt)+) => {{
|
||||||
|
$($lines)+.iter().fold(0, |accum, val| accum.max(val.len()))
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn get_lines(s: &str) -> Vec<&str> {
|
||||||
|
s.lines().collect::<Vec<_>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn get_lines_owned(s: &str) -> Vec<String> {
|
||||||
|
get_lines(s).iter().map(|s| s.to_string()).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FloatingText {
|
impl FloatingText {
|
||||||
pub fn new(text: Vec<String>, mode: FloatingTextMode) -> Self {
|
pub fn new(text: String, mode: FloatingTextMode) -> Self {
|
||||||
|
let src = get_lines(&text)
|
||||||
|
.into_iter()
|
||||||
|
.map(|s| s.to_string())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let max_line_width = max_width!(src);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
text,
|
src,
|
||||||
scroll: 0,
|
|
||||||
mode,
|
mode,
|
||||||
|
max_line_width,
|
||||||
|
v_scroll: 0,
|
||||||
|
h_scroll: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_command(command: &Command, mode: FloatingTextMode) -> Option<Self> {
|
pub fn from_command(command: &Command, mode: FloatingTextMode) -> Option<Self> {
|
||||||
let lines = match command {
|
let (max_line_width, src) = match command {
|
||||||
Command::Raw(cmd) => {
|
Command::Raw(cmd) => {
|
||||||
// Reconstruct the line breaks and file formatting after the
|
// just apply highlights directly
|
||||||
// 'include_str!()' call in the node
|
(max_width!(get_lines(cmd)), Some(cmd.clone()))
|
||||||
cmd.lines().map(|line| line.to_string()).collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Command::LocalFile(file_path) => {
|
Command::LocalFile(file_path) => {
|
||||||
let file_contents = std::fs::read_to_string(file_path)
|
// have to read from tmp dir to get cmd src
|
||||||
|
let raw = std::fs::read_to_string(file_path)
|
||||||
.map_err(|_| format!("File not found: {:?}", file_path))
|
.map_err(|_| format!("File not found: {:?}", file_path))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
file_contents.lines().map(|line| line.to_string()).collect()
|
|
||||||
|
(max_width!(get_lines(&raw)), Some(raw))
|
||||||
}
|
}
|
||||||
|
|
||||||
// If command is a folder, we don't display a preview
|
// If command is a folder, we don't display a preview
|
||||||
Command::None => return None,
|
Command::None => (0usize, None),
|
||||||
};
|
};
|
||||||
Some(Self::new(lines, mode))
|
|
||||||
|
let src = get_lines_owned(&get_highlighted_string(&src?)?);
|
||||||
|
|
||||||
|
Some(Self {
|
||||||
|
src,
|
||||||
|
mode,
|
||||||
|
max_line_width,
|
||||||
|
h_scroll: 0,
|
||||||
|
v_scroll: 0,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scroll_down(&mut self) {
|
fn scroll_down(&mut self) {
|
||||||
if self.scroll + 1 < self.text.len() {
|
if self.v_scroll + 1 < self.src.len() {
|
||||||
self.scroll += 1;
|
self.v_scroll += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scroll_up(&mut self) {
|
fn scroll_up(&mut self) {
|
||||||
if self.scroll > 0 {
|
if self.v_scroll > 0 {
|
||||||
self.scroll -= 1;
|
self.v_scroll -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scroll_left(&mut self) {
|
||||||
|
if self.h_scroll > 0 {
|
||||||
|
self.h_scroll -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scroll_right(&mut self) {
|
||||||
|
if self.h_scroll + 1 < self.max_line_width {
|
||||||
|
self.h_scroll += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,25 +225,43 @@ impl FloatContent for FloatingText {
|
||||||
|
|
||||||
// Calculate the inner area to ensure text is not drawn over the border
|
// Calculate the inner area to ensure text is not drawn over the border
|
||||||
let inner_area = block.inner(area);
|
let inner_area = block.inner(area);
|
||||||
|
let Rect { height, .. } = inner_area;
|
||||||
// Create the list of lines to be displayed
|
let lines = self
|
||||||
let lines: Vec<Line> = self
|
.src
|
||||||
.text
|
|
||||||
.iter()
|
.iter()
|
||||||
.skip(self.scroll)
|
.skip(self.v_scroll)
|
||||||
.flat_map(|line| {
|
.take(height as usize)
|
||||||
if line.is_empty() {
|
.flat_map(|l| l.into_text().unwrap())
|
||||||
return vec![String::new()];
|
.map(|line| {
|
||||||
|
let mut skipped = 0;
|
||||||
|
let mut spans = line
|
||||||
|
.into_iter()
|
||||||
|
.skip_while(|span| {
|
||||||
|
let skip = (skipped + span.content.len()) <= self.h_scroll;
|
||||||
|
if skip {
|
||||||
|
skipped += span.content.len();
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<VecDeque<_>>();
|
||||||
|
|
||||||
|
if spans.is_empty() {
|
||||||
|
Line::raw(Cow::Owned(String::new()))
|
||||||
|
} else {
|
||||||
|
if skipped < self.h_scroll {
|
||||||
|
let to_split = spans.pop_front().unwrap();
|
||||||
|
let new_content = to_split.content.clone().into_owned()
|
||||||
|
[self.h_scroll - skipped..]
|
||||||
|
.to_owned();
|
||||||
|
spans.push_front(to_split.content(Cow::Owned(new_content)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Line::from(Vec::from(spans))
|
||||||
}
|
}
|
||||||
line.chars()
|
|
||||||
.collect::<Vec<char>>()
|
|
||||||
.chunks(inner_area.width as usize)
|
|
||||||
.map(|chunk| chunk.iter().collect())
|
|
||||||
.collect::<Vec<String>>()
|
|
||||||
})
|
})
|
||||||
.take(inner_area.height as usize)
|
.collect::<Vec<_>>();
|
||||||
.map(Line::from)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Create list widget
|
// Create list widget
|
||||||
let list = List::new(lines)
|
let list = List::new(lines)
|
||||||
|
@ -115,9 +276,12 @@ impl FloatContent for FloatingText {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_key_event(&mut self, key: &KeyEvent) -> bool {
|
fn handle_key_event(&mut self, key: &KeyEvent) -> bool {
|
||||||
|
use KeyCode::*;
|
||||||
match key.code {
|
match key.code {
|
||||||
KeyCode::Down | KeyCode::Char('j') => self.scroll_down(),
|
Down | Char('j') => self.scroll_down(),
|
||||||
KeyCode::Up | KeyCode::Char('k') => self.scroll_up(),
|
Up | Char('k') => self.scroll_up(),
|
||||||
|
Left | Char('h') => self.scroll_left(),
|
||||||
|
Right | Char('l') => self.scroll_right(),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
|
@ -133,7 +297,9 @@ impl FloatContent for FloatingText {
|
||||||
hints: vec![
|
hints: vec![
|
||||||
Shortcut::new(vec!["j", "Down"], "Scroll down"),
|
Shortcut::new(vec!["j", "Down"], "Scroll down"),
|
||||||
Shortcut::new(vec!["k", "Up"], "Scroll up"),
|
Shortcut::new(vec!["k", "Up"], "Scroll up"),
|
||||||
Shortcut::new(vec!["Enter", "q"], "Close window"),
|
Shortcut::new(vec!["h", "Left"], "Scroll left"),
|
||||||
|
Shortcut::new(vec!["l", "Right"], "Scroll right"),
|
||||||
|
Shortcut::new(vec!["Enter", "p", "d"], "Close window"),
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -495,12 +495,7 @@ impl AppState {
|
||||||
}
|
}
|
||||||
fn enable_description(&mut self) {
|
fn enable_description(&mut self) {
|
||||||
if let Some(command_description) = self.get_selected_description() {
|
if let Some(command_description) = self.get_selected_description() {
|
||||||
let description_content: Vec<String> = vec![]
|
let description = FloatingText::new(command_description, FloatingTextMode::Description);
|
||||||
.into_iter()
|
|
||||||
.chain(command_description.lines().map(|line| line.to_string())) // New line when \n is given in toml
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let description = FloatingText::new(description_content, FloatingTextMode::Description);
|
|
||||||
self.spawn_float(description, 80, 80);
|
self.spawn_float(description, 80, 80);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user