From cb6684b63b3c4c5a90e194c5719bc82690180f30 Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Fri, 31 Jan 2014 23:46:14 +0000 Subject: Introduce line editor library. This library will be used by clang-query. I can imagine LLDB becoming another client of this library, so I think LLVM is a sensible place for it to live. It wraps libedit, and adds tab completion support. The code is loosely based on the line editor bits in LLDB, with a few improvements: - Polymorphism for retrieving the list of tab completions, based on the concept pattern from the new pass manager. - Tab completion doesn't corrupt terminal output if the input covers multiple lines. Unfortunately this can only be done in a truly horrible way, as far as I can tell. But since the alternative is to implement our own line editor (which I don't think LLVM should be in the business of doing, at least for now) I think it may be acceptable. - Includes a fallback for the case where the user doesn't have libedit installed. Note that this uses C stdio, mainly because libedit also uses C stdio. Differential Revision: http://llvm-reviews.chandlerc.com/D2200 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@200595 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/Config/config.h.cmake | 3 + include/llvm/Config/config.h.in | 3 + include/llvm/LineEditor/LineEditor.h | 152 +++++++++++++++++++++++++++++++++++ 3 files changed, 158 insertions(+) create mode 100644 include/llvm/LineEditor/LineEditor.h (limited to 'include/llvm') diff --git a/include/llvm/Config/config.h.cmake b/include/llvm/Config/config.h.cmake index 5c72ad8a99..4a15197895 100644 --- a/include/llvm/Config/config.h.cmake +++ b/include/llvm/Config/config.h.cmake @@ -212,6 +212,9 @@ /* Define to 1 if you have the 'z' library (-lz). */ #cmakedefine HAVE_LIBZ ${HAVE_LIBZ} +/* Define to 1 if you have the 'edit' library (-ledit). */ +#cmakedefine HAVE_LIBEDIT ${HAVE_LIBEDIT} + /* Define to 1 if you have the header file. */ #cmakedefine HAVE_LIMITS_H ${HAVE_LIMITS_H} diff --git a/include/llvm/Config/config.h.in b/include/llvm/Config/config.h.in index 0d43ae50a1..da5b6c9f64 100644 --- a/include/llvm/Config/config.h.in +++ b/include/llvm/Config/config.h.in @@ -205,6 +205,9 @@ /* Define if you have the libdl library or equivalent. */ #undef HAVE_LIBDL +/* Define if libedit is available on this platform. */ +#undef HAVE_LIBEDIT + /* Define to 1 if you have the `imagehlp' library (-limagehlp). */ #undef HAVE_LIBIMAGEHLP diff --git a/include/llvm/LineEditor/LineEditor.h b/include/llvm/LineEditor/LineEditor.h new file mode 100644 index 0000000000..7ac9b57323 --- /dev/null +++ b/include/llvm/LineEditor/LineEditor.h @@ -0,0 +1,152 @@ +//===-- llvm/LineEditor/LineEditor.h - line editor --------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LINEEDITOR_LINEEDITOR_H +#define LLVM_LINEEDITOR_LINEEDITOR_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/OwningPtr.h" +#include +#include +#include + +namespace llvm { + +class LineEditor { +public: + /// Create a LineEditor object. + /// + /// \param ProgName The name of the current program. Used to form a default + /// prompt. + /// \param HistoryPath Path to the file in which to store history data, if + /// possible. + /// \param In The input stream used by the editor. + /// \param Out The output stream used by the editor. + /// \param Err The error stream used by the editor. + LineEditor(StringRef ProgName, StringRef HistoryPath = "", FILE *In = stdin, + FILE *Out = stdout, FILE *Err = stderr); + ~LineEditor(); + + /// Reads a line. + /// + /// \return The line, or llvm::Optional() on EOF. + llvm::Optional readLine() const; + + void saveHistory(); + void loadHistory(); + + static std::string getDefaultHistoryPath(StringRef ProgName); + + /// The action to perform upon a completion request. + struct CompletionAction { + enum ActionKind { + /// Insert Text at the cursor position. + AK_Insert, + /// Show Completions, or beep if the list is empty. + AK_ShowCompletions + }; + + ActionKind Kind; + + /// The text to insert. + std::string Text; + + /// The list of completions to show. + std::vector Completions; + }; + + /// A possible completion at a given cursor position. + struct Completion { + Completion() {} + Completion(const std::string &TypedText, const std::string &DisplayText) + : TypedText(TypedText), DisplayText(DisplayText) {} + + /// The text to insert. If the user has already input some of the + /// completion, this should only include the rest of the text. + std::string TypedText; + + /// A description of this completion. This may be the completion itself, or + /// maybe a summary of its type or arguments. + std::string DisplayText; + }; + + /// Set the completer for this LineEditor. A completer is a function object + /// which takes arguments of type StringRef (the string to complete) and + /// size_t (the zero-based cursor position in the StringRef) and returns a + /// CompletionAction. + template void setCompleter(T Comp) { + Completer.reset(new CompleterModel(Comp)); + } + + /// Set the completer for this LineEditor to the given list completer. + /// A list completer is a function object which takes arguments of type + /// StringRef (the string to complete) and size_t (the zero-based cursor + /// position in the StringRef) and returns a std::vector. + template void setListCompleter(T Comp) { + Completer.reset(new ListCompleterModel(Comp)); + } + + /// Use the current completer to produce a CompletionAction for the given + /// completion request. If the current completer is a list completer, this + /// will return an AK_Insert CompletionAction if each completion has a common + /// prefix, or an AK_ShowCompletions CompletionAction otherwise. + /// + /// \param Buffer The string to complete + /// \param Pos The zero-based cursor position in the StringRef + CompletionAction getCompletionAction(StringRef Buffer, size_t Pos) const; + + const std::string &getPrompt() const { return Prompt; } + void setPrompt(const std::string &P) { Prompt = P; } + + // Public so callbacks in LineEditor.cpp can use it. + struct InternalData; + +private: + std::string Prompt; + std::string HistoryPath; + OwningPtr Data; + + struct CompleterConcept { + virtual ~CompleterConcept(); + virtual CompletionAction complete(StringRef Buffer, size_t Pos) const = 0; + }; + + struct ListCompleterConcept : CompleterConcept { + ~ListCompleterConcept(); + CompletionAction complete(StringRef Buffer, size_t Pos) const; + static std::string getCommonPrefix(const std::vector &Comps); + virtual std::vector getCompletions(StringRef Buffer, + size_t Pos) const = 0; + }; + + template + struct CompleterModel : CompleterConcept { + CompleterModel(T Value) : Value(Value) {} + CompletionAction complete(StringRef Buffer, size_t Pos) const { + return Value(Buffer, Pos); + } + T Value; + }; + + template + struct ListCompleterModel : ListCompleterConcept { + ListCompleterModel(T Value) : Value(Value) {} + std::vector getCompletions(StringRef Buffer, size_t Pos) const { + return Value(Buffer, Pos); + } + T Value; + }; + + llvm::OwningPtr Completer; +}; + +} + +#endif -- cgit v1.2.3