Skip to content

Commit

Permalink
Merge pull request #170 from blacknon/0.3.17
Browse files Browse the repository at this point in the history
0.3.17
  • Loading branch information
blacknon authored Nov 13, 2024
2 parents 4964048 + 9500e75 commit f685ba1
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 46 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ keywords = ["watch", "command", "monitoring"]
license-file = "LICENSE"
name = "hwatch"
repository = "https://github.com/blacknon/hwatch"
version = "0.3.16"
version = "0.3.17"

[dependencies]
ansi-parser = "0.9.0"
Expand Down
4 changes: 2 additions & 2 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ use tui::{
Frame, Terminal,
};
use similar::{ChangeTag, TextDiff};
use unicode_width::UnicodeWidthStr;

// local module
use crate::ansi::get_ansi_strip_str;
Expand Down Expand Up @@ -411,8 +412,7 @@ impl<'a> App<'a> {
// match input_mode
match self.input_mode {
InputMode::Filter | InputMode::RegexFilter => {
//
let input_text_x = self.header_area.input_text.len() as u16 + 1;
let input_text_x = self.header_area.input_text.width() as u16 + 1;
let input_text_y = self.header_area.area.y + 1;

// set cursor
Expand Down
19 changes: 16 additions & 3 deletions src/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.

// TODO: commandの表示を単色ではなく、Syntax highlightしたカラーリングに書き換える(v0.3.9)
// TODO: commandの表示を単色ではなく、Syntax highlightしたカラーリングに書き換える??(v0.3.9)
// TODO: input内容の表示
// TODO: 幅調整系の数字をconstにする(生数字で雑計算だとわけわからん)

Expand All @@ -14,6 +14,7 @@ use tui::{
Frame,
prelude::Line,
};
use unicode_width::UnicodeWidthStr;

// local module
use crate::app::{ActiveArea, InputMode};
Expand Down Expand Up @@ -187,15 +188,16 @@ impl<'a> HeaderArea<'a> {
}

// filter keyword.
let filter_keyword_width = if width > ((self.banner.len() + 20) + 2 + 14) {
let filter_keyword_width = if width > ((self.banner.len() + 20) + 2 + 14) && width > 59 {
// width - POSITION_X_HELP_TEXT - 2 - 14
// length("[Number] [Color] [Output] [history] [Line(Only)]") = 48
// length("[Number] [Color] [Reverse] [Output] [history] [Line(Only)]") = 58
width - 59
} else {
0
};
let filter_keyword = format!("{:wid$}", self.input_text, wid = filter_keyword_width);
// format!("{:wid$}", self.input_text, wid = filter_keyword_width);
let filter_keyword = format_with_multibyte_width(&self.input_text, filter_keyword_width);
let filter_keyword_style: Style;

if self.input_text.is_empty() {
Expand Down Expand Up @@ -358,3 +360,14 @@ impl<'a> HeaderArea<'a> {
frame.render_widget(block, self.area);
}
}

fn format_with_multibyte_width(input: &str, target_width: usize) -> String {
let current_width = UnicodeWidthStr::width(input);
if current_width >= target_width {
input.to_string()
} else {
// 残りの幅を計算し、スペースでパディングを追加
let padding = " ".repeat(target_width - current_width);
format!("{}{}", input, padding)
}
}
9 changes: 6 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.

// v0.3.18
// TODO(blacknon): watchウィンドウの表示を折り返しだけではなく、横方向にスクロールして出力するモードも追加する
// TODO(blacknon): コマンドが終了していなくても、インターバル間隔でコマンドを実行する
// (パラレルで実行してもよいコマンドじゃないといけないよ、という機能か。投げっぱなしにしてintervalで待つようにするオプションを付ける)
// TODO(blacknon): DiffModeをInterfaceで取り扱うようにし、historyへの追加や検索時のhitなどについてもInterface側で取り扱えるようにする。(DiffModeのPlugin化の布石)

// v0.3.xx
// TODO(blacknon): [FR: add "completion" subcommand](https://github.com/blacknon/hwatch/issues/107)
// TODO(blacknon): [[FR] add precise interval option](https://github.com/blacknon/hwatch/issues/111)
Expand All @@ -10,9 +16,6 @@
// TODO(blacknon): filter modeの検索ヒット数を表示する(どうやってやろう…?というより、どこに表示させよう…?)
// TODO(blacknon): Windowsのバイナリをパッケージマネジメントシステムでインストール可能になるよう、Releaseでうまいこと処理をする
// TODO(blacknon): UTF-8以外のエンコードでも動作するよう対応する(エンコード対応)
// TODO(blacknon): watchウィンドウの表示を折り返しだけではなく、横方向にスクロールして出力するモードも追加する
// TODO(blacknon): コマンドが終了していなくても、インターバル間隔でコマンドを実行する
// (パラレルで実行してもよいコマンドじゃないといけないよ、という機能か。投げっぱなしにしてintervalで待つようにするオプションを付ける)
// TODO(blacknon): 空白の数だけ違う場合、diffとして扱わないようにするオプションの追加(shortcut keyではなく、`:set hogehoge...`で指定する機能として実装)
// TODO(blacknon): watchをモダンよりのものに変更する
// TODO(blacknon): diff modeをさらに複数用意し、選択・切り替えできるdiffをオプションから指定できるようにする(watchをold-watchにして、モダンなwatchをデフォルトにしたり)
Expand Down
5 changes: 0 additions & 5 deletions src/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -314,11 +314,6 @@ fn send_input(tx: Sender<AppEvent>) -> io::Result<()> {
if let Ok(event) = result {
let _ = tx.send(AppEvent::TerminalEvent(event));
}

// clearing buffer
while crossterm::event::poll(std::time::Duration::from_millis(0)).unwrap() {
let _ = crossterm::event::read()?;
}
}
Ok(())
}
Expand Down
127 changes: 96 additions & 31 deletions src/watch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,13 @@ use tui::{
widgets::{Block, Borders, Paragraph, Scrollbar, ScrollbarOrientation, ScrollbarState},
Frame
};

use regex::Regex;
use unicode_segmentation::UnicodeSegmentation;

// set highlight style
static KEYWORD_HIGHLIGHT_STYLE: Style = Style::new().fg(Color::Black).bg(Color::Yellow);
static SELECTED_KEYWORD_HIGHLIGHT_STYLE: Style = Style::new().fg(Color::Black).bg(Color::Cyan);

#[derive(Clone)]
pub struct WatchArea<'a> {
/// ratatui::layout::Rect. The area to draw the widget in.
Expand All @@ -27,6 +30,9 @@ pub struct WatchArea<'a> {
/// Wrapped data.
wrap_data: Vec<Line<'a>>,

/// highlighted data.
highlight_data: Vec<Line<'a>>,

/// search keyword.
keyword: String,

Expand Down Expand Up @@ -73,6 +79,8 @@ impl<'a> WatchArea<'a> {

wrap_data: vec![Line::from("")],

highlight_data: vec![Line::from("")],

keyword: String::from(""),

keyword_is_regex: false,
Expand Down Expand Up @@ -121,6 +129,15 @@ impl<'a> WatchArea<'a> {
// update keyword position
self.keyword_position = get_keyword_positions(&self.wrap_data, &self.keyword, self.keyword_is_regex, self.is_line_number, self.is_line_diff_head);
}

// set highlight style
self.highlight_data = highlight_text(
self.wrap_data.clone(),
self.keyword_position.clone(),
self.selected_keyword,
KEYWORD_HIGHLIGHT_STYLE,
SELECTED_KEYWORD_HIGHLIGHT_STYLE
);
}

///
Expand All @@ -132,6 +149,15 @@ impl<'a> WatchArea<'a> {
// update keyword position
self.keyword_position = get_keyword_positions(&self.wrap_data, &self.keyword, self.keyword_is_regex, self.is_line_number, self.is_line_diff_head);
}

// set highlight style
self.highlight_data = highlight_text(
self.wrap_data.clone(),
self.keyword_position.clone(),
self.selected_keyword,
KEYWORD_HIGHLIGHT_STYLE,
SELECTED_KEYWORD_HIGHLIGHT_STYLE
);
}

///
Expand Down Expand Up @@ -171,6 +197,15 @@ impl<'a> WatchArea<'a> {
} else {
self.keyword_position = vec![];
}

// set highlight style
self.highlight_data = highlight_text(
self.wrap_data.clone(),
self.keyword_position.clone(),
self.selected_keyword,
KEYWORD_HIGHLIGHT_STYLE,
SELECTED_KEYWORD_HIGHLIGHT_STYLE
);
}

///
Expand All @@ -179,7 +214,15 @@ impl<'a> WatchArea<'a> {
self.keyword_is_regex = false;
self.keyword_position = vec![];
self.selected_keyword = -1;
}

// set highlight style
self.highlight_data = highlight_text(
self.wrap_data.clone(),
self.keyword_position.clone(),
self.selected_keyword,
KEYWORD_HIGHLIGHT_STYLE,
SELECTED_KEYWORD_HIGHLIGHT_STYLE
); }

///
pub fn previous_keyword(&mut self) {
Expand All @@ -203,6 +246,15 @@ impl<'a> WatchArea<'a> {
// scroll move
self.scroll_move(position.0 as i16);
}

// set highlight style
self.highlight_data = highlight_text(
self.wrap_data.clone(),
self.keyword_position.clone(),
self.selected_keyword,
KEYWORD_HIGHLIGHT_STYLE,
SELECTED_KEYWORD_HIGHLIGHT_STYLE
);
}

///
Expand Down Expand Up @@ -231,15 +283,20 @@ impl<'a> WatchArea<'a> {
self.scroll_move(position.0 as i16);
}
}

// set highlight style
self.highlight_data = highlight_text(
self.wrap_data.clone(),
self.keyword_position.clone(),
self.selected_keyword,
KEYWORD_HIGHLIGHT_STYLE,
SELECTED_KEYWORD_HIGHLIGHT_STYLE
);
}

///
pub fn draw(&mut self, frame: &mut Frame) {
// set highlight style
let highlight_style = Style::new().fg(Color::Black).bg(Color::Yellow);
let selected_highlight_style = Style::new().fg(Color::Black).bg(Color::Cyan);

let block_data = highlight_text(&self.wrap_data, self.keyword_position.clone(), self.selected_keyword, selected_highlight_style, highlight_style);
let block_data = self.highlight_data.clone();

// declare variables
let pane_block: Block<'_>;
Expand Down Expand Up @@ -371,14 +428,18 @@ impl<'a> WatchArea<'a> {

///
fn get_keyword_positions(lines: &Vec<Line>, keyword: &str, is_regex: bool, is_line_number: bool, is_diff_head: bool) -> Vec<(usize, usize, usize)> {
// Ignore the number of characters at the beginning of the line specified by `ignore_head_count` when searching.
let mut ignore_head_count = 0;

//
if is_line_number {
let num_count = lines.len().to_string().len();
ignore_head_count = num_count + 3; // ^<number>` | `
}

// ` ` | ` + ` | ` - `
if is_diff_head {
ignore_head_count += 4; // ` ` | ` + ` | ` - `
ignore_head_count += 4;
}

let re = if is_regex {
Expand All @@ -390,25 +451,25 @@ fn get_keyword_positions(lines: &Vec<Line>, keyword: &str, is_regex: bool, is_li
let mut hits = Vec::new();

for (line_index, line) in lines.iter().enumerate() {
let combined_text: String = line.spans.iter().map(|span| span.content.as_ref()).collect();
let combined_text = if is_line_number {
combined_text[ignore_head_count..].to_string()
} else {
combined_text
};
let base_combined_text: String = line.spans.iter().map(|span| span.content.as_ref()).collect();
let combined_text: String = base_combined_text.chars().skip(ignore_head_count).collect();

if let Some(re) = &re {

for mat in re.find_iter(&combined_text) {
hits.push((line_index, mat.start() + ignore_head_count, mat.end() + ignore_head_count));
}
} else {
let mut start_position = 0;
let keyword_len = keyword.chars().count();
let combined_text_chars: Vec<char> = combined_text.chars().collect();

while let Some(pos) = combined_text[start_position..].find(keyword) {
let match_start = start_position + pos;
let match_end = match_start + keyword.len();
hits.push((line_index, match_start + ignore_head_count, match_end + ignore_head_count));
start_position = match_end;
while start_position + keyword_len <= combined_text_chars.len() {
let current_slice: String = combined_text_chars[start_position .. (start_position + keyword_len)].iter().collect();
if current_slice == keyword {
hits.push((line_index, start_position + ignore_head_count, start_position + keyword_len + ignore_head_count));
}
start_position += 1;
}
}
}
Expand Down Expand Up @@ -468,7 +529,7 @@ fn wrap_utf8_lines<'a>(lines: &Vec<Line>, width: usize) -> Vec<Line<'a>> {
}

///
fn highlight_text<'a>(lines: &'a Vec<Line>, positions: Vec<(usize, usize, usize)>, selected_keyword: i16, selected_highlight_style: Style, highlight_style: Style) -> Vec<Line<'a>> {
fn highlight_text(lines: Vec<Line>, positions: Vec<(usize, usize, usize)>, selected_keyword: i16, selected_highlight_style: Style, highlight_style: Style) -> Vec<Line> {
let mut new_lines = Vec::new();
let mut current_count:i16 = 0;

Expand All @@ -477,10 +538,11 @@ fn highlight_text<'a>(lines: &'a Vec<Line>, positions: Vec<(usize, usize, usize)
let mut current_pos = 0;

// Get the highlighted position of the corresponding keyword for this line
let line_hits: Vec<(usize, usize, usize)> = positions
.iter()
let line_hits: Vec<(usize, usize)> = positions
.clone()
.into_iter()
.filter(|(line_index, _, _)| *line_index == i)
.cloned()
.map(|(_, start_position, end_position)| (start_position, end_position))
.collect();

// Process each Span to generate a new Span
Expand All @@ -492,26 +554,28 @@ fn highlight_text<'a>(lines: &'a Vec<Line>, positions: Vec<(usize, usize, usize)
// Processing when the highlight range spans Span
if !line_hits.is_empty() {
let mut last_pos = 0;
for (_, start_position, end_position) in line_hits.iter() {

for (start_position, end_position) in line_hits.iter() {
// Ignore if the hit is after the current span
if *start_position >= span_end {
continue;
}

// Calculating highlight_start and highlight_end
let highlight_start = (*start_position).saturating_sub(span_start); // 値が負にならないように調整
let highlight_start = (*start_position).saturating_sub(span_start);
let highlight_end = (*end_position).min(span_end).saturating_sub(span_start);

if highlight_start > last_pos {
let before_highlight_text: String = span_text.chars().skip(last_pos).take(highlight_start - last_pos).collect();
new_spans.push(Span::styled(
span_text[last_pos..highlight_start].to_string(),
before_highlight_text,
span.style,
));
}

let text_str: String = span_text[highlight_start..highlight_end].to_string();
let text_str: String = span_text.chars().skip(highlight_start).take(highlight_end-highlight_start).collect();

if text_str.len() > 0 {
if text_str.chars().count() > 0 {
if current_count == selected_keyword {
new_spans.push(Span::styled(
text_str,
Expand All @@ -529,9 +593,10 @@ fn highlight_text<'a>(lines: &'a Vec<Line>, positions: Vec<(usize, usize, usize)
last_pos = highlight_end;
}

if last_pos < span_text.len() {
if last_pos < span_text.chars().count() {
let after_highlight_text:String = span_text.chars().skip(last_pos).collect();
new_spans.push(Span::styled(
span_text[last_pos..].to_string(),
after_highlight_text,
span.style,
));
}
Expand All @@ -540,7 +605,7 @@ fn highlight_text<'a>(lines: &'a Vec<Line>, positions: Vec<(usize, usize, usize)
new_spans.push(Span::styled(span_text.clone(), span.style));
}

current_pos += span_text.len();
current_pos += span_text.chars().count();
}

new_lines.push(Line::from(new_spans));
Expand Down

0 comments on commit f685ba1

Please sign in to comment.