00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00026 #include "Editor.hh"
00027
00028
00029
00030 #include <senf/Utils/membind.hh>
00031 #include <senf/Scheduler/Scheduler.hh>
00032
00033
00034 #define prefix_
00035
00036
00037 prefix_ senf::term::BaseEditor::BaseEditor(AbstractTerminal & terminal)
00038 : terminal_ (&terminal),
00039 keyTimeout_ (ClockService::milliseconds(DEFAULT_KEY_TIMEOUT_MS)),
00040 timer_ ("senf::term::BaseEditor::keySequenceTimeout",
00041 membind(&BaseEditor::keySequenceTimeout, this)),
00042 column_ (0u), displayHeight_ (1u), line_ (0u)
00043 {
00044 terminal_->setCallbacks(*this);
00045 }
00046
00047 prefix_ void senf::term::BaseEditor::newline()
00048 {
00049 reset();
00050 write("\n");
00051 write(tifo_.getString(Terminfo::properties::ClrEol));
00052 column_ = 0;
00053 }
00054
00055 prefix_ void senf::term::BaseEditor::toColumn(unsigned c)
00056 {
00057 if (c >= width())
00058 c = width();
00059 if (c > column_) {
00060 if (tifo_.hasProperty(Terminfo::properties::ParmRightCursor)) {
00061 write(tifo_.formatString(Terminfo::properties::ParmRightCursor, c - column_));
00062 column_ = c;
00063 }
00064 else {
00065 char const * cuf1 (tifo_.getString(Terminfo::properties::CursorRight));
00066 while (c > column_) {
00067 write(cuf1);
00068 ++column_;
00069 }
00070 }
00071 }
00072 else if (c < column_) {
00073 if (tifo_.hasProperty(Terminfo::properties::ParmLeftCursor)) {
00074 write(tifo_.formatString(Terminfo::properties::ParmLeftCursor, column_ - c));
00075 column_ = c;
00076 }
00077 else {
00078 char const * cub1 (tifo_.getString(Terminfo::properties::CursorLeft));
00079 while (c < column_) {
00080 write(cub1);
00081 --column_;
00082 }
00083 }
00084 }
00085 }
00086
00087 prefix_ void senf::term::BaseEditor::put(char ch)
00088 {
00089 if (column_ >= width()-1)
00090 return;
00091 write(ch);
00092 ++ column_;
00093 }
00094
00095 prefix_ void senf::term::BaseEditor::put(std::string const & text)
00096 {
00097 if (text.size() > width()-column_-1) {
00098 write(text.substr(0,width()-column_-1));
00099 column_ = width() - 1;
00100 }
00101 else {
00102 write(text);
00103 column_ += text.size();
00104 }
00105 }
00106
00107 prefix_ void senf::term::BaseEditor::clearLine()
00108 {
00109 write("\r");
00110 write(tifo_.getString(Terminfo::properties::ClrEol));
00111 column_ = 0;
00112 }
00113
00114 prefix_ void senf::term::BaseEditor::setBold()
00115 {
00116 if (tifo_.hasProperty(Terminfo::properties::EnterBoldMode) &&
00117 tifo_.hasProperty(Terminfo::properties::ExitAttributeMode))
00118 write(tifo_.getString(Terminfo::properties::EnterBoldMode));
00119 }
00120
00121 prefix_ void senf::term::BaseEditor::setNormal()
00122 {
00123 if (tifo_.hasProperty(Terminfo::properties::EnterBoldMode) &&
00124 tifo_.hasProperty(Terminfo::properties::ExitAttributeMode))
00125 write(tifo_.getString(Terminfo::properties::ExitAttributeMode));
00126 }
00127
00128 prefix_ void senf::term::BaseEditor::maybeClrScr()
00129 {
00130 if (tifo_.hasProperty(Terminfo::properties::ClearScreen))
00131 write(tifo_.getString(Terminfo::properties::ClearScreen));
00132 }
00133
00134 prefix_ void senf::term::BaseEditor::toLine(unsigned l)
00135 {
00136 if (l >= height())
00137 l = height() - 1;
00138 unsigned ll (l);
00139 if (ll >= displayHeight_)
00140 ll = displayHeight_-1;
00141 if (ll > line_) {
00142 if (tifo_.hasProperty(Terminfo::properties::ParmDownCursor)) {
00143 write(tifo_.formatString(Terminfo::properties::ParmDownCursor, ll - line_));
00144 line_ = ll;
00145 }
00146 else {
00147 char const * cud1 (tifo_.getString(Terminfo::properties::CursorDown));
00148 while (ll > line_) {
00149 write(cud1);
00150 ++line_;
00151 }
00152 }
00153 }
00154 else if (ll < line_) {
00155 if (tifo_.hasProperty(Terminfo::properties::ParmUpCursor)) {
00156 write(tifo_.formatString(Terminfo::properties::ParmUpCursor, line_ - ll));
00157 line_ = ll;
00158 }
00159 else {
00160 char const * cuu1 (tifo_.getString(Terminfo::properties::CursorUp));
00161 while (ll < line_) {
00162 write(cuu1);
00163 --line_;
00164 }
00165 }
00166 }
00167 while (line_ < l) {
00168 write("\n");
00169 write(tifo_.getString(Terminfo::properties::ClrEol));
00170 ++displayHeight_;
00171 ++line_;
00172 }
00173 write('\r');
00174 column_ = 0;
00175 }
00176
00177 prefix_ void senf::term::BaseEditor::reset()
00178 {
00179 for (unsigned i (1); i < displayHeight_; ++i) {
00180 toLine(i);
00181 clearLine();
00182 }
00183 toLine(0);
00184 displayHeight_ = 1;
00185 }
00186
00187 prefix_ unsigned senf::term::BaseEditor::currentColumn()
00188 const
00189 {
00190 return column_;
00191 }
00192
00193 prefix_ unsigned senf::term::BaseEditor::currentLine()
00194 const
00195 {
00196 return line_;
00197 }
00198
00199 prefix_ bool senf::term::BaseEditor::cb_init()
00200 {
00201 try {
00202 tifo_.load(terminal_->terminalType());
00203 keyParser_.load(tifo_);
00204 }
00205 catch (Terminfo::InvalidTerminfoException & ex) {
00206 return false;
00207 }
00208
00209 typedef Terminfo::properties p;
00210 if (! (tifo_.hasProperty(p::ClrEol) &&
00211 (tifo_.hasProperty(p::ParmRightCursor) || tifo_.hasProperty(p::CursorRight)) &&
00212 (tifo_.hasProperty(p::ParmLeftCursor) || tifo_.hasProperty(p::CursorLeft))))
00213 return false;
00214
00215 if (tifo_.hasProperty(Terminfo::properties::KeypadXmit))
00216 write(tifo_.getString(Terminfo::properties::KeypadXmit));
00217 return true;
00218 }
00219
00220 prefix_ void senf::term::BaseEditor::cb_charReceived(char c)
00221 {
00222 inputBuffer_ += c;
00223 timer_.timeout(scheduler::eventTime() + keyTimeout_);
00224 processKeys();
00225 }
00226
00227 prefix_ void senf::term::BaseEditor::cb_windowSizeChanged()
00228 {
00229 if (column_ >= width())
00230 column_ = width()-1;
00231 }
00232
00233 prefix_ void senf::term::BaseEditor::keySequenceTimeout()
00234 {
00235 while (!inputBuffer_.empty()) {
00236 processKeys();
00237 v_keyReceived(keycode_t(inputBuffer_[0]));
00238 inputBuffer_.erase(0, 1);
00239 }
00240 }
00241
00242 prefix_ void senf::term::BaseEditor::processKeys()
00243 {
00244 do {
00245 std::pair<KeyParser::keycode_t, std::string::size_type> result
00246 (keyParser_.lookup(inputBuffer_));
00247 if (result.first == KeyParser::Incomplete)
00248 return;
00249 v_keyReceived(result.first);
00250 inputBuffer_.erase(0, result.second);
00251 } while (! inputBuffer_.empty());
00252 timer_.disable();
00253 }
00254
00255 prefix_ unsigned senf::term::BaseEditor::width()
00256 const
00257 {
00258 return terminal_->width();
00259 }
00260
00261 prefix_ unsigned senf::term::BaseEditor::height()
00262 const
00263 {
00264 return terminal_->height();
00265 }
00266
00267 prefix_ void senf::term::BaseEditor::write(char ch)
00268 {
00269 terminal_->write(ch);
00270 }
00271
00272 prefix_ void senf::term::BaseEditor::write(std::string const & s)
00273 {
00274 for (std::string::const_iterator i (s.begin()); i != s.end(); ++i)
00275 write(*i);
00276 }
00277
00278
00279
00280 prefix_ senf::term::LineEditor::LineEditor(AbstractTerminal & terminal, AcceptCallback cb)
00281 : BaseEditor(terminal), enabled_ (false), prompt_ ("$"), promptWidth_ (1u), editWidth_ (0u),
00282 text_ (""), point_ (0u), displayPos_ (0u), lastKey_ (0u), callback_ (cb), historyPoint_ (0u)
00283 {
00284 defineKey(KeyParser::Return, &bindings::accept);
00285 defineKey(KeyParser::Right, &bindings::forwardChar);
00286 defineKey(KeyParser::Left, &bindings::backwardChar);
00287 defineKey(KeyParser::Up, &bindings::prevHistory);
00288 defineKey(KeyParser::Down, &bindings::nextHistory);
00289 defineKey(KeyParser::Backspace, &bindings::backwardDeleteChar);
00290 defineKey(KeyParser::Delete, &bindings::deleteChar);
00291 defineKey(KeyParser::Home, &bindings::beginningOfLine);
00292 defineKey(KeyParser::End, &bindings::endOfLine);
00293 defineKey(KeyParser::Ctrl('K'), &bindings::deleteToEndOfLine);
00294 defineKey(KeyParser::Ctrl('A'), &bindings::beginningOfLine);
00295 defineKey(KeyParser::Ctrl('E'), &bindings::endOfLine);
00296 defineKey(KeyParser::Ctrl('D'), &bindings::deleteChar);
00297 defineKey(KeyParser::Ctrl('C'), &bindings::restartEdit);
00298 defineKey(KeyParser::Ctrl('L'), &bindings::clearScreen);
00299 }
00300
00301 prefix_ void senf::term::LineEditor::prompt(std::string const & text)
00302 {
00303 prompt_ = text;
00304 promptWidth_ = prompt_.size();
00305 if (promptWidth_ > width() - 4 && width() > 4)
00306 promptWidth_ = width() - 4;
00307 editWidth_ = width() - promptWidth_ - 3;
00308 if (enabled_)
00309 redisplay();
00310 }
00311
00312 prefix_ void senf::term::LineEditor::set(std::string const & text, unsigned pos)
00313 {
00314 text_ = text;
00315 point_ = pos;
00316 if (point_ > text.size())
00317 point_ = text.size();
00318 displayPos_ = 0u;
00319 if (point_ > editWidth_)
00320 displayPos_ = point_ - editWidth_;
00321 redisplay();
00322 }
00323
00324 prefix_ void senf::term::LineEditor::show()
00325 {
00326 if (enabled_)
00327 return;
00328 enabled_ = true;
00329 for (unsigned n (0); n < auxDisplay_.size(); ++n) {
00330 toLine(n+1);
00331 put(auxDisplay_[n]);
00332 }
00333 toLine(0);
00334 forceRedisplay();
00335 }
00336
00337 prefix_ void senf::term::LineEditor::hide()
00338 {
00339 if (! enabled_)
00340 return;
00341 reset();
00342 clearLine();
00343 enabled_ = false;
00344 }
00345
00346 prefix_ void senf::term::LineEditor::accept()
00347 {
00348 if (enabled_)
00349 newline();
00350 hide();
00351 pushHistory(text_, true);
00352 callback_(text_);
00353 clear();
00354 }
00355
00356 prefix_ void senf::term::LineEditor::clear()
00357 {
00358 set("");
00359 historyPoint_ = history_.size();
00360 }
00361
00362 prefix_ void senf::term::LineEditor::redisplay()
00363 {
00364 redisplayNeeded_ = true;
00365 }
00366
00367 prefix_ void senf::term::LineEditor::forceRedisplay()
00368 {
00369 if (! enabled_)
00370 return;
00371 clearLine();
00372 setBold();
00373 if (prompt_.size() > promptWidth_)
00374 put(prompt_.substr(prompt_.size()-promptWidth_));
00375 else
00376 put(prompt_);
00377 put( displayPos_ > 0 ? '<' : ' ' );
00378 if (text_.size() > displayPos_ + editWidth_) {
00379 toColumn(editWidth_ + promptWidth_ + 1);
00380 put('>');
00381 toColumn(promptWidth_ + 1);
00382 }
00383 setNormal();
00384 put(text_.substr(displayPos_, editWidth_));
00385 toColumn(point_ - displayPos_ + promptWidth_ + 1);
00386 redisplayNeeded_ = false;
00387 }
00388
00389 prefix_ void senf::term::LineEditor::gotoChar(unsigned n)
00390 {
00391 point_ = n;
00392 if (point_ > text_.size())
00393 point_ = text_.size();
00394 if (point_ < displayPos_)
00395 displayPos_ = point_;
00396 if (point_ > displayPos_+editWidth_)
00397 displayPos_ = point_-editWidth_;
00398 redisplay();
00399 }
00400
00401 prefix_ void senf::term::LineEditor::scrollTo(unsigned n)
00402 {
00403 displayPos_ = n;
00404 if (displayPos_ > text_.size())
00405 displayPos_ = text_.size();
00406 if (point_ < displayPos_)
00407 point_ = displayPos_;
00408 if (point_ > displayPos_+editWidth_)
00409 point_ = displayPos_+editWidth_;
00410 redisplay();
00411 }
00412
00413 prefix_ void senf::term::LineEditor::deleteChar(unsigned n)
00414 {
00415 if (point_ >= text_.size())
00416 return;
00417 text_.erase(point_, n);
00418 redisplay();
00419 }
00420
00421 prefix_ void senf::term::LineEditor::insert(char ch)
00422 {
00423 text_.insert(point_, std::string(1, ch));
00424 gotoChar(point_+1);
00425 redisplay();
00426 }
00427
00428 prefix_ void senf::term::LineEditor::insert(std::string const & text)
00429 {
00430 text_.insert(point_, text);
00431 gotoChar(point_+text.size());
00432 redisplay();
00433 }
00434
00435 prefix_ void senf::term::LineEditor::pushHistory(std::string const & text, bool accept)
00436 {
00437 if (! text.empty()
00438 && (accept || historyPoint_ == history_.size() || history_[historyPoint_] != text)
00439 && (history_.empty() || history_.back() != text)) {
00440 history_.push_back(text);
00441 while (history_.size() > MAX_HISTORY_SIZE)
00442 history_.erase(history_.begin());
00443 if (accept)
00444 historyPoint_ = history_.size() - 1;
00445 }
00446 }
00447
00448 prefix_ void senf::term::LineEditor::prevHistory()
00449 {
00450 if (historyPoint_ <= 0)
00451 return;
00452 pushHistory(text_);
00453 std::string entry (history_[--historyPoint_]);
00454 set(entry, entry.size());
00455 }
00456
00457 prefix_ void senf::term::LineEditor::nextHistory()
00458 {
00459 if (historyPoint_ >= history_.size())
00460 return;
00461 pushHistory(text_);
00462 ++ historyPoint_;
00463 if (historyPoint_ >= history_.size())
00464 set("");
00465 else {
00466 std::string entry (history_[historyPoint_]);
00467 set(entry, entry.size());
00468 }
00469 }
00470
00471 prefix_ void senf::term::LineEditor::auxDisplay(unsigned line, std::string const & text)
00472 {
00473 toLine(line+1);
00474 clearLine();
00475 put(text);
00476 while (auxDisplay_.size() < line+1)
00477 auxDisplay_.push_back("");
00478 auxDisplay_[line] = text;
00479 }
00480
00481 prefix_ unsigned senf::term::LineEditor::maxAuxDisplayHeight()
00482 {
00483 return height()-1;
00484 }
00485
00486 prefix_ void senf::term::LineEditor::clearAuxDisplay()
00487 {
00488 reset();
00489 auxDisplay_.clear();
00490 }
00491
00492 prefix_ std::string const & senf::term::LineEditor::text()
00493 {
00494 return text_;
00495 }
00496
00497 prefix_ unsigned senf::term::LineEditor::point()
00498 {
00499 return point_;
00500 }
00501
00502 prefix_ unsigned senf::term::LineEditor::displayPos()
00503 {
00504 return displayPos_;
00505 }
00506
00507 prefix_ senf::term::LineEditor::keycode_t senf::term::LineEditor::lastKey()
00508 {
00509 return lastKey_;
00510 }
00511
00512 prefix_ void senf::term::LineEditor::defineKey(keycode_t key, KeyBinding binding)
00513 {
00514 bindings_[key] = binding;
00515 }
00516
00517 prefix_ void senf::term::LineEditor::unsetKey(keycode_t key)
00518 {
00519 bindings_.erase(key);
00520 }
00521
00522 prefix_ bool senf::term::LineEditor::cb_init()
00523 {
00524 if (!BaseEditor::cb_init())
00525 return false;
00526 prompt(prompt_);
00527 show();
00528 return true;
00529 }
00530
00531 prefix_ void senf::term::LineEditor::cb_windowSizeChanged()
00532 {
00533 BaseEditor::cb_windowSizeChanged();
00534 clearAuxDisplay();
00535 prompt(prompt_);
00536 gotoChar(point_);
00537 forceRedisplay();
00538 }
00539
00540 prefix_ void senf::term::LineEditor::v_keyReceived(keycode_t key)
00541 {
00542 if (! enabled_)
00543 return;
00544 clearAuxDisplay();
00545 lastKey_ = key;
00546 KeyMap::iterator i (bindings_.find(key));
00547 if (i != bindings_.end())
00548 i->second(*this);
00549 else if (key >= ' ' && key < 256)
00550 insert(char(key));
00551 if (currentLine() != 0)
00552 toLine(0);
00553 if (redisplayNeeded_)
00554 forceRedisplay();
00555 else
00556 toColumn(point_ - displayPos_ + promptWidth_ + 1);
00557 }
00558
00559
00560
00561 prefix_ void senf::term::bindings::selfInsertCommand(LineEditor & editor)
00562 {
00563 LineEditor::keycode_t key (editor.lastKey());
00564 if (key >= ' ' && key < 256)
00565 editor.insert(key);
00566 }
00567
00568 prefix_ void senf::term::bindings::forwardChar(LineEditor & editor)
00569 {
00570 editor.gotoChar(editor.point()+1);
00571 }
00572
00573 prefix_ void senf::term::bindings::backwardChar(LineEditor & editor)
00574 {
00575 unsigned p (editor.point());
00576 if (p>0)
00577 editor.gotoChar(p-1);
00578 }
00579
00580 prefix_ void senf::term::bindings::accept(LineEditor & editor)
00581 {
00582 editor.accept();
00583 }
00584
00585 prefix_ void senf::term::bindings::acceptWithRepeat(LineEditor & editor)
00586 {
00587 if (editor.text().empty()) {
00588 editor.prevHistory();
00589 editor.forceRedisplay();
00590 }
00591 editor.accept();
00592 }
00593
00594 prefix_ void senf::term::bindings::backwardDeleteChar(LineEditor & editor)
00595 {
00596 unsigned p (editor.point());
00597 if (p>0) {
00598 editor.gotoChar(p-1);
00599 editor.deleteChar();
00600 }
00601 }
00602
00603 prefix_ void senf::term::bindings::deleteChar(LineEditor & editor)
00604 {
00605 editor.deleteChar();
00606 }
00607
00608 prefix_ void senf::term::bindings::beginningOfLine(LineEditor & editor)
00609 {
00610 editor.gotoChar(0u);
00611 }
00612
00613 prefix_ void senf::term::bindings::endOfLine(LineEditor & editor)
00614 {
00615 editor.gotoChar(editor.text().size());
00616 }
00617
00618 prefix_ void senf::term::bindings::deleteToEndOfLine(LineEditor & editor)
00619 {
00620 editor.deleteChar(editor.text().size()-editor.point());
00621 }
00622
00623 prefix_ void senf::term::bindings::restartEdit(LineEditor & editor)
00624 {
00625 editor.newline();
00626 editor.clear();
00627 editor.redisplay();
00628 }
00629
00630 prefix_ void senf::term::bindings::prevHistory(LineEditor & editor)
00631 {
00632 editor.prevHistory();
00633 }
00634
00635 prefix_ void senf::term::bindings::nextHistory(LineEditor & editor)
00636 {
00637 editor.nextHistory();
00638 }
00639
00640 prefix_ void senf::term::bindings::clearScreen(LineEditor & editor)
00641 {
00642 editor.maybeClrScr();
00643 editor.clearLine();
00644 editor.forceRedisplay();
00645 }
00646
00647 prefix_ void senf::term::bindings::complete(LineEditor & editor, Completer completer)
00648 {
00649 typedef std::vector<std::string> Completions;
00650
00651 std::string text (editor.text());
00652 Completions completions;
00653 unsigned b (0);
00654 unsigned e (editor.point());
00655 std::string prefix;
00656 completer(editor, b, e, prefix, completions);
00657 if (completions.empty())
00658 return;
00659 if (e > text.size())
00660 e = text.size();
00661 if (b > e)
00662 b = e;
00663
00664
00665 unsigned commonStart (completions[0].size());
00666 unsigned maxLen (commonStart);
00667 for (Completions::const_iterator i (boost::next(completions.begin()));
00668 i != completions.end(); ++i) {
00669 if (i->size() > maxLen)
00670 maxLen = i->size();
00671 unsigned n (0u);
00672 for (; n < commonStart && n < i->size() && completions[0][n] == (*i)[n]; ++n) ;
00673 commonStart = n;
00674 }
00675
00676
00677 std::string completion (prefix+completions[0].substr(0, commonStart));
00678 bool didComplete (false);
00679 if (text.substr(b, e) != completion) {
00680 text.erase(b, e);
00681 text.insert(b, completion);
00682 didComplete = true;
00683 }
00684
00685
00686 editor.set(text, b+prefix.size()+commonStart);
00687 if (didComplete || completions.size() == 1)
00688 return;
00689
00690
00691 unsigned colWidth (maxLen+2);
00692 unsigned nColumns ((editor.width()-1) / colWidth);
00693 if (nColumns < 1) nColumns = 1;
00694 unsigned nRows ((completions.size()+nColumns-1) / nColumns);
00695 if (nRows > editor.maxAuxDisplayHeight()) {
00696 editor.auxDisplay(0, "(too many completions)");
00697 return;
00698 }
00699 Completions::iterator i (completions.begin());
00700 for (unsigned row (0); row < nRows; ++row) {
00701 std::string line;
00702 for (unsigned column (0); column < nColumns && i != completions.end(); ++column, ++i) {
00703 std::string entry (colWidth, ' ');
00704 std::copy(i->begin(),
00705 i->size() > colWidth-2 ? i->begin()+colWidth-2 : i->end(),
00706 entry.begin());
00707 line += entry;
00708 }
00709 editor.auxDisplay(row, line);
00710 }
00711 }
00712
00713
00714 #undef prefix_
00715
00716
00717
00718
00719
00720
00721
00722
00723
00724
00725
00726