1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
|
use std::io::{self, Stdout, Write};
use anyhow::Result;
use crossterm::{
cursor,
event::{self, Event, KeyCode, KeyModifiers},
queue, style,
terminal::{self, disable_raw_mode, enable_raw_mode},
};
use crate::utils::paste;
pub fn edit() -> Result<String> {
enable_raw_mode()?;
let mut stdout = io::stdout();
let ret = edit_inner(&mut stdout);
// restore terminal
disable_raw_mode()?;
ret
}
fn edit_inner(writer: &mut Stdout) -> Result<String> {
let mut session = Session::new(writer);
loop {
let evt = event::read()?;
if let Event::Key(key) = evt {
match key.code {
KeyCode::Char('c') if key.modifiers == KeyModifiers::CONTROL => {
// quit
return Ok(String::new());
}
KeyCode::Char('d') if key.modifiers == KeyModifiers::CONTROL => {
// submit
return Ok(session.buffer);
}
KeyCode::Char('v') if key.modifiers == KeyModifiers::CONTROL => {
let content = paste()?;
session.push_str(&content)?;
}
KeyCode::Char(c)
if matches!(key.modifiers, KeyModifiers::NONE | KeyModifiers::SHIFT) =>
{
session.push(c)?;
}
KeyCode::Enter => {
session.push('\n')?;
}
_ => {}
}
}
session.flush()?;
}
}
struct Session<'a, T: Write> {
writer: &'a mut T,
buffer: String,
dirty: bool,
}
impl<'a, T: Write> Session<'a, T> {
fn new<'b: 'a>(writer: &'b mut T) -> Self {
Self {
buffer: String::new(),
writer,
dirty: false,
}
}
fn push(&mut self, ch: char) -> io::Result<()> {
if ch == '\n' {
self.new_line()?;
} else {
queue!(self.writer, style::Print(ch))?;
}
self.buffer.push(ch);
self.dirty = true;
Ok(())
}
fn push_str(&mut self, text: &str) -> io::Result<()> {
for line in text.lines() {
if !line.is_empty() {
queue!(self.writer, style::Print(line))?;
}
self.new_line()?;
}
Ok(())
}
fn new_line(&mut self) -> io::Result<()> {
let (_, y) = cursor::position()?;
let (_, h) = terminal::size()?;
if y == h - 1 {
queue!(self.writer, terminal::ScrollUp(1), cursor::MoveTo(0, y))?;
} else {
queue!(self.writer, cursor::MoveToNextLine(1))?;
}
Ok(())
}
fn flush(&mut self) -> io::Result<()> {
if self.dirty {
return self.writer.flush();
}
Ok(())
}
}
|