From f072bdccba0fb5ab0dbfad42eb8233fd8fe80599 Mon Sep 17 00:00:00 2001 From: blacknon Date: Sun, 10 Nov 2024 18:30:08 +0900 Subject: [PATCH 01/10] update comment. --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/main.rs | 9 ++++++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8c4eabe..55feabb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1064,7 +1064,7 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hwatch" -version = "0.3.16" +version = "0.3.17" dependencies = [ "ansi-parser", "ansi_term", diff --git a/Cargo.toml b/Cargo.toml index 07aa85a..240e2d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/src/main.rs b/src/main.rs index bd4b460..7ce96b5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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.17 +// 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) @@ -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をデフォルトにしたり) From a68736f6ce3a158eb7a427b63f6889f0270f1923 Mon Sep 17 00:00:00 2001 From: blacknon Date: Mon, 11 Nov 2024 00:12:05 +0900 Subject: [PATCH 02/10] update. debug and add comment --- src/main.rs | 2 ++ src/watch.rs | 10 ++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main.rs b/src/main.rs index 7ce96b5..a0968ff 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,8 @@ // that can be found in the LICENSE file. // v0.3.17 +// TODO(blacknon): 検索で日本語を入力すると、1文字づつしか入力されないので修正する(最優先) => IMEがネックになっているので、位置文字づつではなく文字列としてまとめて受け付ける変更が必要??? +// TODO(blacknon): 検索で日本語を含む出力結果を検索すると、正しい位置情報が得られないので修正する(最優先) => UTF-8でのpositionが正常に受け付けられることが重要になる? // TODO(blacknon): watchウィンドウの表示を折り返しだけではなく、横方向にスクロールして出力するモードも追加する // TODO(blacknon): コマンドが終了していなくても、インターバル間隔でコマンドを実行する // (パラレルで実行してもよいコマンドじゃないといけないよ、という機能か。投げっぱなしにしてintervalで待つようにするオプションを付ける) diff --git a/src/watch.rs b/src/watch.rs index 555dcd1..29245e4 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -392,7 +392,7 @@ fn get_keyword_positions(lines: &Vec, keyword: &str, is_regex: bool, is_li 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() + combined_text.chars().skip(ignore_head_count).collect() } else { combined_text }; @@ -503,13 +503,14 @@ fn highlight_text<'a>(lines: &'a Vec, positions: Vec<(usize, usize, usize) 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 current_count == selected_keyword { @@ -530,8 +531,9 @@ fn highlight_text<'a>(lines: &'a Vec, positions: Vec<(usize, usize, usize) } if last_pos < span_text.len() { + 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, )); } From a59291a10f739eb968c0ae68ee095090c2a55716 Mon Sep 17 00:00:00 2001 From: blacknon Date: Wed, 13 Nov 2024 00:07:13 +0900 Subject: [PATCH 03/10] update. debug and memo. --- src/main.rs | 1 + src/watch.rs | 64 ++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 46 insertions(+), 19 deletions(-) diff --git a/src/main.rs b/src/main.rs index a0968ff..08d1b22 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,6 +9,7 @@ // TODO(blacknon): コマンドが終了していなくても、インターバル間隔でコマンドを実行する // (パラレルで実行してもよいコマンドじゃないといけないよ、という機能か。投げっぱなしにしてintervalで待つようにするオプションを付ける) // TODO(blacknon): DiffModeをInterfaceで取り扱うようにし、historyへの追加や検索時のhitなどについてもInterface側で取り扱えるようにする。(DiffModeのPlugin化の布石) +// TODO(blacknon): [hwatch 0.3.16 freezes in a narrow terminal when used with --no-help-banner](https://github.com/blacknon/hwatch/issues/169) // v0.3.xx // TODO(blacknon): [FR: add "completion" subcommand](https://github.com/blacknon/hwatch/issues/107) diff --git a/src/watch.rs b/src/watch.rs index 29245e4..9f09d3f 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -371,14 +371,18 @@ impl<'a> WatchArea<'a> { /// fn get_keyword_positions(lines: &Vec, 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; // ^` | ` } + // ` ` | ` + ` | ` - ` if is_diff_head { - ignore_head_count += 4; // ` ` | ` + ` | ` - ` + ignore_head_count += 4; } let re = if is_regex { @@ -390,29 +394,31 @@ fn get_keyword_positions(lines: &Vec, 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.chars().skip(ignore_head_count).collect() - } 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 = 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; } } } + eprintln!("{:?}", hits); + hits } @@ -469,6 +475,8 @@ fn wrap_utf8_lines<'a>(lines: &Vec, width: usize) -> Vec> { /// fn highlight_text<'a>(lines: &'a Vec, positions: Vec<(usize, usize, usize)>, selected_keyword: i16, selected_highlight_style: Style, highlight_style: Style) -> Vec> { + // TODO: spanが1行で別れている場合に、うまくカウントが合計できておらずハイライトがズレてるっぽい + let mut new_lines = Vec::new(); let mut current_count:i16 = 0; @@ -477,12 +485,17 @@ fn highlight_text<'a>(lines: &'a Vec, 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(); + eprintln!("line: {}, line_hits: {:?}", i, line_hits); + + eprintln!("line: {}, span: {:?}", i, line.spans); + // Process each Span to generate a new Span for span in &line.spans { let span_text = span.content.as_ref().to_string(); @@ -492,27 +505,39 @@ fn highlight_text<'a>(lines: &'a Vec, 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 + 1; // 値が負にならないように調整 let highlight_start = (*start_position).saturating_sub(span_start); // 値が負にならないように調整 + // let highlight_end = *end_position + 1; let highlight_end = (*end_position).min(span_end).saturating_sub(span_start); + // eprintln!("span_start: {}, span_end: {}", span_start, span_end); + // eprintln!("start_position: {}, span_start: {}", start_position, span_start); + // eprintln!("highlight:: line: {}, highlight_start: {}, highlight_end: {}", i, highlight_start, highlight_end); + + eprintln!("line: {}, highlight_start: {}, highlight_end: {}, span_text: '{}'", i, highlight_start, highlight_end, span_text); + if highlight_start > last_pos { - let before_highlight_text:String = span_text.chars().skip(last_pos).take(highlight_start-last_pos).collect(); + let before_highlight_text: String = span_text.chars().skip(last_pos).take(highlight_start - last_pos).collect(); + eprintln!("line: {}, before_highlight_text.chars().count(): {}, before_highlight_text: '{}'", i, before_highlight_text.chars().count(), before_highlight_text); new_spans.push(Span::styled( before_highlight_text, span.style, )); } + eprintln!("line: {}, last_pos: {}, span_text: '{}'", i, last_pos, span_text); + let text_str: String = span_text.chars().skip(highlight_start).take(highlight_end-highlight_start).collect(); + eprintln!("line: {}, last_pos: {}, highlight_start: {}, highlight_end: {}, text_str.chars().count(): {}, text_str: {}", i, last_pos, highlight_start, highlight_end, text_str.chars().count(), text_str); - if text_str.len() > 0 { + if text_str.chars().count() > 0 { if current_count == selected_keyword { new_spans.push(Span::styled( text_str, @@ -530,8 +555,9 @@ fn highlight_text<'a>(lines: &'a Vec, 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(); + eprintln!("line: {}, last_pos: {}, after_highlight_text: {}", i, last_pos, after_highlight_text); new_spans.push(Span::styled( after_highlight_text, span.style, From 83ba1a24c6b4d92b6e4c8f6fbeee1321be2368f7 Mon Sep 17 00:00:00 2001 From: blacknon Date: Wed, 13 Nov 2024 17:03:02 +0900 Subject: [PATCH 04/10] update. debug fix. --- src/watch.rs | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/src/watch.rs b/src/watch.rs index 9f09d3f..b8573a4 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -239,6 +239,7 @@ impl<'a> WatchArea<'a> { let highlight_style = Style::new().fg(Color::Black).bg(Color::Yellow); let selected_highlight_style = Style::new().fg(Color::Black).bg(Color::Cyan); + // TODO: 毎回ハイライト処理させると重くなるので、cacheに入れておくように修正する? let block_data = highlight_text(&self.wrap_data, self.keyword_position.clone(), self.selected_keyword, selected_highlight_style, highlight_style); // declare variables @@ -417,8 +418,6 @@ fn get_keyword_positions(lines: &Vec, keyword: &str, is_regex: bool, is_li } } - eprintln!("{:?}", hits); - hits } @@ -492,10 +491,6 @@ fn highlight_text<'a>(lines: &'a Vec, positions: Vec<(usize, usize, usize) .map(|(_, start_position, end_position)| (start_position, end_position)) .collect(); - eprintln!("line: {}, line_hits: {:?}", i, line_hits); - - eprintln!("line: {}, span: {:?}", i, line.spans); - // Process each Span to generate a new Span for span in &line.spans { let span_text = span.content.as_ref().to_string(); @@ -505,6 +500,7 @@ fn highlight_text<'a>(lines: &'a Vec, 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() { // Ignore if the hit is after the current span if *start_position >= span_end { @@ -512,30 +508,18 @@ fn highlight_text<'a>(lines: &'a Vec, positions: Vec<(usize, usize, usize) } // Calculating highlight_start and highlight_end - // let highlight_start = *start_position + 1; // 値が負にならないように調整 let highlight_start = (*start_position).saturating_sub(span_start); // 値が負にならないように調整 - // let highlight_end = *end_position + 1; let highlight_end = (*end_position).min(span_end).saturating_sub(span_start); - // eprintln!("span_start: {}, span_end: {}", span_start, span_end); - // eprintln!("start_position: {}, span_start: {}", start_position, span_start); - // eprintln!("highlight:: line: {}, highlight_start: {}, highlight_end: {}", i, highlight_start, highlight_end); - - eprintln!("line: {}, highlight_start: {}, highlight_end: {}, span_text: '{}'", i, highlight_start, highlight_end, span_text); - if highlight_start > last_pos { let before_highlight_text: String = span_text.chars().skip(last_pos).take(highlight_start - last_pos).collect(); - eprintln!("line: {}, before_highlight_text.chars().count(): {}, before_highlight_text: '{}'", i, before_highlight_text.chars().count(), before_highlight_text); new_spans.push(Span::styled( before_highlight_text, span.style, )); } - eprintln!("line: {}, last_pos: {}, span_text: '{}'", i, last_pos, span_text); - let text_str: String = span_text.chars().skip(highlight_start).take(highlight_end-highlight_start).collect(); - eprintln!("line: {}, last_pos: {}, highlight_start: {}, highlight_end: {}, text_str.chars().count(): {}, text_str: {}", i, last_pos, highlight_start, highlight_end, text_str.chars().count(), text_str); if text_str.chars().count() > 0 { if current_count == selected_keyword { @@ -557,7 +541,6 @@ fn highlight_text<'a>(lines: &'a Vec, positions: Vec<(usize, usize, usize) if last_pos < span_text.chars().count() { let after_highlight_text:String = span_text.chars().skip(last_pos).collect(); - eprintln!("line: {}, last_pos: {}, after_highlight_text: {}", i, last_pos, after_highlight_text); new_spans.push(Span::styled( after_highlight_text, span.style, @@ -568,7 +551,7 @@ fn highlight_text<'a>(lines: &'a Vec, 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)); From 35100037c71f8ade695a7867fccf863ea3eba935 Mon Sep 17 00:00:00 2001 From: blacknon Date: Wed, 13 Nov 2024 17:58:10 +0900 Subject: [PATCH 05/10] update. debug fix. --- src/watch.rs | 64 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 8 deletions(-) diff --git a/src/watch.rs b/src/watch.rs index b8573a4..728fb93 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -16,6 +16,10 @@ use tui::{ 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. @@ -27,6 +31,9 @@ pub struct WatchArea<'a> { /// Wrapped data. wrap_data: Vec>, + /// highlighted data. + highlight_data: Vec>, + /// search keyword. keyword: String, @@ -73,6 +80,8 @@ impl<'a> WatchArea<'a> { wrap_data: vec![Line::from("")], + highlight_data: vec![Line::from("")], + keyword: String::from(""), keyword_is_regex: false, @@ -121,6 +130,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 + ); } /// @@ -171,6 +189,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 + ); } /// @@ -179,7 +206,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) { @@ -203,6 +238,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 + ); } /// @@ -231,16 +275,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); - - // TODO: 毎回ハイライト処理させると重くなるので、cacheに入れておくように修正する? - 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<'_>; @@ -473,7 +521,7 @@ fn wrap_utf8_lines<'a>(lines: &Vec, width: usize) -> Vec> { } /// -fn highlight_text<'a>(lines: &'a Vec, positions: Vec<(usize, usize, usize)>, selected_keyword: i16, selected_highlight_style: Style, highlight_style: Style) -> Vec> { +fn highlight_text(lines: Vec, positions: Vec<(usize, usize, usize)>, selected_keyword: i16, selected_highlight_style: Style, highlight_style: Style) -> Vec { // TODO: spanが1行で別れている場合に、うまくカウントが合計できておらずハイライトがズレてるっぽい let mut new_lines = Vec::new(); From 069772d0a4353546bdf242cc337ea6bb14a9b1ae Mon Sep 17 00:00:00 2001 From: blacknon Date: Wed, 13 Nov 2024 21:59:56 +0900 Subject: [PATCH 06/10] update. debug fix. --- src/app.rs | 4 +++- src/view.rs | 5 ----- src/watch.rs | 8 +++----- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/app.rs b/src/app.rs index 3b54e9b..07995c8 100644 --- a/src/app.rs +++ b/src/app.rs @@ -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; @@ -412,7 +413,8 @@ impl<'a> App<'a> { match self.input_mode { InputMode::Filter | InputMode::RegexFilter => { // - let input_text_x = self.header_area.input_text.len() as u16 + 1; + // TODO: cursor位置をUTF-8対応の文字幅にする + 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 diff --git a/src/view.rs b/src/view.rs index ea02c72..9ebac29 100644 --- a/src/view.rs +++ b/src/view.rs @@ -314,11 +314,6 @@ fn send_input(tx: Sender) -> 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(()) } diff --git a/src/watch.rs b/src/watch.rs index 728fb93..a1383dd 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -453,8 +453,8 @@ fn get_keyword_positions(lines: &Vec, keyword: &str, is_regex: bool, is_li } } else { let mut start_position = 0; - let keyword_len = keyword.chars().count(); // キーワードの文字数を取得 - let combined_text_chars: Vec = combined_text.chars().collect(); // 入力テキストを文字ベクタに変換 + let keyword_len = keyword.chars().count(); + let combined_text_chars: Vec = combined_text.chars().collect(); while start_position + keyword_len <= combined_text_chars.len() { let current_slice: String = combined_text_chars[start_position .. (start_position + keyword_len)].iter().collect(); @@ -522,8 +522,6 @@ fn wrap_utf8_lines<'a>(lines: &Vec, width: usize) -> Vec> { /// fn highlight_text(lines: Vec, positions: Vec<(usize, usize, usize)>, selected_keyword: i16, selected_highlight_style: Style, highlight_style: Style) -> Vec { - // TODO: spanが1行で別れている場合に、うまくカウントが合計できておらずハイライトがズレてるっぽい - let mut new_lines = Vec::new(); let mut current_count:i16 = 0; @@ -556,7 +554,7 @@ fn highlight_text(lines: Vec, positions: Vec<(usize, usize, usize)>, selec } // 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 { From f4830b8f0741746bf9d07629b5f2e6541d7bc7a4 Mon Sep 17 00:00:00 2001 From: blacknon Date: Wed, 13 Nov 2024 22:34:45 +0900 Subject: [PATCH 07/10] update. debug fix. --- src/app.rs | 2 -- src/watch.rs | 9 +++++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/app.rs b/src/app.rs index 07995c8..6dd1629 100644 --- a/src/app.rs +++ b/src/app.rs @@ -412,8 +412,6 @@ impl<'a> App<'a> { // match input_mode match self.input_mode { InputMode::Filter | InputMode::RegexFilter => { - // - // TODO: cursor位置をUTF-8対応の文字幅にする let input_text_x = self.header_area.input_text.width() as u16 + 1; let input_text_y = self.header_area.area.y + 1; diff --git a/src/watch.rs b/src/watch.rs index a1383dd..0989c71 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -150,6 +150,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 + ); } /// From 3c88fe950581027a1c2795b044bfbe4fb7665a9a Mon Sep 17 00:00:00 2001 From: blacknon Date: Wed, 13 Nov 2024 22:35:49 +0900 Subject: [PATCH 08/10] update. debug #169. --- src/header.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/header.rs b/src/header.rs index 424ec06..122d26e 100644 --- a/src/header.rs +++ b/src/header.rs @@ -187,7 +187,7 @@ 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 From 97a1b67bb375a79de9d379a7ad818b660380d80c Mon Sep 17 00:00:00 2001 From: blacknon Date: Wed, 13 Nov 2024 22:45:20 +0900 Subject: [PATCH 09/10] update. --- src/header.rs | 2 +- src/main.rs | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/header.rs b/src/header.rs index 122d26e..744c101 100644 --- a/src/header.rs +++ b/src/header.rs @@ -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にする(生数字で雑計算だとわけわからん) diff --git a/src/main.rs b/src/main.rs index 08d1b22..a8d77f3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,14 +2,11 @@ // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. -// v0.3.17 -// TODO(blacknon): 検索で日本語を入力すると、1文字づつしか入力されないので修正する(最優先) => IMEがネックになっているので、位置文字づつではなく文字列としてまとめて受け付ける変更が必要??? -// TODO(blacknon): 検索で日本語を含む出力結果を検索すると、正しい位置情報が得られないので修正する(最優先) => UTF-8でのpositionが正常に受け付けられることが重要になる? +// v0.3.18 // TODO(blacknon): watchウィンドウの表示を折り返しだけではなく、横方向にスクロールして出力するモードも追加する // TODO(blacknon): コマンドが終了していなくても、インターバル間隔でコマンドを実行する // (パラレルで実行してもよいコマンドじゃないといけないよ、という機能か。投げっぱなしにしてintervalで待つようにするオプションを付ける) // TODO(blacknon): DiffModeをInterfaceで取り扱うようにし、historyへの追加や検索時のhitなどについてもInterface側で取り扱えるようにする。(DiffModeのPlugin化の布石) -// TODO(blacknon): [hwatch 0.3.16 freezes in a narrow terminal when used with --no-help-banner](https://github.com/blacknon/hwatch/issues/169) // v0.3.xx // TODO(blacknon): [FR: add "completion" subcommand](https://github.com/blacknon/hwatch/issues/107) From 9500e7586a6ade7cc6fa619e933e8c5d717163af Mon Sep 17 00:00:00 2001 From: blacknon Date: Wed, 13 Nov 2024 22:51:39 +0900 Subject: [PATCH 10/10] update header. support widht utf-8 --- src/header.rs | 15 ++++++++++++++- src/watch.rs | 1 - 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/header.rs b/src/header.rs index 744c101..03f1b31 100644 --- a/src/header.rs +++ b/src/header.rs @@ -14,6 +14,7 @@ use tui::{ Frame, prelude::Line, }; +use unicode_width::UnicodeWidthStr; // local module use crate::app::{ActiveArea, InputMode}; @@ -195,7 +196,8 @@ impl<'a> HeaderArea<'a> { } 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() { @@ -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) + } +} diff --git a/src/watch.rs b/src/watch.rs index 0989c71..d136d41 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -12,7 +12,6 @@ use tui::{ widgets::{Block, Borders, Paragraph, Scrollbar, ScrollbarOrientation, ScrollbarState}, Frame }; - use regex::Regex; use unicode_segmentation::UnicodeSegmentation;