Home | History | Annotate | Download | only in src
      1 // Copyright 2012 the V8 project authors. All rights reserved.
      2 // Redistribution and use in source and binary forms, with or without
      3 // modification, are permitted provided that the following conditions are
      4 // met:
      5 //
      6 //     * Redistributions of source code must retain the above copyright
      7 //       notice, this list of conditions and the following disclaimer.
      8 //     * Redistributions in binary form must reproduce the above
      9 //       copyright notice, this list of conditions and the following
     10 //       disclaimer in the documentation and/or other materials provided
     11 //       with the distribution.
     12 //     * Neither the name of Google Inc. nor the names of its
     13 //       contributors may be used to endorse or promote products derived
     14 //       from this software without specific prior written permission.
     15 //
     16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27 
     28 #include <cstdio>  // NOLINT
     29 #include <string.h> // NOLINT
     30 #include <readline/readline.h> // NOLINT
     31 #include <readline/history.h> // NOLINT
     32 
     33 // The readline includes leaves RETURN defined which breaks V8 compilation.
     34 #undef RETURN
     35 
     36 #include "d8.h"
     37 
     38 // There are incompatibilities between different versions and different
     39 // implementations of readline.  This smooths out one known incompatibility.
     40 #if RL_READLINE_VERSION >= 0x0500
     41 #define completion_matches rl_completion_matches
     42 #endif
     43 
     44 
     45 namespace v8 {
     46 
     47 
     48 class ReadLineEditor: public LineEditor {
     49  public:
     50   ReadLineEditor() : LineEditor(LineEditor::READLINE, "readline") { }
     51   virtual Handle<String> Prompt(const char* prompt);
     52   virtual bool Open(Isolate* isolate);
     53   virtual bool Close();
     54   virtual void AddHistory(const char* str);
     55 
     56   static const char* kHistoryFileName;
     57   static const int kMaxHistoryEntries;
     58 
     59  private:
     60 #ifndef V8_SHARED
     61   static char** AttemptedCompletion(const char* text, int start, int end);
     62   static char* CompletionGenerator(const char* text, int state);
     63 #endif  // V8_SHARED
     64   static char kWordBreakCharacters[];
     65 
     66   Isolate* isolate_;
     67 };
     68 
     69 
     70 static ReadLineEditor read_line_editor;
     71 char ReadLineEditor::kWordBreakCharacters[] = {' ', '\t', '\n', '"',
     72     '\\', '\'', '`', '@', '.', '>', '<', '=', ';', '|', '&', '{', '(',
     73     '\0'};
     74 
     75 
     76 const char* ReadLineEditor::kHistoryFileName = ".d8_history";
     77 const int ReadLineEditor::kMaxHistoryEntries = 1000;
     78 
     79 
     80 bool ReadLineEditor::Open(Isolate* isolate) {
     81   isolate_ = isolate;
     82 
     83   rl_initialize();
     84 
     85 #ifdef V8_SHARED
     86   // Don't do completion on shared library mode
     87   // http://cnswww.cns.cwru.edu/php/chet/readline/readline.html#SEC24
     88   rl_bind_key('\t', rl_insert);
     89 #else
     90   rl_attempted_completion_function = AttemptedCompletion;
     91 #endif  // V8_SHARED
     92 
     93   rl_completer_word_break_characters = kWordBreakCharacters;
     94   rl_bind_key('\t', rl_complete);
     95   using_history();
     96   stifle_history(kMaxHistoryEntries);
     97   return read_history(kHistoryFileName) == 0;
     98 }
     99 
    100 
    101 bool ReadLineEditor::Close() {
    102   return write_history(kHistoryFileName) == 0;
    103 }
    104 
    105 
    106 Handle<String> ReadLineEditor::Prompt(const char* prompt) {
    107   char* result = NULL;
    108   {  // Release lock for blocking input.
    109     Unlocker unlock(Isolate::GetCurrent());
    110     result = readline(prompt);
    111   }
    112   if (result == NULL) return Handle<String>();
    113   AddHistory(result);
    114   return String::NewFromUtf8(isolate_, result);
    115 }
    116 
    117 
    118 void ReadLineEditor::AddHistory(const char* str) {
    119   // Do not record empty input.
    120   if (strlen(str) == 0) return;
    121   // Remove duplicate history entry.
    122   history_set_pos(history_length-1);
    123   if (current_history()) {
    124     do {
    125       if (strcmp(current_history()->line, str) == 0) {
    126         remove_history(where_history());
    127         break;
    128       }
    129     } while (previous_history());
    130   }
    131   add_history(str);
    132 }
    133 
    134 
    135 #ifndef V8_SHARED
    136 char** ReadLineEditor::AttemptedCompletion(const char* text,
    137                                            int start,
    138                                            int end) {
    139   char** result = completion_matches(text, CompletionGenerator);
    140   rl_attempted_completion_over = true;
    141   return result;
    142 }
    143 
    144 
    145 char* ReadLineEditor::CompletionGenerator(const char* text, int state) {
    146   static unsigned current_index;
    147   static Persistent<Array> current_completions;
    148   Isolate* isolate = read_line_editor.isolate_;
    149   Locker lock(isolate);
    150   HandleScope scope(isolate);
    151   Handle<Array> completions;
    152   if (state == 0) {
    153     Local<String> full_text = String::NewFromUtf8(isolate,
    154                                                   rl_line_buffer,
    155                                                   String::kNormalString,
    156                                                   rl_point);
    157     completions = Shell::GetCompletions(isolate,
    158                                         String::NewFromUtf8(isolate, text),
    159                                         full_text);
    160     current_completions.Reset(isolate, completions);
    161     current_index = 0;
    162   } else {
    163     completions = Local<Array>::New(isolate, current_completions);
    164   }
    165   if (current_index < completions->Length()) {
    166     Handle<Integer> index = Integer::New(current_index);
    167     Handle<Value> str_obj = completions->Get(index);
    168     current_index++;
    169     String::Utf8Value str(str_obj);
    170     return strdup(*str);
    171   } else {
    172     current_completions.Reset();
    173     return NULL;
    174   }
    175 }
    176 #endif  // V8_SHARED
    177 
    178 
    179 }  // namespace v8
    180