(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(lenint) -> 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  }