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
22namespace v8 {
23
24
25class ReadLineEditor: public LineEditor {
26 public:
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
47static ReadLineEditor read_line_editor;
48char ReadLineEditor::kWordBreakCharacters[] = {' ', '\t', '\n', '"',
49    '\\', '\'', '`', '@', '.', '>', '<', '=', ';', '|', '&', '{', '(',
50    '\0'};
51
52
53const char* ReadLineEditor::kHistoryFileName = ".d8_history";
54const int ReadLineEditor::kMaxHistoryEntries = 1000;
55
56
57bool 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
78bool ReadLineEditor::Close() {
79  return write_history(kHistoryFileName) == 0;
80}
81
82
83Handle<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
92void 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
110char** 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
119char* 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