From 284b7eae017f89b56ee4deb7e66a7d3029c5cdb3 Mon Sep 17 00:00:00 2001
From: gwenn <gtreguier@gmail.com>
Date: Sun, 28 Aug 2016 16:08:14 +0200
Subject: [PATCH] More page completions

---
 src/lib.rs         | 73 +++++++++++++++++++++++++++++++++++++++++-----
 src/tty/unix.rs    | 16 ++++++++--
 src/tty/windows.rs | 19 ++++++++++--
 3 files changed, 95 insertions(+), 13 deletions(-)

diff --git a/src/lib.rs b/src/lib.rs
index c101a134..a3cd8ab2 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -599,13 +599,13 @@ fn complete_line<R: Read>(rdr: &mut tty::RawReader<R>,
         if key != KeyPress::Tab {
             return Ok(Some(key));
         }
+        // move cursor to EOL to avoid overwriting the command line
+        let save_pos = s.line.pos();
+        try!(edit_move_end(s));
+        s.line.set_pos(save_pos);
         // we got a second tab, maybe show list of possible completions
         let mut show_completions = true;
         if candidates.len() > config.completion_prompt_limit() {
-            // move cursor to EOL to avoid overwriting the command line
-            let save_pos = s.line.pos();
-            try!(edit_move_end(s));
-            s.line.set_pos(save_pos);
             let msg = format!("\nDisplay all {} possibilities? (y or n)", candidates.len());
             try!(write_and_flush(s.out, msg.as_bytes()));
             s.old_rows += 1;
@@ -621,7 +621,7 @@ fn complete_line<R: Read>(rdr: &mut tty::RawReader<R>,
             };
         }
         if show_completions {
-            page_completions(rdr, s)
+            page_completions(rdr, s, &candidates)
         } else {
             try!(s.refresh_line());
             Ok(None)
@@ -631,9 +631,66 @@ fn complete_line<R: Read>(rdr: &mut tty::RawReader<R>,
     }
 }
 
-fn page_completions<R: Read>(r: &mut tty::RawReader<R>,
-                                s: &mut State)
-                                -> Result<Option<KeyPress>> {
+fn page_completions<R: Read>(rdr: &mut tty::RawReader<R>,
+                             s: &mut State,
+                             candidates: &[String])
+                             -> Result<Option<KeyPress>> {
+    use std::cmp;
+    use unicode_width::UnicodeWidthStr;
+
+    let min_col_pad = 2;
+    let max_width = cmp::min(s.cols,
+                             candidates.into_iter()
+                                 .map(|s| UnicodeWidthStr::width(s.as_str()))
+                                 .max()
+                                 .unwrap() + min_col_pad);
+    let num_cols = s.cols / max_width;
+
+    let mut pause_row = tty::get_rows(s.output_handle) - 1;
+    let num_rows = (candidates.len() + num_cols - 1) / num_cols;
+    let mut ab = String::new();
+    for row in 0..num_rows {
+        if row == pause_row {
+            try!(write_and_flush(s.out, b"\n--More--"));
+            let mut key = KeyPress::Null;
+            while key != KeyPress::Char('y') && key != KeyPress::Char('Y') &&
+                  key != KeyPress::Char('n') && key != KeyPress::Char('N') &&
+                  key != KeyPress::Char('q') &&
+                  key != KeyPress::Char('Q') &&
+                  key != KeyPress::Char(' ') &&
+                  key != KeyPress::Backspace && key != KeyPress::Enter {
+                key = try!(rdr.next_key(true));
+            }
+            match key {
+                KeyPress::Char('y') |
+                KeyPress::Char('Y') |
+                KeyPress::Char(' ') => {
+                    pause_row += tty::get_rows(s.output_handle) - 1;
+                }
+                KeyPress::Enter => {
+                    pause_row += 1;
+                }
+                _ => break,
+            }
+        } else {
+            try!(write_and_flush(s.out, b"\n"));
+        }
+        ab.clear();
+        for col in 0..num_cols {
+            let i = (col * num_rows) + row;
+            if i < candidates.len() {
+                let candidate = &candidates[i];
+                ab.push_str(candidate);
+                let width = UnicodeWidthStr::width(candidate.as_str());
+                if ((col + 1) * num_rows) + row < candidates.len() {
+                    for _ in width..max_width {
+                        ab.push(' ');
+                    }
+                }
+            }
+        }
+        try!(write_and_flush(s.out, ab.as_bytes()));
+    }
     // TODO
     Ok(None)
 }
diff --git a/src/tty/unix.rs b/src/tty/unix.rs
index 106d3120..ff1d0b31 100644
--- a/src/tty/unix.rs
+++ b/src/tty/unix.rs
@@ -32,6 +32,18 @@ const TIOCGWINSZ: libc::c_int = 0x5413;
 /// Try to get the number of columns in the current terminal,
 /// or assume 80 if it fails.
 pub fn get_columns(_: Handle) -> usize {
+    let (cols, _) = get_win_size();
+    cols
+}
+
+/// Try to get the number of rows in the current terminal,
+/// or assume 24 if it fails.
+pub fn get_rows(_: Handle) -> usize {
+    let (_, rows) = get_win_size();
+    rows
+}
+
+fn get_win_size() -> (usize, usize) {
     use std::mem::zeroed;
     use libc::c_ushort;
 
@@ -46,8 +58,8 @@ pub fn get_columns(_: Handle) -> usize {
 
         let mut size: winsize = zeroed();
         match libc::ioctl(STDOUT_FILENO, TIOCGWINSZ, &mut size) {
-            0 => size.ws_col as usize, // TODO getCursorPosition
-            _ => 80,
+            0 => (size.ws_col as usize, size.ws_row as usize), // TODO getCursorPosition
+            _ => (80, 24),
         }
     }
 }
diff --git a/src/tty/windows.rs b/src/tty/windows.rs
index 3d01af53..7bcbdd56 100644
--- a/src/tty/windows.rs
+++ b/src/tty/windows.rs
@@ -40,12 +40,25 @@ macro_rules! check {
     };
 }
 
-/// Try to get the number of columns in the current terminal, or assume 80 if it fails.
+/// Try to get the number of columns in the current terminal,
+/// or assume 80 if it fails.
 pub fn get_columns(handle: Handle) -> usize {
+    let (cols, _) = get_win_size();
+    cols
+}
+
+/// Try to get the number of rows in the current terminal,
+/// or assume 24 if it fails.
+pub fn get_rows(_: Handle) -> usize {
+    let (_, rows) = get_win_size();
+    rows
+}
+
+fn get_win_size() -> (usize, usize) {
     let mut info = unsafe { mem::zeroed() };
     match unsafe { kernel32::GetConsoleScreenBufferInfo(handle, &mut info) } {
-        0 => 80,
-        _ => info.dwSize.X as usize,
+        0 => (80, 24),
+        _ => (info.dwSize.X as usize, 1 + inf.srWindow.Bottom - inf.srWindow.Top),
     }
 }
 
-- 
GitLab