cling/lib/UserInterface/textinput/TerminalDisplay.cpp

256 lines
7.2 KiB
C++

//===--- TerminalDisplay.cpp - Output To Terminal ---------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines the abstract interface for writing to a terminal.
//
// Axel Naumann <axel@cern.ch>, 2011-05-12
//===----------------------------------------------------------------------===//
#include "textinput/TerminalDisplay.h"
#ifdef WIN32
#include "textinput/TerminalDisplayWin.h"
#else
#include "textinput/TerminalDisplayUnix.h"
#endif
#include "textinput/TextInput.h"
#include "textinput/Color.h"
#include "textinput/Text.h"
#include "textinput/Editor.h"
namespace textinput {
TerminalDisplay::~TerminalDisplay() {}
TerminalDisplay*
TerminalDisplay::Create() {
#ifdef WIN32
return new TerminalDisplayWin();
#else
return new TerminalDisplayUnix();
#endif
}
void
TerminalDisplay::NotifyTextChange(Range r) {
if (!IsTTY()) return;
Attach();
WriteWrapped(r.fPromptUpdate,GetContext()->GetTextInput()->IsInputHidden(),
r.fStart, r.fLength);
Move(GetCursor());
}
void
TerminalDisplay::NotifyCursorChange() {
Attach();
Move(GetCursor());
}
void
TerminalDisplay::NotifyResetInput() {
Attach();
if (IsTTY()) {
WriteRawString("\n", 1);
}
fWriteLen = 0;
fWritePos = Pos();
}
void
TerminalDisplay::NotifyError() {
Attach();
WriteRawString("\x07", 1);
}
void
TerminalDisplay::DisplayInfo(const std::vector<std::string>& Options) {
char infoColIdx = 0;
if (GetContext()->GetColorizer()) {
infoColIdx = GetContext()->GetColorizer()->GetInfoColor();
}
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);
WriteRawString("\n", 1);
}
// Reset position
Detach();
Attach();
}
void
TerminalDisplay::Detach() {
fWritePos = Pos();
fWriteLen = 0;
if (GetContext()->GetColorizer()) {
Color DefaultColor;
GetContext()->GetColorizer()->GetColor(0, DefaultColor);
SetColor(0, DefaultColor);
// We can't tell whether the application will activate a different color:
fPrevColor = -1;
}
}
size_t
TerminalDisplay::WriteWrappedElement(const Text& Element, size_t TextOffset,
size_t WriteOffset, size_t Requested) {
size_t Start = TextOffset;
size_t Remaining = Requested;
size_t Available = Element.length() - Start;
if (Requested == (size_t) -1) {
Requested = Available;
}
if (Available > 0) {
if (Available < Remaining) {
Remaining = Available;
}
while (Remaining > 0) {
size_t numThisLine = Remaining;
// How much can this line hold?
size_t numToEOL = GetWidth() - ((Start + WriteOffset) % GetWidth());
if (!numToEOL) {
MoveDown();
++fWritePos.fLine;
MoveFront();
fWritePos.fCol = 0;
numToEOL = GetWidth();
}
if (numThisLine > numToEOL) {
numThisLine = numToEOL;
}
if (GetContext()->GetColorizer()) {
// We only write same-color chunks; how long is it?
const std::vector<char>& Colors = Element.GetColors();
char ThisColor = Colors[Start];
size_t numSameColor = 1;
while (numSameColor < numThisLine
&& ThisColor == Colors[Start + numSameColor])
++numSameColor;
numThisLine = numSameColor;
if (ThisColor != fPrevColor) {
Color C;
GetContext()->GetColorizer()->GetColor(ThisColor, C);
SetColor(ThisColor, C);
fPrevColor = ThisColor;
}
}
WriteRawString(Element.GetText().c_str() + Start, numThisLine);
fWritePos = IndexToPos(PosToIndex(fWritePos) + numThisLine);
if (numThisLine == numToEOL) {
ActOnEOL();
}
Start += numThisLine;
Remaining -= 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
EraseToRight();
}
if (WroteL < VisL) {
Pos prevWC = GetCursor();
MoveFront();
fWritePos.fCol = 0;
for (size_t l = WroteL + 1; l <= VisL; ++l) {
MoveDown();
++fWritePos.fLine;
EraseToRight();
}
Move(prevWC);
}
}
return Remaining;
}
size_t
TerminalDisplay::WriteWrapped(Range::EPromptUpdate PromptUpdate, bool hidden,
size_t Offset, size_t Requested /* = -1*/) {
Attach();
const Text& Prompt = GetContext()->GetPrompt();
size_t PromptLen = GetContext()->GetPrompt().length();
const Text& EditPrompt = GetContext()->GetEditor()->GetEditorPrompt();
size_t EditorPromptLen = EditPrompt.length();
if (!IsTTY()) {
PromptLen = 0;
EditorPromptLen = 0;
PromptUpdate = Range::kNoPromptUpdate;
}
if (PromptUpdate & Range::kUpdatePrompt) {
// Writing from front means we write the prompt, too
Move(Pos());
WriteWrappedElement(Prompt, 0, 0, PromptLen);
}
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);
}
// Any prompt update means we'll have to re-write the text
Offset = 0;
Requested = (size_t) -1;
}
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);
} else {
avail = WriteWrappedElement(GetContext()->GetLine(), Offset,
PromptLen + EditorPromptLen, Requested);
}
fWriteLen = PromptLen + EditorPromptLen + GetContext()->GetLine().length();
return avail;
}
void
TerminalDisplay::Move(Pos p) {
Attach();
if (fWritePos == p) return;
if (fWritePos.fLine > p.fLine) {
MoveUp(fWritePos.fLine - p.fLine);
fWritePos.fLine -= fWritePos.fLine - p.fLine;
} else if (fWritePos.fLine < p.fLine) {
MoveDown(p.fLine - fWritePos.fLine);
fWritePos.fLine += p.fLine - fWritePos.fLine;
}
if (p.fCol == 0) {
MoveFront();
fWritePos.fCol = 0;
} else if (fWritePos.fCol > p.fCol) {
MoveLeft(fWritePos.fCol - p.fCol);
fWritePos.fCol -= fWritePos.fCol - p.fCol;
} else if (p.fCol > fWritePos.fCol) {
MoveRight(p.fCol - fWritePos.fCol);
fWritePos.fCol += p.fCol - fWritePos.fCol;
}
}
}