X-Git-Url: http://g0dil.de/git?a=blobdiff_plain;f=Utils%2FTermlib%2FEditor.cc;h=8d16f4db9d923eecec3450ce1aee9253417a7e42;hb=2e64ec3584f231af8c4aab0b780046b355ff64ae;hp=c52390702440c4f4ccb7b80812da9a1e14cc4268;hpb=844c117cb04bc73a5b920c2c49efbf14515da3e2;p=senf.git diff --git a/Utils/Termlib/Editor.cc b/Utils/Termlib/Editor.cc index c523907..8d16f4d 100644 --- a/Utils/Termlib/Editor.cc +++ b/Utils/Termlib/Editor.cc @@ -39,14 +39,15 @@ prefix_ senf::term::BaseEditor::BaseEditor(AbstractTerminal & terminal) keyTimeout_ (senf::ClockService::milliseconds(DEFAULT_KEY_TIMEOUT_MS)), timer_ ("senf::term::BaseEditor::keySequenceTimeout", senf::membind(&BaseEditor::keySequenceTimeout, this)), - column_ (0u) + column_ (0u), displayHeight_ (1u), line_ (0u) { terminal_->setCallbacks(*this); } prefix_ void senf::term::BaseEditor::newline() { - write("\r\n"); + reset(); + write("\n"); write(tifo_.getString(Terminfo::properties::ClrEol)); column_ = 0; } @@ -74,7 +75,7 @@ prefix_ void senf::term::BaseEditor::toColumn(unsigned c) column_ = c; } else { - char const * cub1 (tifo_.getString(Terminfo::properties::CursorRight)); + char const * cub1 (tifo_.getString(Terminfo::properties::CursorLeft)); while (c < column_) { write(cub1); --column_; @@ -83,44 +84,137 @@ prefix_ void senf::term::BaseEditor::toColumn(unsigned c) } } -prefix_ void senf::term::BaseEditor::insertChar(char ch) +prefix_ void senf::term::BaseEditor::put(char ch) { - if (column_+1 >= width()) + if (column_ >= width()-1) return; - if (tifo_.hasProperty(Terminfo::properties::InsertCharacter)) - write(tifo_.getString(Terminfo::properties::InsertCharacter)); - else - write(tifo_.formatString(Terminfo::properties::ParmIch, 1)); write(ch); ++ column_; } -prefix_ void senf::term::BaseEditor::overwriteChar(char ch) +prefix_ void senf::term::BaseEditor::put(std::string const & text) { - write(ch); - ++ column_; + if (text.size() > width()-column_-1) { + write(text.substr(0,width()-column_-1)); + column_ = width() - 1; + } + else { + write(text); + column_ += text.size(); + } } -prefix_ void senf::term::BaseEditor::deleteChar() +prefix_ void senf::term::BaseEditor::clearLine() { - if (tifo_.hasProperty(Terminfo::properties::DeleteCharacter)) - write(tifo_.getString(Terminfo::properties::DeleteCharacter)); - else - write(tifo_.formatString(Terminfo::properties::ParmDch, 1)); + write("\r"); + write(tifo_.getString(Terminfo::properties::ClrEol)); + column_ = 0; +} + +prefix_ void senf::term::BaseEditor::setBold() +{ + if (tifo_.hasProperty(Terminfo::properties::EnterBoldMode) && + tifo_.hasProperty(Terminfo::properties::ExitAttributeMode)) + write(tifo_.getString(Terminfo::properties::EnterBoldMode)); +} + +prefix_ void senf::term::BaseEditor::setNormal() +{ + if (tifo_.hasProperty(Terminfo::properties::EnterBoldMode) && + tifo_.hasProperty(Terminfo::properties::ExitAttributeMode)) + write(tifo_.getString(Terminfo::properties::ExitAttributeMode)); +} + +prefix_ void senf::term::BaseEditor::maybeClrScr() +{ + if (tifo_.hasProperty(Terminfo::properties::ClearScreen)) + write(tifo_.getString(Terminfo::properties::ClearScreen)); +} + +prefix_ void senf::term::BaseEditor::toLine(unsigned l) +{ + if (l >= height()) + l = height() - 1; + unsigned ll (l); + if (ll >= displayHeight_) + ll = displayHeight_-1; + if (ll > line_) { + if (tifo_.hasProperty(Terminfo::properties::ParmDownCursor)) { + write(tifo_.formatString(Terminfo::properties::ParmDownCursor, ll - line_)); + line_ = ll; + } + else { + char const * cud1 (tifo_.getString(Terminfo::properties::CursorDown)); + while (ll > line_) { + write(cud1); + ++line_; + } + } + } + else if (ll < line_) { + if (tifo_.hasProperty(Terminfo::properties::ParmUpCursor)) { + write(tifo_.formatString(Terminfo::properties::ParmUpCursor, line_ - ll)); + line_ = ll; + } + else { + char const * cuu1 (tifo_.getString(Terminfo::properties::CursorUp)); + while (ll < line_) { + write(cuu1); + --line_; + } + } + } + while (line_ < l) { + write("\n"); + write(tifo_.getString(Terminfo::properties::ClrEol)); + ++displayHeight_; + ++line_; + } + write('\r'); + column_ = 0; +} + +prefix_ void senf::term::BaseEditor::reset() +{ + for (unsigned i (1); i < displayHeight_; ++i) { + toLine(i); + clearLine(); + } + toLine(0); + displayHeight_ = 1; } prefix_ unsigned senf::term::BaseEditor::currentColumn() - const + const { return column_; } -prefix_ void senf::term::BaseEditor::cb_init() +prefix_ unsigned senf::term::BaseEditor::currentLine() + const +{ + return line_; +} + +prefix_ bool senf::term::BaseEditor::cb_init() { - tifo_.load(terminal_->terminalType()); - keyParser_.load(tifo_); + try { + tifo_.load(terminal_->terminalType()); + keyParser_.load(tifo_); + } + catch (Terminfo::InvalidTerminfoException & ex) { + return false; + } + + typedef Terminfo::properties p; + if (! (tifo_.hasProperty(p::ClrEol) && + (tifo_.hasProperty(p::ParmRightCursor) || tifo_.hasProperty(p::CursorRight)) && + (tifo_.hasProperty(p::ParmLeftCursor) || tifo_.hasProperty(p::CursorLeft)))) + return false; + if (tifo_.hasProperty(Terminfo::properties::KeypadXmit)) write(tifo_.getString(Terminfo::properties::KeypadXmit)); + return true; } prefix_ void senf::term::BaseEditor::cb_charReceived(char c) @@ -163,6 +257,11 @@ prefix_ unsigned senf::term::BaseEditor::width() return terminal_->width(); } +prefix_ unsigned senf::term::BaseEditor::height() +{ + return terminal_->height(); +} + prefix_ void senf::term::BaseEditor::write(char ch) { terminal_->write(ch); @@ -174,6 +273,426 @@ prefix_ void senf::term::BaseEditor::write(std::string const & s) write(*i); } +/////////////////////////////////////////////////////////////////////////// + +prefix_ senf::term::LineEditor::LineEditor(AbstractTerminal & terminal, AcceptCallback cb) + : BaseEditor(terminal), enabled_ (true), prompt_ ("$"), promptWidth_ (1u), editWidth_ (0u), + text_ (""), point_ (0u), displayPos_ (0u), lastKey_ (0u), callback_ (cb), historyPoint_ (0u) +{ + defineKey(KeyParser::Return, &bindings::accept); + defineKey(KeyParser::Right, &bindings::forwardChar); + defineKey(KeyParser::Left, &bindings::backwardChar); + defineKey(KeyParser::Up, &bindings::prevHistory); + defineKey(KeyParser::Down, &bindings::nextHistory); + defineKey(KeyParser::Backspace, &bindings::backwardDeleteChar); + defineKey(KeyParser::Delete, &bindings::deleteChar); + defineKey(KeyParser::Home, &bindings::beginningOfLine); + defineKey(KeyParser::End, &bindings::endOfLine); + defineKey(KeyParser::Ctrl('K'), &bindings::deleteToEndOfLine); + defineKey(KeyParser::Ctrl('A'), &bindings::beginningOfLine); + defineKey(KeyParser::Ctrl('E'), &bindings::endOfLine); + defineKey(KeyParser::Ctrl('D'), &bindings::deleteChar); + defineKey(KeyParser::Ctrl('C'), &bindings::restartEdit); + defineKey(KeyParser::Ctrl('L'), &bindings::clearScreen); +} + +prefix_ void senf::term::LineEditor::prompt(std::string const & text) +{ + prompt_ = text; + promptWidth_ = prompt_.size(); + if (promptWidth_ > width() - 4 && width() > 4) + promptWidth_ = width() - 4; + editWidth_ = width() - promptWidth_ - 3; + if (enabled_) + redisplay(); +} + +prefix_ void senf::term::LineEditor::set(std::string const & text, unsigned pos) +{ + text_ = text; + point_ = pos; + if (point_ > text.size()) + point_ = text.size(); + displayPos_ = 0u; + if (point_ > editWidth_) + displayPos_ = point_ - editWidth_; + redisplay(); +} + +prefix_ void senf::term::LineEditor::show() +{ + if (enabled_) + return; + enabled_ = true; + for (unsigned n (0); n < auxDisplay_.size(); ++n) { + toLine(n+1); + put(auxDisplay_[n]); + } + toLine(0); + forceRedisplay(); +} + +prefix_ void senf::term::LineEditor::hide() +{ + if (! enabled_) + return; + reset(); + clearLine(); + enabled_ = false; +} + +prefix_ void senf::term::LineEditor::accept() +{ + if (enabled_) + newline(); + hide(); + pushHistory(text_); + callback_(text_); + clear(); +} + +prefix_ void senf::term::LineEditor::clear() +{ + set(""); + historyPoint_ = history_.size(); +} + +prefix_ void senf::term::LineEditor::redisplay() +{ + redisplayNeeded_ = true; +} + +prefix_ void senf::term::LineEditor::forceRedisplay() +{ + if (! enabled_) + return; + clearLine(); + setBold(); + if (prompt_.size() > promptWidth_) + put(prompt_.substr(prompt_.size()-promptWidth_)); + else + put(prompt_); + put( displayPos_ > 0 ? '<' : ' ' ); + if (text_.size() > displayPos_ + editWidth_) { + toColumn(editWidth_ + promptWidth_ + 1); + put('>'); + toColumn(promptWidth_ + 1); + } + setNormal(); + put(text_.substr(displayPos_, editWidth_)); + toColumn(point_ - displayPos_ + promptWidth_ + 1); + redisplayNeeded_ = false; +} + +prefix_ void senf::term::LineEditor::gotoChar(unsigned n) +{ + point_ = n; + if (point_ > text_.size()) + point_ = text_.size(); + if (point_ < displayPos_) + displayPos_ = point_; + if (point_ > displayPos_+editWidth_) + displayPos_ = point_-editWidth_; + redisplay(); +} + +prefix_ void senf::term::LineEditor::scrollTo(unsigned n) +{ + displayPos_ = n; + if (displayPos_ > text_.size()) + displayPos_ = text_.size(); + if (point_ < displayPos_) + point_ = displayPos_; + if (point_ > displayPos_+editWidth_) + point_ = displayPos_+editWidth_; + redisplay(); +} + +prefix_ void senf::term::LineEditor::deleteChar(unsigned n) +{ + if (point_ >= text_.size()) + return; + text_.erase(point_, n); + redisplay(); +} + +prefix_ void senf::term::LineEditor::insert(char ch) +{ + text_.insert(point_, std::string(1, ch)); + gotoChar(point_+1); + redisplay(); +} + +prefix_ void senf::term::LineEditor::insert(std::string const & text) +{ + text_.insert(point_, text); + gotoChar(point_+text.size()); + redisplay(); +} + +prefix_ void senf::term::LineEditor::pushHistory(std::string const & text) +{ + if (! text.empty() + && (historyPoint_ == history_.size() || history_[historyPoint_] != text) + && (history_.empty() || history_.back() != text)) { + history_.push_back(text); + while (history_.size() > MAX_HISTORY_SIZE) + history_.erase(history_.begin()); + historyPoint_ = history_.size() - 1; + } +} + +prefix_ void senf::term::LineEditor::prevHistory() +{ + if (historyPoint_ <= 0) + return; + pushHistory(text_); + std::string entry (history_[--historyPoint_]); + set(entry, entry.size()); +} + +prefix_ void senf::term::LineEditor::nextHistory() +{ + if (historyPoint_ >= history_.size()) + return; + pushHistory(text_); + ++ historyPoint_; + if (historyPoint_ >= history_.size()) + set(""); + else { + std::string entry (history_[historyPoint_]); + set(entry, entry.size()); + } +} + +prefix_ void senf::term::LineEditor::auxDisplay(int line, std::string const & text) +{ + toLine(line+1); + clearLine(); + put(text); + while (auxDisplay_.size() < line+1) + auxDisplay_.push_back(""); + auxDisplay_[line] = text; +} + +prefix_ unsigned senf::term::LineEditor::maxAuxDisplayHeight() +{ + return height()-1; +} + +prefix_ void senf::term::LineEditor::clearAuxDisplay() +{ + reset(); + auxDisplay_.clear(); +} + +prefix_ std::string const & senf::term::LineEditor::text() +{ + return text_; +} + +prefix_ unsigned senf::term::LineEditor::point() +{ + return point_; +} + +prefix_ unsigned senf::term::LineEditor::displayPos() +{ + return displayPos_; +} + +prefix_ senf::term::LineEditor::keycode_t senf::term::LineEditor::lastKey() +{ + return lastKey_; +} + +prefix_ void senf::term::LineEditor::defineKey(keycode_t key, KeyBinding binding) +{ + bindings_[key] = binding; +} + +prefix_ void senf::term::LineEditor::unsetKey(keycode_t key) +{ + bindings_.erase(key); +} + +prefix_ bool senf::term::LineEditor::cb_init() +{ + if (!BaseEditor::cb_init()) + return false; + prompt(prompt_); + forceRedisplay(); + return true; +} + +prefix_ void senf::term::LineEditor::cb_windowSizeChanged() +{ + BaseEditor::cb_windowSizeChanged(); + clearAuxDisplay(); + prompt(prompt_); + gotoChar(point_); + forceRedisplay(); +} + +prefix_ void senf::term::LineEditor::v_keyReceived(keycode_t key) +{ + if (! enabled_) + return; + clearAuxDisplay(); + lastKey_ = key; + KeyMap::iterator i (bindings_.find(key)); + if (i != bindings_.end()) + i->second(*this); + else if (key >= ' ' && key < 256) + insert(char(key)); + if (currentLine() != 0) + toLine(0); + if (redisplayNeeded_) + forceRedisplay(); + else + toColumn(point_ - displayPos_ + promptWidth_ + 1); +} + +/////////////////////////////////////////////////////////////////////////// + +prefix_ void senf::term::bindings::selfInsertCommand(LineEditor & editor) +{ + LineEditor::keycode_t key (editor.lastKey()); + if (key >= ' ' && key < 256) + editor.insert(key); +} + +prefix_ void senf::term::bindings::forwardChar(LineEditor & editor) +{ + editor.gotoChar(editor.point()+1); +} + +prefix_ void senf::term::bindings::backwardChar(LineEditor & editor) +{ + unsigned p (editor.point()); + if (p>0) + editor.gotoChar(p-1); +} + +prefix_ void senf::term::bindings::accept(LineEditor & editor) +{ + editor.accept(); +} + +prefix_ void senf::term::bindings::backwardDeleteChar(LineEditor & editor) +{ + unsigned p (editor.point()); + if (p>0) { + editor.gotoChar(p-1); + editor.deleteChar(); + } +} + +prefix_ void senf::term::bindings::deleteChar(LineEditor & editor) +{ + editor.deleteChar(); +} + +prefix_ void senf::term::bindings::beginningOfLine(LineEditor & editor) +{ + editor.gotoChar(0u); +} + +prefix_ void senf::term::bindings::endOfLine(LineEditor & editor) +{ + editor.gotoChar(editor.text().size()); +} + +prefix_ void senf::term::bindings::deleteToEndOfLine(LineEditor & editor) +{ + editor.deleteChar(editor.text().size()-editor.point()); +} + +prefix_ void senf::term::bindings::restartEdit(LineEditor & editor) +{ + editor.newline(); + editor.clear(); + editor.redisplay(); +} + +prefix_ void senf::term::bindings::prevHistory(LineEditor & editor) +{ + editor.prevHistory(); +} + +prefix_ void senf::term::bindings::nextHistory(LineEditor & editor) +{ + editor.nextHistory(); +} + +prefix_ void senf::term::bindings::clearScreen(LineEditor & editor) +{ + editor.maybeClrScr(); + editor.clearLine(); + editor.forceRedisplay(); +} + +prefix_ void senf::term::bindings::complete(LineEditor & editor, Completer completer) +{ + typedef std::vector Completions; + + Completions completions; + completer(editor, 0, editor.point(), completions); + if (completions.empty()) + return; + + // Find common start string of all completions + unsigned commonStart (completions[0].size()); + unsigned maxLen (commonStart); + for (Completions::const_iterator i (boost::next(completions.begin())); + i != completions.end(); ++i) { + if (i->size() > maxLen) + maxLen = i->size(); + unsigned n (0u); + for (; n < commonStart && n < i->size() && completions[0][n] == (*i)[n]; ++n) ; + commonStart = n; + } + + // Replace to-be-completed string with the common start string shared by all completions + std::string text (editor.text()); + std::string completion (completions[0].substr(0, commonStart)); + bool didComplete (false); + if (text.substr(0, editor.point()) != completion) { + text.erase(0, editor.point()); + text.insert(0, completion); + didComplete = true; + } + + // Otherwise place cursor directly after the (possibly partial) completion + editor.set(text, commonStart); + if (didComplete || completions.size() == 1) + return; + + // Text was not changed, show list of possible completions + unsigned colWidth (maxLen+2); + unsigned nColumns ((editor.width()-1) / colWidth); + if (nColumns < 1) nColumns = 1; + unsigned nRows ((completions.size()+nColumns-1) / nColumns); + if (nRows > editor.maxAuxDisplayHeight()) { + editor.auxDisplay(0, "(too many completions)"); + return; + } + Completions::iterator i (completions.begin()); + for (unsigned row (0); row < nRows; ++row) { + std::string line; + for (unsigned column (0); column < nColumns && i != completions.end(); ++column) { + std::string entry (colWidth, ' '); + if (i->size() > colWidth-2) + std::copy(i->begin(), i->begin()+colWidth-2, entry.begin()); + else + std::copy(i->begin(), i->end(), entry.begin()); + line += entry; + ++i; + } + editor.auxDisplay(row, line); + } +} + ///////////////////////////////cc.e//////////////////////////////////////// #undef prefix_ //#include "Editor.mpp"