display hyperlinks
This commit is contained in:
parent
ae5ce58eec
commit
f3bc16b3c5
6 changed files with 93 additions and 7 deletions
|
|
@ -49,6 +49,11 @@ struct Args {
|
|||
#[arg(long = "logs-dir")]
|
||||
logs_dir: PathBuf,
|
||||
|
||||
/// Path where the compiler source code lives, for links in the TUI to work.
|
||||
#[arg(global = true)]
|
||||
#[arg(long = "compiler-root")]
|
||||
compiler_root: Option<PathBuf>,
|
||||
|
||||
#[arg(trailing_var_arg = true)]
|
||||
#[arg(allow_hyphen_values = true)]
|
||||
#[arg(global = true)]
|
||||
|
|
@ -60,11 +65,12 @@ fn main() {
|
|||
preset,
|
||||
logs_dir,
|
||||
rest,
|
||||
compiler_root,
|
||||
} = Args::parse();
|
||||
|
||||
let rustc_log = match preset {
|
||||
Preset::Show => {
|
||||
tui::run(logs_dir);
|
||||
tui::run(logs_dir, compiler_root);
|
||||
exit(0);
|
||||
}
|
||||
Preset::Types => {
|
||||
|
|
|
|||
|
|
@ -188,7 +188,7 @@ impl LogViewer {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn items(&self, max: usize) -> Option<(Vec<(Rc<LogEntry>, usize)>, usize)> {
|
||||
pub fn items(&mut self, max: usize) -> Option<(Vec<(Rc<LogEntry>, usize)>, usize)> {
|
||||
let mut temp_iter = self.curr.iter.clone();
|
||||
let mut res = Vec::new();
|
||||
for _ in 0..max {
|
||||
|
|
@ -198,6 +198,10 @@ impl LogViewer {
|
|||
res.push(i);
|
||||
}
|
||||
|
||||
if self.curr.selection_offset > res.len() {
|
||||
self.curr.selection_offset = res.len();
|
||||
}
|
||||
|
||||
Some((res, self.curr.selection_offset))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use itertools::Itertools;
|
||||
use ratatui_themes::{Theme, ThemeName};
|
||||
use std::{
|
||||
fs::{self, DirEntry},
|
||||
|
|
@ -22,7 +23,7 @@ use ratatui::{
|
|||
crossterm::event::{self, Event, KeyCode, KeyModifiers},
|
||||
layout::{Constraint, HorizontalAlignment, Layout, Rect},
|
||||
style::Style,
|
||||
text::Span,
|
||||
text::{Span, Text},
|
||||
widgets::{
|
||||
Block, Clear, List, ListItem, ListState, Padding, Paragraph, StatefulWidget, Widget, Wrap,
|
||||
},
|
||||
|
|
@ -34,10 +35,10 @@ pub mod model;
|
|||
pub mod processing;
|
||||
pub mod reader;
|
||||
|
||||
pub fn run(logs_dir: PathBuf) {
|
||||
pub fn run(logs_dir: PathBuf, compiler_root: Option<PathBuf>) {
|
||||
let terminal = ratatui::init();
|
||||
let theme = Theme::new(ThemeName::OneDarkPro);
|
||||
let app_result = App::new(logs_dir, theme).run(terminal);
|
||||
let app_result = App::new(logs_dir, compiler_root, theme).run(terminal);
|
||||
ratatui::restore();
|
||||
|
||||
if let Err(e) = app_result {
|
||||
|
|
@ -97,16 +98,18 @@ fn initialize_filter(lv: &mut LogViewer, kind: Option<FilterKind>) -> WipFilter
|
|||
struct App {
|
||||
tabs: Vec<Tab>,
|
||||
logs_dir: PathBuf,
|
||||
compiler_root: Option<PathBuf>,
|
||||
current_file: Option<LogfileReader>,
|
||||
theme: Theme,
|
||||
}
|
||||
|
||||
impl App {
|
||||
fn new(logs_dir: PathBuf, theme: Theme) -> Self {
|
||||
fn new(logs_dir: PathBuf, compiler_root: Option<PathBuf>, theme: Theme) -> Self {
|
||||
let mut res = Self {
|
||||
tabs: Vec::new(),
|
||||
current_file: None,
|
||||
logs_dir,
|
||||
compiler_root,
|
||||
theme,
|
||||
};
|
||||
res.replace_tab(res.choose_file());
|
||||
|
|
@ -366,13 +369,14 @@ impl Widget for &mut App {
|
|||
};
|
||||
|
||||
let footer_area = {
|
||||
let block = Block::bordered()
|
||||
let mut block = Block::bordered()
|
||||
.style(default)
|
||||
.border_style(if footer_focused {
|
||||
border_selected
|
||||
} else {
|
||||
border
|
||||
});
|
||||
|
||||
let inner = block.inner(footer_area);
|
||||
block.render(footer_area, buf);
|
||||
inner
|
||||
|
|
@ -441,6 +445,29 @@ impl Widget for &mut App {
|
|||
));
|
||||
Widget::render(list, main_area, buf);
|
||||
|
||||
Clear.render(footer_area, buf);
|
||||
let [first_line, footer_area] =
|
||||
Layout::vertical([Constraint::Length(1), Constraint::Fill(1)])
|
||||
.areas(footer_area);
|
||||
if let Some((e, _)) = lv.selected() {
|
||||
let rustc_root = self.compiler_root.as_ref();
|
||||
let (file, line) = e.file_line_string();
|
||||
|
||||
if let Some(rustc_root) = rustc_root
|
||||
&& let Ok(canonical_rustc_root) = rustc_root.canonicalize()
|
||||
{
|
||||
let full_file_path = canonical_rustc_root.join(&file);
|
||||
Hyperlink::new(
|
||||
format!("In file: {}", file.display()),
|
||||
format!("file://{}:{line}", full_file_path.display()),
|
||||
)
|
||||
.render(first_line, buf);
|
||||
} else {
|
||||
Span::from(format!("In file: {}:{line}", file.display()))
|
||||
.render(first_line, buf);
|
||||
}
|
||||
}
|
||||
|
||||
let items = lv.footer_fields();
|
||||
let width = 20;
|
||||
let builder = ListBuilder::new(|cx| {
|
||||
|
|
@ -591,3 +618,40 @@ impl Widget for &mut App {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Hyperlink<'content> {
|
||||
text: Text<'content>,
|
||||
url: String,
|
||||
}
|
||||
|
||||
impl<'content> Hyperlink<'content> {
|
||||
fn new(text: impl Into<Text<'content>>, url: impl Into<String>) -> Self {
|
||||
Self {
|
||||
text: text.into(),
|
||||
url: url.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for Hyperlink<'_> {
|
||||
fn render(self, area: Rect, buffer: &mut Buffer) {
|
||||
(&self.text).render(area, buffer);
|
||||
|
||||
// this is a hacky workaround for https://github.com/ratatui/ratatui/issues/902, a bug
|
||||
// in the terminal code that incorrectly calculates the width of ANSI escape sequences. It
|
||||
// works by rendering the hyperlink as a series of 2-character chunks, which is the
|
||||
// calculated width of the hyperlink text.
|
||||
for (i, two_chars) in self
|
||||
.text
|
||||
.to_string()
|
||||
.chars()
|
||||
.chunks(2)
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
{
|
||||
let text = two_chars.collect::<String>();
|
||||
let hyperlink = format!("\x1B]8;;{}\x07{}\x1B]8;;\x07", self.url, text);
|
||||
buffer[(area.x + i as u16 * 2, area.y)].set_symbol(hyperlink.as_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use std::{
|
||||
collections::BTreeMap,
|
||||
hash::{DefaultHasher, Hash, Hasher},
|
||||
path::PathBuf,
|
||||
rc::Rc,
|
||||
sync::OnceLock,
|
||||
};
|
||||
|
|
@ -38,6 +39,15 @@ pub enum LogEntry {
|
|||
}
|
||||
|
||||
impl LogEntry {
|
||||
pub fn file_line_string(&self) -> (PathBuf, usize) {
|
||||
let entry = match self {
|
||||
LogEntry::Single { raw } => raw,
|
||||
LogEntry::Sub { enter, .. } => enter,
|
||||
};
|
||||
|
||||
(PathBuf::from(entry.filename.clone()), entry.line_number)
|
||||
}
|
||||
|
||||
pub fn hash(&self) -> u64 {
|
||||
let mut hasher = DefaultHasher::new();
|
||||
match self {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue