LLVM 24.0.0git
LineEditor.cpp
Go to the documentation of this file.
1//===-- LineEditor.cpp - line editor --------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
10#include "llvm/ADT/STLExtras.h"
12#include "llvm/Config/config.h"
13#include "llvm/Support/Path.h"
15#include <algorithm>
16#include <cassert>
17#include <cstdio>
18#ifdef HAVE_LIBEDIT
19#include <histedit.h>
20constexpr int DefaultHistorySize = 800;
21#endif
22
23using namespace llvm;
24
26 SmallString<32> Path;
27 if (sys::path::home_directory(Path)) {
28 sys::path::append(Path, "." + ProgName + "-history");
29 return std::string(Path);
30 }
31 return std::string();
32}
33
34LineEditor::CompleterConcept::~CompleterConcept() = default;
35LineEditor::ListCompleterConcept::~ListCompleterConcept() = default;
36
37std::string LineEditor::ListCompleterConcept::getCommonPrefix(
38 const std::vector<Completion> &Comps) {
39 assert(!Comps.empty());
40
41 std::string CommonPrefix = Comps[0].TypedText;
42 for (const Completion &C : llvm::drop_begin(Comps)) {
43 size_t Len = std::min(CommonPrefix.size(), C.TypedText.size());
44 size_t CommonLen = 0;
45 for (; CommonLen != Len; ++CommonLen) {
46 if (CommonPrefix[CommonLen] != C.TypedText[CommonLen])
47 break;
48 }
49 CommonPrefix.resize(CommonLen);
50 }
51 return CommonPrefix;
52}
53
54LineEditor::CompletionAction
55LineEditor::ListCompleterConcept::complete(StringRef Buffer, size_t Pos) const {
56 CompletionAction Action;
57 std::vector<Completion> Comps = getCompletions(Buffer, Pos);
58 if (Comps.empty()) {
60 return Action;
61 }
62
63 std::string CommonPrefix = getCommonPrefix(Comps);
64
65 // If the common prefix is non-empty we can simply insert it. If there is a
66 // single completion, this will insert the full completion. If there is more
67 // than one, this might be enough information to jog the user's memory but if
68 // not the user can also hit tab again to see the completions because the
69 // common prefix will then be empty.
70 if (CommonPrefix.empty()) {
72 for (const Completion &Comp : Comps)
73 Action.Completions.push_back(Comp.DisplayText);
74 } else {
75 Action.Kind = CompletionAction::AK_Insert;
76 Action.Text = CommonPrefix;
77 }
78
79 return Action;
80}
81
83 size_t Pos) const {
84 if (!Completer) {
85 CompletionAction Action;
87 return Action;
88 }
89
90 return Completer->complete(Buffer, Pos);
91}
92
93#ifdef HAVE_LIBEDIT
94
95// libedit-based implementation.
96
98 LineEditor *LE;
99
100 History *Hist;
101 EditLine *EL;
102
103 unsigned PrevCount;
104 std::string ContinuationOutput;
105
106 FILE *Out;
107};
108
109namespace {
110
111const char *ElGetPromptFn(EditLine *EL) {
113 if (el_get(EL, EL_CLIENTDATA, &Data) == 0)
114 return Data->LE->getPrompt().c_str();
115 return "> ";
116}
117
118// Handles tab completion.
119//
120// This function is really horrible. But since the alternative is to get into
121// the line editor business, here we are.
122unsigned char ElCompletionFn(EditLine *EL, int ch) {
123 LineEditor::InternalData *Data;
124 if (el_get(EL, EL_CLIENTDATA, &Data) == 0) {
125 if (!Data->ContinuationOutput.empty()) {
126 // This is the continuation of the AK_ShowCompletions branch below.
127 FILE *Out = Data->Out;
128
129 // Print the required output (see below).
130 ::fwrite(Data->ContinuationOutput.c_str(),
131 Data->ContinuationOutput.size(), 1, Out);
132
133 // Push a sequence of Ctrl-B characters to move the cursor back to its
134 // original position.
135 std::string Prevs(Data->PrevCount, '\02');
136 ::el_push(EL, const_cast<char *>(Prevs.c_str()));
137
138 Data->ContinuationOutput.clear();
139
140 return CC_REFRESH;
141 }
142
143 const LineInfo *LI = ::el_line(EL);
144 LineEditor::CompletionAction Action = Data->LE->getCompletionAction(
145 StringRef(LI->buffer, LI->lastchar - LI->buffer),
146 LI->cursor - LI->buffer);
147 switch (Action.Kind) {
149 ::el_insertstr(EL, Action.Text.c_str());
150 return CC_REFRESH;
151
153 if (Action.Completions.empty()) {
154 return CC_REFRESH_BEEP;
155 } else {
156 // Push a Ctrl-E and a tab. The Ctrl-E causes libedit to move the cursor
157 // to the end of the line, so that when we emit a newline we will be on
158 // a new blank line. The tab causes libedit to call this function again
159 // after moving the cursor. There doesn't seem to be anything we can do
160 // from here to cause libedit to move the cursor immediately. This will
161 // break horribly if the user has rebound their keys, so for now we do
162 // not permit user rebinding.
163 ::el_push(EL, const_cast<char *>("\05\t"));
164
165 // This assembles the output for the continuation block above.
166 raw_string_ostream OS(Data->ContinuationOutput);
167
168 // Move cursor to a blank line.
169 OS << "\n";
170
171 // Emit the completions.
172 for (const std::string &Completion : Action.Completions)
173 OS << Completion << "\n";
174
175 // Fool libedit into thinking nothing has changed. Reprint its prompt
176 // and the user input. Note that the cursor will remain at the end of
177 // the line after this.
178 OS << Data->LE->getPrompt()
179 << StringRef(LI->buffer, LI->lastchar - LI->buffer);
180
181 // This is the number of characters we need to tell libedit to go back:
182 // the distance between end of line and the original cursor position.
183 Data->PrevCount = LI->lastchar - LI->cursor;
184
185 return CC_REFRESH;
186 }
187 }
188 }
189 return CC_ERROR;
190}
191
192} // end anonymous namespace
193
194LineEditor::LineEditor(StringRef ProgName, StringRef HistoryPath, FILE *In,
195 FILE *Out, FILE *Err)
196 : Prompt((ProgName + "> ").str()), HistoryPath(std::string(HistoryPath)),
197 Data(new InternalData) {
198 if (HistoryPath.empty())
199 this->HistoryPath = getDefaultHistoryPath(ProgName);
200
201 Data->LE = this;
202 Data->Out = Out;
203
204 Data->Hist = ::history_init();
205 assert(Data->Hist);
206
207 Data->EL = ::el_init(ProgName.str().c_str(), In, Out, Err);
208 assert(Data->EL);
209
210 ::el_set(Data->EL, EL_PROMPT, ElGetPromptFn);
211 ::el_set(Data->EL, EL_EDITOR, "emacs");
212 ::el_set(Data->EL, EL_SIGNAL, 1);
213 ::el_set(Data->EL, EL_HIST, history, Data->Hist);
214 ::el_set(Data->EL, EL_ADDFN, "tab_complete", "Tab completion function",
215 ElCompletionFn);
216 ::el_set(Data->EL, EL_BIND, "\t", "tab_complete", NULL);
217 ::el_set(Data->EL, EL_BIND, "^r", "em-inc-search-prev",
218 NULL); // Cycle through backwards search, entering string
219 ::el_set(Data->EL, EL_BIND, "^w", "ed-delete-prev-word",
220 NULL); // Delete previous word, behave like bash does.
221 ::el_set(Data->EL, EL_BIND, "\033[3~", "ed-delete-next-char",
222 NULL); // Fix the delete key.
223 ::el_set(Data->EL, EL_CLIENTDATA, Data.get());
224
225 setHistorySize(DefaultHistorySize);
226 HistEvent HE;
227 ::history(Data->Hist, &HE, H_SETUNIQUE, 1);
228 loadHistory();
229}
230
232 saveHistory();
233
234 ::history_end(Data->Hist);
235 ::el_end(Data->EL);
236 ::fwrite("\n", 1, 1, Data->Out);
237}
238
240 if (!HistoryPath.empty()) {
241 HistEvent HE;
242 ::history(Data->Hist, &HE, H_SAVE, HistoryPath.c_str());
243 }
244}
245
247 if (!HistoryPath.empty()) {
248 HistEvent HE;
249 ::history(Data->Hist, &HE, H_LOAD, HistoryPath.c_str());
250 }
251}
252
254 HistEvent HE;
255 ::history(Data->Hist, &HE, H_SETSIZE, size);
256}
257
258std::optional<std::string> LineEditor::readLine() const {
259 // Call el_gets to prompt the user and read the user's input.
260 int LineLen = 0;
261 const char *Line = ::el_gets(Data->EL, &LineLen);
262
263 // Either of these may mean end-of-file.
264 if (!Line || LineLen == 0)
265 return std::nullopt;
266
267 // Strip any newlines off the end of the string.
268 while (LineLen > 0 &&
269 (Line[LineLen - 1] == '\n' || Line[LineLen - 1] == '\r'))
270 --LineLen;
271
272 HistEvent HE;
273 if (LineLen > 0)
274 ::history(Data->Hist, &HE, H_ENTER, Line);
275
276 return std::string(Line, LineLen);
277}
278
279#else // HAVE_LIBEDIT
280
281// Simple fgets-based implementation.
282
284 FILE *In;
285 FILE *Out;
286};
287
288LineEditor::LineEditor(StringRef ProgName, StringRef HistoryPath, FILE *In,
289 FILE *Out, FILE *Err)
290 : Prompt((ProgName + "> ").str()), Data(new InternalData) {
291 Data->In = In;
292 Data->Out = Out;
293}
294
296 ::fwrite("\n", 1, 1, Data->Out);
297}
298
302
303std::optional<std::string> LineEditor::readLine() const {
304 ::fprintf(Data->Out, "%s", Prompt.c_str());
305
306 std::string Line;
307 do {
308 char Buf[64];
309 char *Res = ::fgets(Buf, sizeof(Buf), Data->In);
310 if (!Res) {
311 if (Line.empty())
312 return std::nullopt;
313 else
314 return Line;
315 }
316 Line.append(Buf);
317 } while (Line.empty() ||
318 (Line[Line.size() - 1] != '\n' && Line[Line.size() - 1] != '\r'));
319
320 while (!Line.empty() &&
321 (Line[Line.size() - 1] == '\n' || Line[Line.size() - 1] == '\r'))
322 Line.resize(Line.size() - 1);
323
324 return Line;
325}
326
327#endif // HAVE_LIBEDIT
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
static GCRegistry::Add< ShadowStackGC > C("shadow-stack", "Very portable GC for uncooperative code generators")
This file contains some templates that are useful if you are working with the STL at all.
This file defines the SmallString class.
LLVM_ABI void setHistorySize(int size)
LLVM_ABI CompletionAction getCompletionAction(StringRef Buffer, size_t Pos) const
Use the current completer to produce a CompletionAction for the given completion request.
LLVM_ABI void loadHistory()
static LLVM_ABI std::string getDefaultHistoryPath(StringRef ProgName)
LLVM_ABI LineEditor(StringRef ProgName, StringRef HistoryPath="", FILE *In=stdin, FILE *Out=stdout, FILE *Err=stderr)
Create a LineEditor object.
LLVM_ABI std::optional< std::string > readLine() const
Reads a line.
LLVM_ABI void saveHistory()
LLVM_ABI ~LineEditor()
SmallString - A SmallString is just a SmallVector with methods and accessors that make it work better...
Definition SmallString.h:26
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
std::string str() const
Get the contents as an std::string.
Definition StringRef.h:222
LLVM_ABI void append(SmallVectorImpl< char > &path, const Twine &a, const Twine &b="", const Twine &c="", const Twine &d="")
Append to path.
Definition Path.cpp:467
LLVM_ABI bool home_directory(SmallVectorImpl< char > &result)
Get the user's home directory.
This is an optimization pass for GlobalISel generic memory operations.
auto drop_begin(T &&RangeOrContainer, size_t N=1)
Return a range covering RangeOrContainer with the first N elements excluded.
Definition STLExtras.h:315
auto size(R &&Range, std::enable_if_t< std::is_base_of< std::random_access_iterator_tag, typename std::iterator_traits< decltype(Range.begin())>::iterator_category >::value, void > *=nullptr)
Get the size of a range.
Definition STLExtras.h:1669
The action to perform upon a completion request.
Definition LineEditor.h:50
std::string Text
The text to insert.
Definition LineEditor.h:61
std::vector< std::string > Completions
The list of completions to show.
Definition LineEditor.h:64
@ AK_ShowCompletions
Show Completions, or beep if the list is empty.
Definition LineEditor.h:55
@ AK_Insert
Insert Text at the cursor position.
Definition LineEditor.h:53
A possible completion at a given cursor position.
Definition LineEditor.h:68