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



https://qiita.com/hhatto/items/f1f6d1abe1cec7c0441c

昨日の続きをやります。

### Ctrl-uまたはCtrl-dでカーソルジャンプ

10行くらい一気に上下に移動したいです。
Ctrl-uで上に、Ctrl-dで下に、それぞれ10行ジャンプさせます。

crosstermではキーイベント取った際のmodifiersでCtrlやAltキーを判別して処理分岐させれるのでそれを使います。
以下のような感じです。

```diff
diff --git a/1207_less_clone/src/main.rs b/1207_less_clone/src/main.rs
index c32e219..7813975 100644
--- a/1207_less_clone/src/main.rs
+++ b/1207_less_clone/src/main.rs
@@ -4,7 +4,7 @@ use std::fs::File;
 use clap::{Parser};
 use crossterm::{
     cursor::{DisableBlinking, MoveTo, MoveUp, MoveDown, MoveLeft, MoveRight, RestorePosition, SavePosition},
-    event::{read, Event, KeyCode, KeyEvent},
+    event::{read, Event, KeyCode, KeyEvent, KeyModifiers},
     execute,
     style::Print,
     terminal,
@@ -68,6 +68,14 @@ fn less_loop(filename: &str) -> Result<()> {
                     code: KeyCode::Char('l'),
                     modifiers: _,
                 }) => execute!(stdout(), MoveRight(1))?,
+                Event::Key(KeyEvent {
+                    code: KeyCode::Char('u'),
+                    modifiers: KeyModifiers::CONTROL,
+                }) => execute!(stdout(), MoveUp(10))?,
+                Event::Key(KeyEvent {
+                    code: KeyCode::Char('d'),
+                    modifiers: KeyModifiers::CONTROL,
+                }) => execute!(stdout(), MoveDown(10))?,
                 Event::Key(KeyEvent {
                     code: KeyCode::Char('/'),
                     modifiers: _,
```

### カーソル移動とともにスクロール表示

jkで上下に移動できるので、ターミナルの上端でkキーで上行こうとしたらスクロールしつつ前の行を表示。
逆にターミナル下端でjキーで下行こうとしたらスクロールしつつ次の行を表示。
という感じにしたい。

表示してる行数の上端と下端を管理する構造体を作った。

```rust
struct DisplayLines {
    start: u16,
    end: u16,
}

impl DisplayLines {
    fn start_mut(&mut self) -> &mut u16 {
        &mut self.start
    }
    fn end_mut(&mut self) -> &mut u16 {
        &mut self.end
    }
}
```

初期ファイル読み込み時に合わせて初期化しておく。

```diff
 fn less_loop(filename: &str) -> Result<()> {
     let f = File::open(filename)?;
     let lines = ropey::Rope::from_reader(f)?;
     let line_count = lines.len_lines();
     let mut is_search_mode = false;

-    let (_, rows) = terminal::size()?;
+    let (_, window_rows) = terminal::size()?;
+    let mut display_lines = DisplayLines { start: 0, end: 0 };

-    for idx in 0..rows {
+    for idx in 0..window_rows {
         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(), MoveTo(0, 0))?;
```

で、スクロールと表示を実装。素直にというか思ったまんまにとりあえず実装。

```diff
@@ -50,7 +66,9 @@ fn less_loop(filename: &str) -> Result<()> {
                 _ => (),
             };
         } else {
+            let (_, row) = position()?;
             execute!(stdout(), SavePosition)?;
+
             match event {
                 Event::Key(KeyEvent {
                     code: KeyCode::Char('h'),
@@ -59,11 +77,29 @@ fn less_loop(filename: &str) -> Result<()> {
                 Event::Key(KeyEvent {
                     code: KeyCode::Char('j'),
                     modifiers: _,
-                }) => execute!(stdout(), MoveDown(1))?,
+                }) => {
+                    if window_rows-3 == row {
+                        *display_lines.start_mut() = display_lines.start + 1;
+                        *display_lines.end_mut() = display_lines.end + 1;
+                        let l = lines.line(display_lines.end.into());
+                        execute!(stdout(), ScrollUp(1), Print(l), RestorePosition)?;
+                    } else {
+                        execute!(stdout(), MoveDown(1))?;
+                    }
+                },
                 Event::Key(KeyEvent {
                     code: KeyCode::Char('k'),
                     modifiers: _,
-                }) => execute!(stdout(), MoveUp(1))?,
+                }) => {
+                    if 0 == row {
+                        *display_lines.start_mut() = display_lines.start - 1;
+                        *display_lines.end_mut() = display_lines.end - 1;
+                        let l = lines.line(display_lines.end.into());
+                        execute!(stdout(), ScrollDown(1), Print(l), RestorePosition)?;
+                    } else {
+                        execute!(stdout(), MoveUp(1))?;
+                    }
+                },
                 Event::Key(KeyEvent {
                     code: KeyCode::Char('l'),
                     modifiers: _,
```

ここまででスクロールっぽい動きはするようになった。
割と読み込みは速いので、もうちょっと表示周り改善してから検索機能の実装に移りたいところ。

ちなみにこれを動かすとファイル終端からさらにjキーでスクロールしようとすると、そんな行はねーよって言われてパニックで終了する。
まだまだ改善が必要である。

今日の差分はこんな感じ。今週あまり時間取れなさそうでかなりゆっくりなペースになりそう。
https://github.com/hhatto/advent-2021/compare/30a514cbf3ea7ce409af0a248ff11fc7221b12c5..3e90ddd468b1b737037cacdbe272d952b29c6d15

今日はここまで。