Skip to content

Commit e750152

Browse files
Fix behavior of read_line_initial_text (#190)
1 parent ed4ca61 commit e750152

File tree

1 file changed

+58
-25
lines changed

1 file changed

+58
-25
lines changed

src/term.rs

Lines changed: 58 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::fmt::{Debug, Display};
22
use std::io::{self, Read, Write};
3-
use std::sync::{Arc, Mutex};
3+
use std::sync::{Arc, Mutex, RwLock};
44

55
#[cfg(unix)]
66
use std::os::unix::io::{AsRawFd, RawFd};
@@ -41,6 +41,8 @@ pub enum TermTarget {
4141
pub struct TermInner {
4242
target: TermTarget,
4343
buffer: Option<Mutex<Vec<u8>>>,
44+
prompt: RwLock<String>,
45+
prompt_guard: Mutex<()>,
4446
}
4547

4648
/// The family of the terminal.
@@ -149,6 +151,8 @@ impl Term {
149151
Term::with_inner(TermInner {
150152
target: TermTarget::Stdout,
151153
buffer: None,
154+
prompt: RwLock::new(String::new()),
155+
prompt_guard: Mutex::new(()),
152156
})
153157
}
154158

@@ -158,6 +162,8 @@ impl Term {
158162
Term::with_inner(TermInner {
159163
target: TermTarget::Stderr,
160164
buffer: None,
165+
prompt: RwLock::new(String::new()),
166+
prompt_guard: Mutex::new(()),
161167
})
162168
}
163169

@@ -166,6 +172,8 @@ impl Term {
166172
Term::with_inner(TermInner {
167173
target: TermTarget::Stdout,
168174
buffer: Some(Mutex::new(vec![])),
175+
prompt: RwLock::new(String::new()),
176+
prompt_guard: Mutex::new(()),
169177
})
170178
}
171179

@@ -174,6 +182,8 @@ impl Term {
174182
Term::with_inner(TermInner {
175183
target: TermTarget::Stderr,
176184
buffer: Some(Mutex::new(vec![])),
185+
prompt: RwLock::new(String::new()),
186+
prompt_guard: Mutex::new(()),
177187
})
178188
}
179189

@@ -201,6 +211,8 @@ impl Term {
201211
style,
202212
}),
203213
buffer: None,
214+
prompt: RwLock::new(String::new()),
215+
prompt_guard: Mutex::new(()),
204216
})
205217
}
206218

@@ -231,14 +243,21 @@ impl Term {
231243

232244
/// Write a string to the terminal and add a newline.
233245
pub fn write_line(&self, s: &str) -> io::Result<()> {
246+
let prompt = self.inner.prompt.read().unwrap();
247+
if !prompt.is_empty() {
248+
self.clear_line()?;
249+
}
234250
match self.inner.buffer {
235251
Some(ref mutex) => {
236252
let mut buffer = mutex.lock().unwrap();
237253
buffer.extend_from_slice(s.as_bytes());
238254
buffer.push(b'\n');
255+
buffer.extend_from_slice(prompt.as_bytes());
239256
Ok(())
240257
}
241-
None => self.write_through(format!("{}\n", s).as_bytes()),
258+
None => {
259+
self.write_through(format!("{}\n{}", s, prompt.as_str()).as_bytes())
260+
}
242261
}
243262
}
244263

@@ -288,40 +307,54 @@ impl Term {
288307
}
289308

290309
/// Read one line of input with initial text.
291-
///
310+
///
311+
/// This method blocks until no other thread is waiting for this read_line
312+
/// before reading a line from the terminal.
292313
/// This does not include the trailing newline. If the terminal is not
293314
/// user attended the return value will always be an empty string.
294315
pub fn read_line_initial_text(&self, initial: &str) -> io::Result<String> {
295316
if !self.is_tty {
296317
return Ok("".into());
297318
}
298-
self.write_str(initial)?;
319+
*self.inner.prompt.write().unwrap() = initial.to_string();
320+
// use a guard in order to prevent races with other calls to read_line_initial_text
321+
let _guard = self.inner.prompt_guard.lock().unwrap();
299322

300-
let mut chars: Vec<char> = initial.chars().collect();
323+
self.write_str(initial)?;
301324

302-
loop {
303-
match self.read_key()? {
304-
Key::Backspace => {
305-
if chars.pop().is_some() {
306-
self.clear_chars(1)?;
325+
fn read_line_internal(slf: &Term, initial: &str) -> io::Result<String> {
326+
let prefix_len = initial.len();
327+
328+
let mut chars: Vec<char> = initial.chars().collect();
329+
330+
loop {
331+
match slf.read_key()? {
332+
Key::Backspace => {
333+
if prefix_len < chars.len() && chars.pop().is_some() {
334+
slf.clear_chars(1)?;
335+
}
336+
slf.flush()?;
337+
}
338+
Key::Char(chr) => {
339+
chars.push(chr);
340+
let mut bytes_char = [0; 4];
341+
chr.encode_utf8(&mut bytes_char);
342+
slf.write_str(chr.encode_utf8(&mut bytes_char))?;
343+
slf.flush()?;
344+
}
345+
Key::Enter => {
346+
slf.write_through(format!("\n{}", initial).as_bytes())?;
347+
break;
348+
}
349+
_ => (),
307350
}
308-
self.flush()?;
309-
}
310-
Key::Char(chr) => {
311-
chars.push(chr);
312-
let mut bytes_char = [0; 4];
313-
chr.encode_utf8(&mut bytes_char);
314-
self.write_str(chr.encode_utf8(&mut bytes_char))?;
315-
self.flush()?;
316-
}
317-
Key::Enter => {
318-
self.write_line("")?;
319-
break;
320-
}
321-
_ => (),
322351
}
352+
Ok(chars.iter().skip(prefix_len).collect::<String>())
323353
}
324-
Ok(chars.iter().collect::<String>())
354+
let ret = read_line_internal(self, initial);
355+
356+
*self.inner.prompt.write().unwrap() = String::new();
357+
ret
325358
}
326359

327360
/// Read a line of input securely.

0 commit comments

Comments
 (0)