1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <stdio.h>  // NOLINT
6 #include <string.h> // NOLINT
7 #include <readline/readline.h> // NOLINT
8 #include <readline/history.h> // NOLINT
9 
10 // The readline includes leaves RETURN defined which breaks V8 compilation.
11 #undef RETURN
12 
13 #include "src/d8.h"
14 
15 // There are incompatibilities between different versions and different
16 // implementations of readline.  This smooths out one known incompatibility.
17 #if RL_READLINE_VERSION >= 0x0500
18 #define completion_matches rl_completion_matches
19 #endif
20 
21 
22 namespace v8 {
23 
24 
25 class ReadLineEditor: public LineEditor {
26  public:
ReadLineEditor()27   ReadLineEditor() : LineEditor(LineEditor::READLINE, "readline") { }
28   virtual Handle<String> Prompt(const char* prompt);
29   virtual bool Open(Isolate* isolate);
30   virtual bool Close();
31   virtual void AddHistory(const char* str);
32 
33   static const char* kHistoryFileName;
34   static const int kMaxHistoryEntries;
35 
36  private:
37 #ifndef V8_SHARED
38   static char** AttemptedCompletion(const char* text, int start, int end);
39   static char* CompletionGenerator(const char* text, int state);
40 #endif  // V8_SHARED
41   static char kWordBreakCharacters[];
42 
43   Isolate* isolate_;
44 };
45 
46 
47 static ReadLineEditor read_line_editor;
48 char ReadLineEditor::kWordBreakCharacters[] = {' ', '\t', '\n', '"',
49     '\\', '\'', '`', '@', '.', '>', '<', '=', ';', '|', '&', '{', '(',
50     '\0'};
51 
52 
53 const char* ReadLineEditor::kHistoryFileName = ".d8_history";
54 const int ReadLineEditor::kMaxHistoryEntries = 1000;
55 
56 
Open(Isolate * isolate)57 bool ReadLineEditor::Open(Isolate* isolate) {
58   isolate_ = isolate;
59 
60   rl_initialize();
61 
62 #ifdef V8_SHARED
63   // Don't do completion on shared library mode
64   // http://cnswww.cns.cwru.edu/php/chet/readline/readline.html#SEC24
65   rl_bind_key('\t', rl_insert);
66 #else
67   rl_attempted_completion_function = AttemptedCompletion;
68 #endif  // V8_SHARED
69 
70   rl_completer_word_break_characters = kWordBreakCharacters;
71   rl_bind_key('\t', rl_complete);
72   using_history();
73   stifle_history(kMaxHistoryEntries);
74   return read_history(kHistoryFileName) == 0;
75 }
76 
77 
Close()78 bool ReadLineEditor::Close() {
79   return write_history(kHistoryFileName) == 0;
80 }
81 
82 
Prompt(const char * prompt)83 Handle<String> ReadLineEditor::Prompt(const char* prompt) {
84   char* result = NULL;
85   result = readline(prompt);
86   if (result == NULL) return Handle<String>();
87   AddHistory(result);
88   return String::NewFromUtf8(isolate_, result);
89 }
90 
91 
AddHistory(const char * str)92 void ReadLineEditor::AddHistory(const char* str) {
93   // Do not record empty input.
94   if (strlen(str) == 0) return;
95   // Remove duplicate history entry.
96   history_set_pos(history_length-1);
97   if (current_history()) {
98     do {
99       if (strcmp(current_history()->line, str) == 0) {
100         remove_history(where_history());
101         break;
102       }
103     } while (previous_history());
104   }
105   add_history(str);
106 }
107 
108 
109 #ifndef V8_SHARED
AttemptedCompletion(const char * text,int start,int end)110 char** ReadLineEditor::AttemptedCompletion(const char* text,
111                                            int start,
112                                            int end) {
113   char** result = completion_matches(text, CompletionGenerator);
114   rl_attempted_completion_over = true;
115   return result;
116 }
117 
118 
CompletionGenerator(const char * text,int state)119 char* ReadLineEditor::CompletionGenerator(const char* text, int state) {
120   static unsigned current_index;
121   static Persistent<Array> current_completions;
122   Isolate* isolate = read_line_editor.isolate_;
123   HandleScope scope(isolate);
124   Handle<Array> completions;
125   if (state == 0) {
126     Local<String> full_text = String::NewFromUtf8(isolate,
127                                                   rl_line_buffer,
128                                                   String::kNormalString,
129                                                   rl_point);
130     completions = Shell::GetCompletions(isolate,
131                                         String::NewFromUtf8(isolate, text),
132                                         full_text);
133     current_completions.Reset(isolate, completions);
134     current_index = 0;
135   } else {
136     completions = Local<Array>::New(isolate, current_completions);
137   }
138   if (current_index < completions->Length()) {
139     Handle<Integer> index = Integer::New(isolate, current_index);
140     Handle<Value> str_obj = completions->Get(index);
141     current_index++;
142     String::Utf8Value str(str_obj);
143     return strdup(*str);
144   } else {
145     current_completions.Reset();
146     return NULL;
147   }
148 }
149 #endif  // V8_SHARED
150 
151 
152 }  // namespace v8
153