LoginSignup
2
0

More than 1 year has passed since last update.

Rustでlessクローンを作りたい その3

Last updated at Posted at 2021-12-09

Rustでlessクローン実装の3日目です。
気分を変えて検索機能を入れてみます。

初めに考えていた戦略通りripgrepを使います。

そのまえにclapバージョン3.0.0の対応

ripgrep関連の機能を入れようとcargo updateするとclapのバージョン 3.0.0-rc.1 にアップデートされてしまいました。
各機能がfeaturesに分離されたようで、そのままではビルドできなくなりました。
というわけで、以下のようなパッチを当てました。

diff --git a/1207_less_clone/Cargo.toml b/1207_less_clone/Cargo.toml
index 142d615..b131d5f 100644
--- a/1207_less_clone/Cargo.toml
+++ b/1207_less_clone/Cargo.toml
@@ -6,6 +6,6 @@ edition = "2021"
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

 [dependencies]
-clap = "3.0.0-beta.5"
+clap = { version = "3.0.0-rc.1", features = ["derive"] }
 crossterm = "0.22.1"
 ropey = "1.3.1"

これでビルドできるようになりました。

ripgrep関連の機能を追加

まずはripgrepのgrepcrateを組み込みます。

diff --git a/1207_less_clone/Cargo.toml b/1207_less_clone/Cargo.toml
index b131d5f..d95294a 100644
--- a/1207_less_clone/Cargo.toml
+++ b/1207_less_clone/Cargo.toml
@@ -9,3 +9,4 @@ edition = "2021"
 clap = { version = "3.0.0-rc.1", features = ["derive"] }
 crossterm = "0.22.1"
 ropey = "1.3.1"
+grep = "0.2"

これでgrep-XXXなcrateを呼び出せます。

検索機能を実装してみる。

use grep::searcher::SearcherBuilder;
use grep::regex::RegexMatcher;
use grep::searcher::sinks::UTF8;

fn search(filename: &str, search_word: &str) -> Result<Vec<(u64, String)>> {
    let matcher = RegexMatcher::new(search_word).unwrap();
    let mut matches: Vec<(u64, String)> = vec![];
    let mut searcher = SearcherBuilder::new().build();
    searcher.search_path(&matcher, filename, UTF8(|lnum, line| {
        matches.push((lnum, line.to_string()));
        Ok(true)
    }))?;
    Ok(matches)
}

ripgrep系の機能は、検索機能自体?を提供するSearcher、文字列(regexp)マッチ機能を提供するMatcher、マッチしたときにどのように処理するかを司るSinkの三点セットになっています。

/WORD↩︎WORD文字列を取り出して、この実装した fn search()に入力します。
パッチはこんな感じ。

diff --git a/1207_less_clone/src/main.rs b/1207_less_clone/src/main.rs
index 174aefd..b975dd5 100644
--- a/1207_less_clone/src/main.rs
+++ b/1207_less_clone/src/main.rs
@@ -52,6 +52,7 @@ fn less_loop(filename: &str) -> Result<()> {
     let line_count = lines.len_lines();
     let mut is_search_mode = false;

+    let mut search_word_vec: Vec<char> = [].to_vec();
     let (_, window_rows) = terminal::size()?;
     let mut display_lines = DisplayLines { start: 0, end: 0 };

@@ -77,6 +78,40 @@ fn less_loop(filename: &str) -> Result<()> {
                 }) => {
                     is_search_mode = false;
                     execute!(stdout(), RestorePosition)?;
+                    search_word_vec = Vec::new();
+                },
+                Event::Key(KeyEvent {
+                    code: KeyCode::Enter,
+                    modifiers: _,
+                }) => {
+                    let search_word = String::from_iter(search_word_vec.clone());
+                    let result = search(filename, search_word.as_str())?;
+                    if result.len() > 0 {
+                        // jump to result line
+                        let (lnum, _) = result[0];
+                        execute!(stdout(), SavePosition, Clear(ClearType::All))?;
+
+                        for idx in lnum..(lnum+window_rows as u64) {
+                            println!("{}", lines.line(idx as usize));
+                            execute!(stdout(), MoveTo(0, idx as u16))?;
+                            if idx as usize >= line_count - 1 {
+                                break
+                            }
+                            *display_lines.end_mut() = idx;
+                        }
+                        execute!(stdout(), RestorePosition)?;
+                    }
+
+                    is_search_mode = false;
+                    execute!(stdout(), RestorePosition)?;
+                    search_word_vec = Vec::new();
+                },
+                Event::Key(KeyEvent {
+                    code: KeyCode::Char(c),
+                    modifiers: _,
+                }) => {
+                    search_word_vec.push(c);
+                    execute!(stdout(), Print(c))?;
                 },
                 _ => (),
             };

1文字ずつ入力を拾って、search_word_vecに検索ワードを入れる。
Enter入力されたらsearch_word_vecを文字列にしてfn search()に渡す。
fn search()から結果が戻ってくるので、いい感じにジャンプする。(今は結果があるかチェックして0番の結果を無条件で使う感じ。)

という流れです。

ripgrepから返される行番号の型がu64なので、そのあたりはu64で保持するように変更しました。

動作確認

いちおここまでで検索してジャンプっぽい動きができました。

input-search-word.png

ただジャンプして表示するとバグりまくってましたorz
search-jump.png

今日は時間切れでここまで。

今日の成果
https://github.com/hhatto/advent-2021/compare/3e90ddd468b1b737037cacdbe272d952b29c6d15..bdfa30aa516e6ec97ef2971486bc761923f4ded6

おわりに

検索してみるとlessより断然検索が早くていい感じです!!
モチベーションがさらに上がってきました:v::v:

検索後の表示周りがバグりまくってるので、明日はそのあたりを整えたいと思います。

今日の感想としては、terminalさわる系のツールはprintデバッグ派の自分には動作確認が大変。
いい感じな方法ないのかな。。

まぁ悩みつつ、テンション上げつつ明日もやっていこうと思います。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0