diff --git a/core/textinput/src/Getline.cxx b/core/textinput/src/Getline.cxx index d962ec0999053..28f4c0b072a88 100644 --- a/core/textinput/src/Getline.cxx +++ b/core/textinput/src/Getline.cxx @@ -170,7 +170,7 @@ extern "C" { void Gl_config(const char* which, int value) { if (strcmp(which, "noecho") == 0) { - TextInputHolder::get().HideInput(value); + TextInputHolder::get().MaskInput(value); } else { // unsupported directive printf("Gl_config unsupported: %s ?\n", which); diff --git a/core/textinput/src/textinput/Editor.cpp b/core/textinput/src/textinput/Editor.cpp index 89fea420a1b9b..027e7c766f6d4 100644 --- a/core/textinput/src/textinput/Editor.cpp +++ b/core/textinput/src/textinput/Editor.cpp @@ -72,7 +72,7 @@ namespace textinput { Range Editor::ResetText() { bool addToHist = !fContext->GetLine().empty() - && !fContext->GetTextInput()->IsInputHidden() + && !fContext->GetTextInput()->IsInputMasked() && fContext->GetTextInput()->IsAutoHistAddEnabled(); if (addToHist) { fContext->GetHistory()->AddLine(fContext->GetLine().GetText()); diff --git a/core/textinput/src/textinput/Reader.h b/core/textinput/src/textinput/Reader.h index 23d3765871855..ef7b3edbf445e 100644 --- a/core/textinput/src/textinput/Reader.h +++ b/core/textinput/src/textinput/Reader.h @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// // -// This file defines the abtract input interface. +// This file defines the abstract input interface. // // Axel Naumann , 2011-05-12 //===----------------------------------------------------------------------===// diff --git a/core/textinput/src/textinput/StreamReaderUnix.cpp b/core/textinput/src/textinput/StreamReaderUnix.cpp index 72e9367487d20..f46d6b8ac5735 100644 --- a/core/textinput/src/textinput/StreamReaderUnix.cpp +++ b/core/textinput/src/textinput/StreamReaderUnix.cpp @@ -131,11 +131,19 @@ namespace textinput { StreamReaderUnix::StreamReaderUnix(): fHaveInputFocus(false), fIsTTY(isatty(fileno(stdin))) { #ifdef TCSANOW + // ~ISTRIP - do not strip 8th char bit + // ~IXOFF - software flow ctrl disabled for input queue TerminalConfigUnix::Get().TIOS()->c_iflag &= ~(ISTRIP|IXOFF); + // BRKINT - flush i/o and send SIGINT + // INLCR - translate NL to CR TerminalConfigUnix::Get().TIOS()->c_iflag |= BRKINT | INLCR; + // ~ICANON - non-canonical = input available immediately, no EOL needed, no processing, line editing disabled + // ~ISIG - don't sent signals on input chars + // ~TOSTOP - don't send SIGTTOU + // ~IEXTEN - disable implementation-defined input processing, don't process spec chars (EOL2, LNEXT...) TerminalConfigUnix::Get().TIOS()->c_lflag &= ~(ICANON|ISIG|TOSTOP|IEXTEN); - TerminalConfigUnix::Get().TIOS()->c_cc[VMIN] = 1; - TerminalConfigUnix::Get().TIOS()->c_cc[VTIME] = 0; + TerminalConfigUnix::Get().TIOS()->c_cc[VMIN] = 1; // minimum chars to read in non-canonical mode + TerminalConfigUnix::Get().TIOS()->c_cc[VTIME] = 0; // waits indefinitely for VMIN chars (blocking) #endif } @@ -143,6 +151,8 @@ namespace textinput { ReleaseInputFocus(); } + //////////////////////////////////////////////////////////////////////////////// + /// Attach to terminal, set the proper configuration. void StreamReaderUnix::GrabInputFocus() { // set to raw i.e. unbuffered @@ -151,6 +161,8 @@ namespace textinput { fHaveInputFocus = true; } + //////////////////////////////////////////////////////////////////////////////// + /// Detach from terminal, set the old configuration. void StreamReaderUnix::ReleaseInputFocus() { // set to buffered @@ -159,6 +171,12 @@ namespace textinput { fHaveInputFocus = false; } + //////////////////////////////////////////////////////////////////////////////// + /// Test or wait for available input + /// + /// \param[in] wait blocking wait on input + /// + /// Wait true - block, wait false - poll bool StreamReaderUnix::HavePendingInput(bool wait) { if (!fReadAheadBuffer.empty()) @@ -172,6 +190,10 @@ namespace textinput { return (avail == 1); } + //////////////////////////////////////////////////////////////////////////////// + /// Process Control Sequence Introducer commands (equivalent to ESC [) + /// + /// \param[in] in input char / data bool StreamReaderUnix::ProcessCSI(InputData& in) { static ExtKeyMap gExtKeyMap; @@ -231,25 +253,29 @@ namespace textinput { return ret != InputData::kEIUninitialized; } + //////////////////////////////////////////////////////////////////////////////// + /// Read one char from stdin. Converts the read char to InputData + /// + /// \param[in] nRead number of already read characters. Increment after reading + /// \param[in] in input char / data to be filled out bool StreamReaderUnix::ReadInput(size_t& nRead, InputData& in) { int c = ReadRawCharacter(); in.SetModifier(InputData::kModNone); - if (c == -1) { + if (c == -1) { // non-character value, EOF negative in.SetExtended(InputData::kEIEOF); - } else if (c == 0x1b) { - // Only try to process CSI if Esc does not have a meaning - // by itself. + } else if (c == 0x1b) { // ESC + // Only try to process CSI if Esc does not have a meaning by itself. if (GetContext()->GetKeyBinding()->IsEscCommandEnabled() || !ProcessCSI(in)) { in.SetExtended(InputData::kEIEsc); } - } else if (isprint(c)) { + } else if (isprint(c)) { // c > 0x1f(31) && c < 0x7f(127) in.SetRaw(c); - } else if (c < 32 || c == (char)127 /* ^?, DEL on MacOS */) { - if (c == 13) { + } else if (c < 32 || c == (char)127 /* ^?, DEL on MacOS */) { // non-printable + if (c == 13) { // 0x0d CR (INLCR - NL converted to CR) in.SetExtended(InputData::kEIEnter); - } else { + } else { // mark control pressed if other non-print char in.SetRaw(c); in.SetModifier(InputData::kModCtrl); } @@ -260,6 +286,9 @@ namespace textinput { ++nRead; return true; } + + //////////////////////////////////////////////////////////////////////////////// + /// Read one character from stdin. Block if not available. int StreamReaderUnix::ReadRawCharacter() { char buf; @@ -274,4 +303,4 @@ namespace textinput { } } -#endif // ifndef _WIN32 +#endif // ifndef WIN32 diff --git a/core/textinput/src/textinput/TerminalDisplay.cpp b/core/textinput/src/textinput/TerminalDisplay.cpp index f2c2416a18e4d..77951b27ca99f 100644 --- a/core/textinput/src/textinput/TerminalDisplay.cpp +++ b/core/textinput/src/textinput/TerminalDisplay.cpp @@ -37,21 +37,31 @@ namespace textinput { #endif } + //////////////////////////////////////////////////////////////////////////////// + /// Notify the display that the text has been changed in range r. + /// Rewrite the display in range r and move back to the cursor. + /// + /// \param[in] r Range to write out the text for. void TerminalDisplay::NotifyTextChange(Range r) { if (!IsTTY()) return; Attach(); - WriteWrapped(r.fPromptUpdate,GetContext()->GetTextInput()->IsInputHidden(), + WriteWrapped(r.fPromptUpdate, GetContext()->GetTextInput()->IsInputMasked(), r.fStart, r.fLength); Move(GetCursor()); } + //////////////////////////////////////////////////////////////////////////////// + /// Notify the display that the cursor has been changed. Move to the cursor. void TerminalDisplay::NotifyCursorChange() { Attach(); Move(GetCursor()); } + //////////////////////////////////////////////////////////////////////////////// + /// Notify the display that the input has been taken. + /// Move to the next line, reset written length and position. void TerminalDisplay::NotifyResetInput() { Attach(); @@ -62,12 +72,20 @@ namespace textinput { fWritePos = Pos(); } + //////////////////////////////////////////////////////////////////////////////// + /// Notify the display that there has been an error. + /// Write out the BEL character. void TerminalDisplay::NotifyError() { Attach(); WriteRawString("\x07", 1); } + //////////////////////////////////////////////////////////////////////////////// + /// Display an informational message at the prompt. + /// Acts like a pop-up. Used e.g. for tab-completion. + /// + /// \param[in] Options options to write out void TerminalDisplay::DisplayInfo(const std::vector& Options) { char infoColIdx = 0; @@ -77,7 +95,7 @@ namespace textinput { WriteRawString("\n", 1); for (size_t i = 0, n = Options.size(); i < n; ++i) { Text t(Options[i], infoColIdx); - WriteWrappedElement(t, 0, 0, (size_t) -1); + WriteWrappedTextPart(t, 0, 0, (size_t) -1); WriteRawString("\n", 1); } // Reset position @@ -85,6 +103,9 @@ namespace textinput { Attach(); } + //////////////////////////////////////////////////////////////////////////////// + /// Detach from the abstract display by resetting the position + /// and written text length. If Colorizer is present, reset the color too. void TerminalDisplay::Detach() { fWritePos = Pos(); @@ -98,41 +119,54 @@ namespace textinput { } } + //////////////////////////////////////////////////////////////////////////////// + /// Write out wrapped text to the display. Used in WriteWrapped and DisplayInfo + /// + /// \param[in] text text to write out + /// \param[in] TextOffset where to begin writing out text from + /// \param[in] WriteOffset where to begin writing out text at the display + /// \param[in] Requested number of text characters requested for output size_t - TerminalDisplay::WriteWrappedElement(const Text& Element, size_t TextOffset, - size_t WriteOffset, size_t Requested) { + TerminalDisplay::WriteWrappedTextPart(const Text &text, size_t TextOffset, + size_t WriteOffset, size_t NumRequested) { size_t Start = TextOffset; - size_t Remaining = Requested; + size_t NumRemaining = NumRequested; // optimistic - size_t Available = Element.length() - Start; - if (Requested == (size_t) -1) { - Requested = Available; + size_t NumAvailable = text.length() - Start; + if (NumRequested == (size_t) -1) { // requested max available + NumRequested = NumAvailable; } - if (Available > 0) { - if (Available < Remaining) { - Remaining = Available; + // If we have some text available for output + if (NumAvailable > 0) { + // If we don't have enough to output NumRemaining, output only what's available + if (NumAvailable < NumRemaining) { + NumRemaining = NumAvailable; } - while (Remaining > 0) { - size_t numThisLine = Remaining; - + while (NumRemaining > 0) { // How much can this line hold? size_t numToEOL = GetWidth() - ((Start + WriteOffset) % GetWidth()); - if (!numToEOL) { + if (numToEOL == 0) { // we are at EOL, move down MoveDown(); ++fWritePos.fLine; MoveFront(); fWritePos.fCol = 0; numToEOL = GetWidth(); } - if (numThisLine > numToEOL) { + + // How much of our text can we fit in this line? + size_t numThisLine; + if (NumRemaining > numToEOL) { numThisLine = numToEOL; + } else { + numThisLine = NumRemaining; } + // If there is a Colorizer, we only write same-colored chunks. + // How long is the current chunk? Adjust numThisLine. if (GetContext()->GetColorizer()) { - // We only write same-color chunks; how long is it? - const std::vector& Colors = Element.GetColors(); + const std::vector& Colors = text.GetColors(); char ThisColor = Colors[Start]; size_t numSameColor = 1; while (numSameColor < numThisLine @@ -148,31 +182,36 @@ namespace textinput { } } - WriteRawString(Element.GetText().c_str() + Start, numThisLine); + // Write out the line and update the write position + WriteRawString(text.GetText().c_str() + Start, numThisLine); fWritePos = IndexToPos(PosToIndex(fWritePos) + numThisLine); - if (numThisLine == numToEOL) { + if (numThisLine == numToEOL) { // If we hit EOL, wrap around ActOnEOL(); } Start += numThisLine; - Remaining -= numThisLine; + NumRemaining -= numThisLine; } } - if (Requested == Available) { - size_t VisL = fWriteLen / GetWidth(); - size_t Wrote = WriteOffset + TextOffset + Requested; - size_t WroteL = Wrote / GetWidth(); - size_t NumToEOL = GetWidth() - (Wrote % GetWidth()); - if (fWriteLen > Wrote && NumToEOL > 0) { - // Wrote less and not at EOL + // If we have processed the characters we have requested + if (NumRequested == NumAvailable) { + size_t NumPrevLines = fWriteLen / GetWidth(); + size_t LenWrote = WriteOffset + TextOffset + NumRequested; + size_t NumWroteLines = LenWrote / GetWidth(); + size_t NumToEOL = GetWidth() - (LenWrote % GetWidth()); + if (LenWrote < fWriteLen && NumToEOL > 0) { + // If we wrote less than previously and not at EOL + // Erase the rest of the current line EraseToRight(); } - if (WroteL < VisL) { + if (NumWroteLines < NumPrevLines) { + // If we wrote less lines than previously, + // erase the surplus previous lines Pos prevWC = GetCursor(); MoveFront(); fWritePos.fCol = 0; - for (size_t l = WroteL + 1; l <= VisL; ++l) { + for (size_t l = NumWroteLines + 1; l <= NumPrevLines; ++l) { MoveDown(); ++fWritePos.fLine; EraseToRight(); @@ -180,11 +219,11 @@ namespace textinput { Move(prevWC); } } - return Remaining; + return NumRemaining; } size_t - TerminalDisplay::WriteWrapped(Range::EPromptUpdate PromptUpdate, bool hidden, + TerminalDisplay::WriteWrapped(Range::EPromptUpdate PromptUpdate, bool masked, size_t Offset, size_t Requested /* = -1*/) { Attach(); @@ -199,16 +238,18 @@ namespace textinput { PromptUpdate = Range::kNoPromptUpdate; } + // If updating prompt, write the main prompt first (e.g. [cling]$) if (PromptUpdate & Range::kUpdatePrompt) { // Writing from front means we write the prompt, too Move(Pos()); - WriteWrappedElement(Prompt, 0, 0, PromptLen); + WriteWrappedTextPart(Prompt, 0, 0, PromptLen); } + // If updating any prompt if (PromptUpdate != Range::kNoPromptUpdate) { // Any prompt update means we'll have to re-write the editor prompt Move(IndexToPos(PromptLen)); if (EditorPromptLen) { - WriteWrappedElement(EditPrompt, 0, PromptLen, EditorPromptLen); + WriteWrappedTextPart(EditPrompt, 0, PromptLen, EditorPromptLen); } // Any prompt update means we'll have to re-write the text Offset = 0; @@ -217,18 +258,22 @@ namespace textinput { Move(IndexToPos(PromptLen + EditorPromptLen + Offset)); size_t avail = 0; - if (hidden) { - Text hide(std::string(GetContext()->GetLine().length(), '*'), 0); - avail = WriteWrappedElement(hide, Offset, - PromptLen + EditorPromptLen, Requested); + if (masked) { + Text mask(std::string(GetContext()->GetLine().length(), '*'), 0); + avail = WriteWrappedTextPart(mask, Offset, + PromptLen + EditorPromptLen, Requested); } else { - avail = WriteWrappedElement(GetContext()->GetLine(), Offset, - PromptLen + EditorPromptLen, Requested); + avail = WriteWrappedTextPart(GetContext()->GetLine(), Offset, + PromptLen + EditorPromptLen, Requested); } fWriteLen = PromptLen + EditorPromptLen + GetContext()->GetLine().length(); return avail; } + //////////////////////////////////////////////////////////////////////////////// + /// Move the cursor to the required position. + /// + /// \param[in] p position to move to void TerminalDisplay::Move(Pos p) { Attach(); diff --git a/core/textinput/src/textinput/TerminalDisplay.h b/core/textinput/src/textinput/TerminalDisplay.h index 66c1a75638df4..c16a454984903 100644 --- a/core/textinput/src/textinput/TerminalDisplay.h +++ b/core/textinput/src/textinput/TerminalDisplay.h @@ -56,7 +56,8 @@ namespace textinput { Pos IndexToPos(size_t idx) const { return Pos(idx % fWidth, idx / fWidth); } size_t PosToIndex(const Pos& pos) const { // Convert a x|y position to an index. - return pos.fCol + pos.fLine * fWidth; } + return pos.fCol + pos.fLine * fWidth; + } size_t GetWidth() const { return fWidth; } void SetWidth(size_t width) { fWidth = width; } @@ -66,10 +67,10 @@ namespace textinput { virtual void MoveLeft(size_t nCols = 1) = 0; virtual void MoveRight(size_t nCols = 1) = 0; virtual void MoveFront() = 0; - size_t WriteWrapped(Range::EPromptUpdate PromptUpdate, bool hidden, + size_t WriteWrapped(Range::EPromptUpdate PromptUpdate, bool masked, size_t offset, size_t len = (size_t)-1); - size_t WriteWrappedElement(const Text& what, size_t TextOffset, - size_t WriteOffset, size_t Requested); + size_t WriteWrappedTextPart(const Text &text, size_t TextOffset, + size_t WriteOffset, size_t Requested); virtual void SetColor(char CIdx, const Color& C) = 0; virtual void WriteRawString(const char* text, size_t len) = 0; virtual void ActOnEOL() {} @@ -79,7 +80,7 @@ namespace textinput { protected: bool fIsTTY; // whether this is a terminal or redirected size_t fWidth; // Width of the terminal in character columns - size_t fWriteLen; // Last char of output written. + size_t fWriteLen; // Length of output written. Pos fWritePos; // Current position of writing (temporarily != cursor) char fPrevColor; // currently configured color }; diff --git a/core/textinput/src/textinput/TerminalDisplayUnix.cpp b/core/textinput/src/textinput/TerminalDisplayUnix.cpp index 2b45168a41a90..5eeea6b0ac3c1 100644 --- a/core/textinput/src/textinput/TerminalDisplayUnix.cpp +++ b/core/textinput/src/textinput/TerminalDisplayUnix.cpp @@ -229,13 +229,20 @@ namespace textinput { MoveInternal('D', nCols); } + //////////////////////////////////////////////////////////////////////////////// + /// Erases the input to the right of the cursor. void TerminalDisplayUnix::EraseToRight() { - static const char text[] = {(char)0x1b, '[', 'K'}; + static const char text[] = {(char)0x1b, '[', 'K'}; // ESC[K if (!IsTTY()) return; WriteRawString(text, sizeof(text)); } + //////////////////////////////////////////////////////////////////////////////// + /// Writes out a raw string to stdout. + /// + /// \param[in] text raw string to be written out + /// \param[in] len length of the raw string void TerminalDisplayUnix::WriteRawString(const char *text, size_t len) { if (write(fileno(stdout), text, len) == -1) { @@ -243,6 +250,9 @@ namespace textinput { } } + //////////////////////////////////////////////////////////////////////////////// + /// Invoke this on EOL. Writes out space backspace, to wrap to the next line. + /// Otherwise, we stay on the same line and the input gets pushed upwards. void TerminalDisplayUnix::ActOnEOL() { if (!IsTTY()) return; diff --git a/core/textinput/src/textinput/TextInput.cpp b/core/textinput/src/textinput/TextInput.cpp index c3f6bd47a252b..394bab797fc1a 100644 --- a/core/textinput/src/textinput/TextInput.cpp +++ b/core/textinput/src/textinput/TextInput.cpp @@ -31,7 +31,7 @@ namespace textinput { TextInput::TextInput(Reader& reader, Display& display, const char* HistFile /* = 0 */): - fHidden(false), + fMasked(false), fAutoHistAdd(true), fLastKey(0), fMaxChars(0), diff --git a/core/textinput/src/textinput/TextInput.h b/core/textinput/src/textinput/TextInput.h index c766ee112cb67..193e7af9a9f2f 100644 --- a/core/textinput/src/textinput/TextInput.h +++ b/core/textinput/src/textinput/TextInput.h @@ -47,7 +47,7 @@ namespace textinput { // Getters const TextInputContext* GetContext() const { return fContext; } - bool IsInputHidden() const { return fHidden; } + bool IsInputMasked() const { return fMasked; } size_t GetMaxPendingCharsToRead() const { return fMaxChars; } bool IsReadingAllPendingChars() const { return fMaxChars == (size_t) -1; } @@ -55,7 +55,7 @@ namespace textinput { // Setters void SetPrompt(const char* p); - void HideInput(bool hidden = true) { fHidden = hidden; } + void MaskInput(bool masked = true) { fMasked = masked; } void SetColorizer(Colorizer* c); void SetCompletion(TabCompletion* tc); void SetFunctionKeyHandler(FunKey* fc); @@ -93,7 +93,7 @@ namespace textinput { void ProcessNewInput(const InputData& in, EditorRange& r); void DisplayNewInput(EditorRange& r, size_t& oldCursorPos); - bool fHidden; // whether input should be shown + bool fMasked; // whether input should be shown bool fAutoHistAdd; // whether input should be added to history char fLastKey; // most recently read key size_t fMaxChars; // Num chars to read; 0 for blocking, -1 for all available diff --git a/core/textinput/src/textinput/doc/examplemain.cpp b/core/textinput/src/textinput/doc/examplemain.cpp index cf85ce37b00fa..9d93987b3a25a 100644 --- a/core/textinput/src/textinput/doc/examplemain.cpp +++ b/core/textinput/src/textinput/doc/examplemain.cpp @@ -76,7 +76,7 @@ int main (int argc, const char * argv[]) { } TI.TakeInput(line); printf("INPUT: --BEGIN--%s--END--\n", line.c_str()); - if (line == "h") TI.HideInput(!TI.IsInputHidden()); + if (line == "h") TI.MaskInput(!TI.IsInputMasked()); } while (!TI.AtEOF() && line != ".q"); delete R; diff --git a/interpreter/cling/include/cling/Interpreter/Interpreter.h b/interpreter/cling/include/cling/Interpreter/Interpreter.h index b3b7bea07140a..76b31073ed72b 100644 --- a/interpreter/cling/include/cling/Interpreter/Interpreter.h +++ b/interpreter/cling/include/cling/Interpreter/Interpreter.h @@ -170,6 +170,11 @@ namespace cling { /// bool m_RawInputEnabled; + ///\brief Flag toggling the interactive print mode on or off. + /// Enabled by default if stdin is attached to terminal. + /// + bool m_InteractivePrintEnabled; + ///\brief Interpreter callbacks. /// std::unique_ptr m_Callbacks; @@ -545,6 +550,9 @@ namespace cling { bool isRawInputEnabled() const { return m_RawInputEnabled; } void enableRawInput(bool raw = true) { m_RawInputEnabled = raw; } + bool isInteractivePrintEnabled() const { return m_InteractivePrintEnabled; } + void enableInteractivePrint(bool iprint = true) { m_InteractivePrintEnabled = iprint; } + clang::CompilerInstance* getCI() const; clang::Sema& getSema() const; diff --git a/interpreter/cling/include/cling/MetaProcessor/MetaProcessor.h b/interpreter/cling/include/cling/MetaProcessor/MetaProcessor.h index 64fd43732e3f8..f736296d494a9 100644 --- a/interpreter/cling/include/cling/MetaProcessor/MetaProcessor.h +++ b/interpreter/cling/include/cling/MetaProcessor/MetaProcessor.h @@ -107,7 +107,7 @@ namespace cling { MetaProcessor(Interpreter& interp, llvm::raw_ostream& outs); ~MetaProcessor(); - const Interpreter& getInterpreter() const { return m_Interp; } + Interpreter& getInterpreter() const { return m_Interp; } ///\brief Get the output stream used by the MetaProcessor for its output. /// (in contrast to the interpreter's output which is redirected using diff --git a/interpreter/cling/lib/Interpreter/Interpreter.cpp b/interpreter/cling/lib/Interpreter/Interpreter.cpp index 32a834fe9bc1a..91f7c0d96ee54 100644 --- a/interpreter/cling/lib/Interpreter/Interpreter.cpp +++ b/interpreter/cling/lib/Interpreter/Interpreter.cpp @@ -167,7 +167,7 @@ namespace cling { Interpreter::Interpreter(int argc, const char* const *argv, const char* llvmdir /*= 0*/, bool noRuntime) : m_UniqueCounter(0), m_PrintDebug(false), m_DynamicLookupDeclared(false), - m_DynamicLookupEnabled(false), m_RawInputEnabled(false) { + m_DynamicLookupEnabled(false), m_RawInputEnabled(false), m_InteractivePrintEnabled(false) { m_LLVMContext.reset(new llvm::LLVMContext); std::vector LeftoverArgsIdx; diff --git a/interpreter/cling/lib/Interpreter/Value.cpp b/interpreter/cling/lib/Interpreter/Value.cpp index 98498be5ce653..74e0cc0910da0 100644 --- a/interpreter/cling/lib/Interpreter/Value.cpp +++ b/interpreter/cling/lib/Interpreter/Value.cpp @@ -272,19 +272,12 @@ namespace cling { } namespace valuePrinterInternal { - std::string printTypeInternal(const Value& V); - std::string printValueInternal(const Value& V); + void printInternal(llvm::raw_ostream& Out, const Value& V); } // end namespace valuePrinterInternal void Value::print(llvm::raw_ostream& Out) const { - - // Get the default type string representation - std::string typeStr = cling::valuePrinterInternal::printTypeInternal(*this); - // Get the value string representation, by printValue() method overloading - std::string valueStr = cling::valuePrinterInternal::printValueInternal(*this); - - // Print the type and the value: - Out << typeStr + " " + valueStr << "\n"; + // Forward the invocation to ValuePrinter.cpp -> keep all of the printing logic there + cling::valuePrinterInternal::printInternal(Out, *this); } void Value::dump() const { diff --git a/interpreter/cling/lib/Interpreter/ValuePrinter.cpp b/interpreter/cling/lib/Interpreter/ValuePrinter.cpp index 0b0f1b9fc9f3c..8c739ec4703b6 100644 --- a/interpreter/cling/lib/Interpreter/ValuePrinter.cpp +++ b/interpreter/cling/lib/Interpreter/ValuePrinter.cpp @@ -12,7 +12,6 @@ #include "cling/Interpreter/CValuePrinter.h" #include "cling/Interpreter/Interpreter.h" #include "cling/Interpreter/Transaction.h" -#include "cling/Interpreter/Value.h" #include "cling/Utils/AST.h" #include "clang/AST/ASTContext.h" @@ -359,6 +358,32 @@ static std::string printUnpackedClingValue(const Value &V) { return strm.str(); } +static void printInteractively(llvm::raw_ostream &Out, std::string tv) { + unsigned long length = tv.length(); + unsigned int n = 400; // == 5 lines (default terminal line width == 80 characters) + if (length < n) { + Out << tv << "\n"; + } else { + Out << "Interactive printing. Press Enter to continue, q Enter to stop.\n"; + unsigned int i = 0; + while (i < length) { + Out << tv.substr(0, n); + Out.flush(); + // Keep reading input until Enter or [q|Q] Enter is encountered + char c = getchar(); + while(c != '\n' && c != 'q' && c != 'Q'){ + c = getchar(); + } + if (c == 'q' || c == 'Q') { + getchar(); // read the new line char (Enter) + return; + } + i += n; + if (i < length) tv = tv.substr(n); + } + } +} + namespace cling { // General fallback - prints the address @@ -570,5 +595,22 @@ namespace cling { } return printUnpackedClingValue(V); } + + void printInternal(llvm::raw_ostream &Out, const Value &V) { + // Get the default type string representation + std::string typeStr = cling::valuePrinterInternal::printTypeInternal(V); + // Get the value string representation, by printValue() method overloading + std::string valueStr = cling::valuePrinterInternal::printValueInternal(V); + + std::string tv = typeStr + " " + valueStr; + + if (V.getInterpreter()->isInteractivePrintEnabled()) { + printInteractively(Out, tv); + } else { + // Print the type and the value: + Out << tv << "\n"; + } + } + } // end namespace valuePrinterInternal } // end namespace cling diff --git a/interpreter/cling/lib/MetaProcessor/MetaParser.cpp b/interpreter/cling/lib/MetaProcessor/MetaParser.cpp index e838c6b5df02f..ead8a09ea53a3 100644 --- a/interpreter/cling/lib/MetaProcessor/MetaParser.cpp +++ b/interpreter/cling/lib/MetaProcessor/MetaParser.cpp @@ -124,7 +124,7 @@ namespace cling { || isXCommand(actionResult, resultValue) ||isTCommand(actionResult) || isAtCommand() || isqCommand() || isUCommand(actionResult) || isICommand() - || isOCommand() || israwInputCommand() + || isOCommand() || israwInputCommand() || isInteractivePrintCommand() || isdebugCommand() || isprintDebugCommand() || isdynamicExtensionsCommand() || ishelpCommand() || isfileExCommand() || isfilesCommand() || isClassCommand() || isNamespaceCommand() || isgCommand() @@ -380,6 +380,20 @@ namespace cling { return false; } + bool MetaParser::isInteractivePrintCommand() { + if (getCurTok().is(tok::ident) && + getCurTok().getIdent().equals("interactive")) { + MetaSema::SwitchMode mode = MetaSema::kToggle; + consumeToken(); + skipWhitespace(); + if (getCurTok().is(tok::constant)) + mode = (MetaSema::SwitchMode)getCurTok().getConstantAsBool(); + m_Actions->actOnInteractivePrintCommand(mode); + return true; + } + return false; + } + bool MetaParser::isdebugCommand() { if (getCurTok().is(tok::ident) && getCurTok().getIdent().equals("debug")) { diff --git a/interpreter/cling/lib/MetaProcessor/MetaParser.h b/interpreter/cling/lib/MetaProcessor/MetaParser.h index 89dc9d15c7597..4d9bc2742e3a4 100644 --- a/interpreter/cling/lib/MetaProcessor/MetaParser.h +++ b/interpreter/cling/lib/MetaProcessor/MetaParser.h @@ -32,7 +32,8 @@ namespace cling { // PrintDebugCommand | DynamicExtensionsCommand | // HelpCommand | FileExCommand | FilesCommand | // ClassCommand | GCommand | StoreStateCommand | - // CompareStateCommand | StatsCommand | undoCommand + // CompareStateCommand | StatsCommand | undoCommand | + // InteractivePrintCommand // LCommand := 'L' FilePath // TCommand := 'T' FilePath FilePath // >Command := '>' FilePath @@ -42,6 +43,7 @@ namespace cling { // ICommand := 'I' [FilePath] // OCommand := 'O'[' ']Constant // RawInputCommand := 'rawInput' [Constant] + // InteractivePrintCommand := 'interactive' [Constant] // PrintDebugCommand := 'printDebug' [Constant] // DebugCommand := 'debug' [Constant] // StoreStateCommand := 'storeState' "Ident" @@ -94,6 +96,7 @@ namespace cling { bool isICommand(); bool isOCommand(); bool israwInputCommand(); + bool isInteractivePrintCommand(); bool isdebugCommand(); bool isprintDebugCommand(); bool isstoreStateCommand(); diff --git a/interpreter/cling/lib/MetaProcessor/MetaSema.cpp b/interpreter/cling/lib/MetaProcessor/MetaSema.cpp index 90d298fcf6741..30ae961b844fc 100644 --- a/interpreter/cling/lib/MetaProcessor/MetaSema.cpp +++ b/interpreter/cling/lib/MetaProcessor/MetaSema.cpp @@ -215,6 +215,17 @@ namespace cling { m_Interpreter.enableRawInput(mode); } + void MetaSema::actOnInteractivePrintCommand(SwitchMode mode/* = kToggle*/) const { + if (mode == kToggle) { + bool flag = !m_Interpreter.isInteractivePrintEnabled(); + m_Interpreter.enableInteractivePrint(flag); + // FIXME: + m_MetaProcessor.getOuts() << (flag ? "U" :"Not u") << "sing interactive printing\n"; + } + else + m_Interpreter.enableInteractivePrint(mode); + } + void MetaSema::actOndebugCommand(llvm::Optional mode) const { clang::CodeGenOptions& CGO = m_Interpreter.getCI()->getCodeGenOpts(); if (!mode) { @@ -331,6 +342,9 @@ namespace cling { "\n" " " << metaString << "rawInput [0|1]\t\t- Toggle wrapping and printing the" "\n\t\t\t\t execution results of the input\n" + "\n" + " " << metaString << "interactive [0|1]\t\t- Toggle interactive printing mode\n" + "\n" " " << metaString << "dynamicExtensions [0|1]\t- Toggles the use of the dynamic scopes and the" "\n\t\t\t\t late binding\n" diff --git a/interpreter/cling/lib/MetaProcessor/MetaSema.h b/interpreter/cling/lib/MetaProcessor/MetaSema.h index 8d23286c6b36b..e489af342dc54 100644 --- a/interpreter/cling/lib/MetaProcessor/MetaSema.h +++ b/interpreter/cling/lib/MetaProcessor/MetaSema.h @@ -143,6 +143,13 @@ namespace cling { /// void actOnrawInputCommand(SwitchMode mode = kToggle) const; + ///\brief Allows for the interactive outputting of the string returned + /// by Value::print(). + /// + ///\param[in] mode - either on/off or toggle. + /// + void actOnInteractivePrintCommand(SwitchMode mode = kToggle) const; + ///\brief Generates debug info for the JIT. /// ///\param[in] mode - either on/off or toggle. diff --git a/interpreter/cling/lib/UserInterface/UserInterface.cpp b/interpreter/cling/lib/UserInterface/UserInterface.cpp index ea042d9861e53..9f3d525cfda6a 100644 --- a/interpreter/cling/lib/UserInterface/UserInterface.cpp +++ b/interpreter/cling/lib/UserInterface/UserInterface.cpp @@ -117,6 +117,7 @@ namespace cling { std::unique_ptr R(StreamReader::Create()); std::unique_ptr D(TerminalDisplay::Create()); TextInput TI(*R, *D, histfilePath.empty() ? 0 : histfilePath.c_str()); + m_MetaProcessor->getInterpreter().enableInteractivePrint(D->IsTTY()); TI.SetPrompt("[cling]$ "); std::string line;