(index<- ) ./libextra/rl.rs
1 // Copyright 2012 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use std::c_str::ToCStr;
12 use std::libc::{c_char, c_int};
13 use std::{local_data, str, rt};
14 use std::unstable::finally::Finally;
15
16 pub mod rustrt {
17 use std::libc::{c_char, c_int};
18
19 externfn!(fn linenoise(prompt: *c_char) -> *c_char)
20 externfn!(fn linenoiseHistoryAdd(line: *c_char) -> c_int)
21 externfn!(fn linenoiseHistorySetMaxLen(len: c_int) -> c_int)
22 externfn!(fn linenoiseHistorySave(file: *c_char) -> c_int)
23 externfn!(fn linenoiseHistoryLoad(file: *c_char) -> c_int)
24 externfn!(fn linenoiseSetCompletionCallback(callback: extern "C" fn(*i8, *())))
25 externfn!(fn linenoiseAddCompletion(completions: *(), line: *c_char))
26
27 externfn!(fn rust_take_linenoise_lock())
28 externfn!(fn rust_drop_linenoise_lock())
29 }
30
31 macro_rules! locked {
32 ($expr:expr) => {
33 {
34 // FIXME #9105: can't use a static mutex in pure Rust yet.
35 rustrt::rust_take_linenoise_lock();
36 let x = $expr;
37 rustrt::rust_drop_linenoise_lock();
38 x
39 }
40 }
41 }
42
43 /// Add a line to history
44 pub fn add_history(line: &str) -> bool {
45 do line.with_c_str |buf| {
46 unsafe {
47 (locked!(rustrt::linenoiseHistoryAdd(buf))) == 1 as c_int
48 }
49 }
50 }
51
52 /// Set the maximum amount of lines stored
53 pub fn set_history_max_len(len: int) -> bool {
54 unsafe {
55 (locked!(rustrt::linenoiseHistorySetMaxLen(len as c_int))) == 1
56 as c_int
57 }
58 }
59
60 /// Save line history to a file
61 pub fn save_history(file: &str) -> bool {
62 do file.with_c_str |buf| {
63 // 0 on success, -1 on failure
64 unsafe {
65 (locked!(rustrt::linenoiseHistorySave(buf))) == 0 as c_int
66 }
67 }
68 }
69
70 /// Load line history from a file
71 pub fn load_history(file: &str) -> bool {
72 do file.with_c_str |buf| {
73 // 0 on success, -1 on failure
74 unsafe {
75 (locked!(rustrt::linenoiseHistoryLoad(buf))) == 0 as c_int
76 }
77 }
78 }
79
80 /// Print out a prompt and then wait for input and return it
81 pub fn read(prompt: &str) -> Option<~str> {
82 do prompt.with_c_str |buf| {
83 let line = unsafe {
84 locked!(rustrt::linenoise(buf))
85 };
86
87 if line.is_null() { None }
88 else {
89 unsafe {
90 do (|| {
91 Some(str::raw::from_c_str(line))
92 }).finally {
93 // linenoise's return value is from strdup, so we
94 // better not leak it.
95 rt::global_heap::exchange_free(line);
96 }
97 }
98 }
99 }
100 }
101
102 /// The callback used to perform completions.
103 pub trait CompletionCb {
104 /// Performs a completion.
105 fn complete(&self, line: ~str, suggestion: &fn(~str));
106 }
107
108 local_data_key!(complete_key: @CompletionCb)
109
110 /// Bind to the main completion callback in the current task.
111 ///
112 /// The completion callback should not call any `extra::rl` functions
113 /// other than the closure that it receives as its second
114 /// argument. Calling such a function will deadlock on the mutex used
115 /// to ensure that the calls are thread-safe.
116 pub unsafe fn complete(cb: @CompletionCb) {
117 local_data::set(complete_key, cb);
118
119 extern fn callback(line: *c_char, completions: *()) {
120 do local_data::get(complete_key) |opt_cb| {
121 // only fetch completions if a completion handler has been
122 // registered in the current task.
123 match opt_cb {
124 None => {}
125 Some(cb) => {
126 unsafe {
127 do cb.complete(str::raw::from_c_str(line))
128 |suggestion| {
129 do suggestion.with_c_str |buf| {
130 rustrt::linenoiseAddCompletion(completions,
131 buf);
132 }
133 }
134 }
135 }
136 }
137 }
138 }
139
140 locked!(rustrt::linenoiseSetCompletionCallback(callback));
141 }