From 6b91660c5c0414e2be4e3d6515dc53eefeafbffb Mon Sep 17 00:00:00 2001 From: Yaraslau Tamashevich Date: Thu, 9 Jan 2025 23:18:49 +0100 Subject: [PATCH] Allow naming of the tabs --- metainfo.xml | 1 + src/contour/Actions.cpp | 1 + src/contour/Actions.h | 8 +- src/contour/Config.h | 3 +- src/contour/ConfigDocumentation.h | 2 + src/contour/TerminalSession.cpp | 44 ++++++++++ src/contour/TerminalSession.h | 9 ++ src/contour/TerminalSessionManager.h | 14 ++- src/vtbackend/Settings.h | 8 ++ src/vtbackend/StatusLineBuilder.cpp | 14 ++- src/vtbackend/StatusLineBuilder.h | 1 + src/vtbackend/Terminal.cpp | 20 +++++ src/vtbackend/Terminal.h | 36 +++++++- src/vtbackend/ViCommands.cpp | 22 +++++ src/vtbackend/ViCommands.h | 6 ++ src/vtbackend/ViInputHandler.cpp | 125 ++++++++++++++++++++------- src/vtbackend/ViInputHandler.h | 37 ++++++-- 17 files changed, 303 insertions(+), 48 deletions(-) diff --git a/metainfo.xml b/metainfo.xml index d3246cac9a..50b03ada8c 100644 --- a/metainfo.xml +++ b/metainfo.xml @@ -113,6 +113,7 @@
  • Adds `MoveTabTo` action to move tabs to a specific position (#1695)
  • Adds handling of control codes for Ctrl+5|6|7|8 (#1701)
  • Adds CenterCursor (`zz`) vi motion
  • +
  • Adds ability to name tabs (#1690)
  • diff --git a/src/contour/Actions.cpp b/src/contour/Actions.cpp index 7fb44a673e..c3e2890535 100644 --- a/src/contour/Actions.cpp +++ b/src/contour/Actions.cpp @@ -88,6 +88,7 @@ optional fromString(string const& name) mapAction("SwitchToPreviousTab"), mapAction("SwitchToTabLeft"), mapAction("SwitchToTabRight"), + mapAction("SetTabName"), }; auto const lowerCaseName = toLower(name); diff --git a/src/contour/Actions.h b/src/contour/Actions.h index 14ccf1c397..be7e7b0d7c 100644 --- a/src/contour/Actions.h +++ b/src/contour/Actions.h @@ -88,6 +88,7 @@ struct SwitchToTab{ int position; }; struct SwitchToPreviousTab{}; struct SwitchToTabLeft{}; struct SwitchToTabRight{}; +struct SetTabName{}; // clang-format on using Action = std::variant; + SwitchToTabRight, + SetTabName>; std::optional fromString(std::string const& name); @@ -276,6 +278,7 @@ namespace documentation constexpr inline std::string_view SwitchToPreviousTab { "Switch to the previously focused tab" }; constexpr inline std::string_view SwitchToTabLeft { "Switch to tab to the left" }; constexpr inline std::string_view SwitchToTabRight { "Switch to tab to the right" }; + constexpr inline std::string_view SetTabName { "Set the name of the current tab" }; } // namespace documentation inline auto getDocumentation() @@ -341,6 +344,7 @@ inline auto getDocumentation() std::tuple { Action { SwitchToPreviousTab {} }, documentation::SwitchToPreviousTab }, std::tuple { Action { SwitchToTabLeft {} }, documentation::SwitchToTabLeft }, std::tuple { Action { SwitchToTabRight {} }, documentation::SwitchToTabRight }, + std::tuple { Action { SetTabName {} }, documentation::SetTabName }, }; } @@ -416,6 +420,7 @@ DECLARE_ACTION_FMT(MoveTabToRight) DECLARE_ACTION_FMT(SwitchToPreviousTab) DECLARE_ACTION_FMT(SwitchToTabLeft) DECLARE_ACTION_FMT(SwitchToTabRight) +DECLARE_ACTION_FMT(SetTabName) // }}} #undef DECLARE_ACTION_FMT @@ -506,6 +511,7 @@ struct std::formatter: std::formatter HANDLE_ACTION(SwitchToPreviousTab); HANDLE_ACTION(SwitchToTabLeft); HANDLE_ACTION(SwitchToTabRight); + HANDLE_ACTION(SetTabName); if (std::holds_alternative(_action)) { const auto action = std::get(_action); diff --git a/src/contour/Config.h b/src/contour/Config.h index 7447c5e867..78cc07f13f 100644 --- a/src/contour/Config.h +++ b/src/contour/Config.h @@ -180,11 +180,10 @@ struct MouseConfig struct IndicatorConfig { std::string left { " {InputMode:Bold,Color=#FFFF00}" - "{Tabs:ActiveColor=#FFFF00,Left= │ }" "{SearchPrompt:Left= │ }" "{TraceMode:Bold,Color=#FFFF00,Left= │ }" "{ProtectedMode:Bold,Left= │ }" }; - std::string middle { "{Title:Left= « ,Right= » }" }; + std::string middle { "{Tabs:ActiveColor=#FFFF00}" }; std::string right { "{HistoryLineCount:Faint,Color=#c0c0c0} │ {Clock:Bold}" }; }; diff --git a/src/contour/ConfigDocumentation.h b/src/contour/ConfigDocumentation.h index c6e2a2a535..edd52aa2e6 100644 --- a/src/contour/ConfigDocumentation.h +++ b/src/contour/ConfigDocumentation.h @@ -813,6 +813,8 @@ constexpr StringLiteral InputMappingsConfig { "p.\n" "{comment} - WriteScreen Writes VT sequence in `chars` member to the screen (bypassing the " "application).\n" + "{comment} - SetTabName Ask the user to assign a name to the active tab.\n" + "\n" "input_mapping:\n" }; diff --git a/src/contour/TerminalSession.cpp b/src/contour/TerminalSession.cpp index b491048608..2c5869abc9 100644 --- a/src/contour/TerminalSession.cpp +++ b/src/contour/TerminalSession.cpp @@ -149,6 +149,43 @@ namespace settings.indicatorStatusLine.left = profile.statusLine.value().indicator.left; settings.indicatorStatusLine.middle = profile.statusLine.value().indicator.middle; settings.indicatorStatusLine.right = profile.statusLine.value().indicator.right; + settings.tabNamingMode = [&]() { + // try to find Tab section in one of the status line segments + + std::string segment; + if (profile.statusLine.value().indicator.left.find("Tabs") != std::string::npos) + { + segment = profile.statusLine.value().indicator.left; + } + else if (profile.statusLine.value().indicator.middle.find("Tabs") != std::string::npos) + { + segment = profile.statusLine.value().indicator.middle; + } + else if (profile.statusLine.value().indicator.right.find("Tabs") != std::string::npos) + { + segment = profile.statusLine.value().indicator.right; + } + + // check if indexing is defined + if (segment.find("Indexing=") != std::string::npos) + { + // cut the string after indexing= + std::string indexing = segment.substr(segment.find("Indexing=") + 9); + // cut right part of the string + indexing = indexing.substr(0, indexing.find(',')); + indexing = indexing.substr(0, indexing.find('}')); + + std::ranges::transform( + indexing, indexing.begin(), [](unsigned char c) { return std::tolower(c); }); + + if (indexing == "title") + { + return vtbackend::TabsNamingMode::Title; + } + } + return vtbackend::TabsNamingMode::Indexing; + }(); + settings.syncWindowTitleWithHostWritableStatusDisplay = profile.statusLine.value().syncWindowTitleWithHostWritableStatusDisplay; if (auto const* p = preferredColorPalette(profile.colors.value(), colorPreference)) @@ -262,6 +299,7 @@ void TerminalSession::attachDisplay(display::TerminalDisplay& newDisplay) void TerminalSession::scheduleRedraw() { _terminal.markScreenDirty(); + _manager->update(); if (_display) _display->scheduleRedraw(); } @@ -1488,6 +1526,12 @@ bool TerminalSession::operator()(actions::SwitchToTabRight) return true; } +bool TerminalSession::operator()(actions::SetTabName) +{ + terminal().requestTabName(); + return true; +} + // }}} // {{{ implementation helpers void TerminalSession::setDefaultCursor() diff --git a/src/contour/TerminalSession.h b/src/contour/TerminalSession.h index ddc808a5fd..9be4b04079 100644 --- a/src/contour/TerminalSession.h +++ b/src/contour/TerminalSession.h @@ -220,6 +220,14 @@ class TerminalSession: public QAbstractItemModel, public vtbackend::Terminal::Ev ~TerminalSession() override; int id() const noexcept { return _id; } + std::optional name() const noexcept + { + if (terminal().tabName()) + return terminal().tabName(); + if (terminal().getTabsNamingMode() == vtbackend::TabsNamingMode::Title) + return terminal().windowTitle(); + return std::nullopt; + } /// Starts the VT background thread. void start(); @@ -364,6 +372,7 @@ class TerminalSession: public QAbstractItemModel, public vtbackend::Terminal::Ev bool operator()(actions::SwitchToPreviousTab); bool operator()(actions::SwitchToTabLeft); bool operator()(actions::SwitchToTabRight); + bool operator()(actions::SetTabName); void scheduleRedraw(); diff --git a/src/contour/TerminalSessionManager.h b/src/contour/TerminalSessionManager.h index cfc54b64c0..09163920bf 100644 --- a/src/contour/TerminalSessionManager.h +++ b/src/contour/TerminalSessionManager.h @@ -27,7 +27,6 @@ class TerminalSessionManager: public QAbstractListModel TerminalSessionManager(ContourGuiApp& app); contour::TerminalSession* createSessionInBackground(); - contour::TerminalSession* activateSession(TerminalSession* session, bool isNewSession = false); Q_INVOKABLE contour::TerminalSession* createSession(); @@ -54,7 +53,10 @@ class TerminalSessionManager: public QAbstractListModel display::TerminalDisplay* display = nullptr; TerminalSession* getSession() { return _sessions[0]; } + void update() { updateStatusLine(); } + private: + contour::TerminalSession* activateSession(TerminalSession* session, bool isNewSession = false); std::unique_ptr createPty(std::optional cwd); [[nodiscard]] std::optional getSessionIndexOf(TerminalSession* session) const noexcept @@ -73,9 +75,15 @@ class TerminalSessionManager: public QAbstractListModel { if (!_activeSession) return; - _activeSession->terminal().setGuiTabInfoForStatusLine(vtbackend::TabsInfo { - .tabCount = _sessions.size(), + .tabs = std::ranges::transform_view(_sessions, + [](auto* session) { + return vtbackend::TabsInfo::Tab { + .name = session->name(), + .color = vtbackend::RGBColor { 0, 0, 0 }, + }; + }) + | ranges::to(), .activeTabPosition = 1 + getSessionIndexOf(_activeSession).value_or(0), }); } diff --git a/src/vtbackend/Settings.h b/src/vtbackend/Settings.h index 74be100ea5..1fa4dea995 100644 --- a/src/vtbackend/Settings.h +++ b/src/vtbackend/Settings.h @@ -24,6 +24,12 @@ struct RefreshInterval explicit RefreshInterval(RefreshRate rate): value { static_cast(1000.0 / rate.value) } {} }; +enum class TabsNamingMode : uint8_t +{ + Indexing, + Title +}; + /// Terminal settings, enabling hardware reset to be easier implemented. struct Settings { @@ -91,6 +97,8 @@ struct Settings bool fromSearchIntoInsertMode = true; bool isInsertAfterYank = false; + TabsNamingMode tabNamingMode = TabsNamingMode::Indexing; + // TODO: we could configure also the number of lines of the host writable statusline and indicator // statusline. }; diff --git a/src/vtbackend/StatusLineBuilder.cpp b/src/vtbackend/StatusLineBuilder.cpp index 2774375421..0044165894 100644 --- a/src/vtbackend/StatusLineBuilder.cpp +++ b/src/vtbackend/StatusLineBuilder.cpp @@ -15,6 +15,7 @@ #include #include #include +#include using namespace std::string_view_literals; @@ -156,6 +157,7 @@ std::optional makeStatusLineItem( styles, activeColor, activeBackground, + std::nullopt, // separator }; } @@ -404,6 +406,9 @@ struct VTSerializer return std::format("Search: {}█", unicode::convert_to(std::u32string_view(vt.search().pattern))); + if (vt.inputHandler().isEditingPrompt()) + return std::format("{}{}█", vt.prompt().prompt, vt.prompt().text); + return {}; } @@ -443,10 +448,10 @@ struct VTSerializer auto const tabsInfo = vt.guiTabsInfoForStatusLine(); std::string fragment; - for (const auto position: std::views::iota(1u, tabsInfo.tabCount + 1)) + for (const auto position: std::views::iota(1u, tabsInfo.tabs.size() + 1)) { if (!fragment.empty()) - fragment += ' '; + fragment += tabs.separator.value_or("|"); auto const isActivePosition = position == tabsInfo.activeTabPosition; auto const activePositionStylized = @@ -459,7 +464,10 @@ struct VTSerializer fragment += makeBackgroundColor(tabs.activeBackground); } - fragment += std::to_string(position); + if (tabsInfo.tabs[position - 1].name) + fragment += tabsInfo.tabs[position - 1].name.value(); + else + fragment += std::to_string(position); if (activePositionStylized) fragment += SGRRESTORE(); diff --git a/src/vtbackend/StatusLineBuilder.h b/src/vtbackend/StatusLineBuilder.h index fe64882920..5f6f76d266 100644 --- a/src/vtbackend/StatusLineBuilder.h +++ b/src/vtbackend/StatusLineBuilder.h @@ -43,6 +43,7 @@ namespace StatusLineDefinitions { std::optional activeColor; std::optional activeBackground; + std::optional separator; }; using Item = std::variant< diff --git a/src/vtbackend/Terminal.cpp b/src/vtbackend/Terminal.cpp index d9fd970c90..ef87e7ee8a 100644 --- a/src/vtbackend/Terminal.cpp +++ b/src/vtbackend/Terminal.cpp @@ -1567,6 +1567,16 @@ std::string const& Terminal::windowTitle() const noexcept return _windowTitle; } +void Terminal::requestTabName() +{ + inputHandler().setTabName([&](std::string name) { _tabName = std::move(name); }); +} + +std::optional Terminal::tabName() const noexcept +{ + return _tabName; +} + void Terminal::saveWindowTitle() { _savedWindowTitles.push(_windowTitle); @@ -2195,6 +2205,16 @@ bool Terminal::setNewSearchTerm(std::u32string text, bool initiatedByDoubleClick return true; } +void Terminal::setPrompt(std::string prompt) +{ + _prompt.prompt = std::move(prompt); +} + +void Terminal::setPromptText(std::string text) +{ + _prompt.text = std::move(text); +} + optional Terminal::searchReverse(u32string text, CellLocation searchPosition) { if (!setNewSearchTerm(std::move(text), false)) diff --git a/src/vtbackend/Terminal.h b/src/vtbackend/Terminal.h index c2676b7b56..9fe5bf2df3 100644 --- a/src/vtbackend/Terminal.h +++ b/src/vtbackend/Terminal.h @@ -42,6 +42,7 @@ #include #include #include +#include namespace vtbackend { @@ -143,6 +144,12 @@ struct Search bool initiatedByDoubleClick = false; }; +struct Prompt +{ + std::string prompt; + std::string text; +}; + // Mandates what execution mode the terminal will take to process VT sequences. // enum class ExecutionMode : uint8_t @@ -209,7 +216,13 @@ class TraceHandler: public SequenceHandler struct TabsInfo { - size_t tabCount = 1; + struct Tab + { + std::optional name; + Color color; + }; + + std::vector tabs; size_t activeTabPosition = 1; }; @@ -246,6 +259,7 @@ class Terminal virtual void requestWindowResize(Width, Height) {} virtual void requestShowHostWritableStatusLine() {} virtual void setWindowTitle(std::string_view /*title*/) {} + virtual void setTabName(std::string_view /*title*/) {} virtual void setTerminalProfile(std::string const& /*configProfileName*/) {} virtual void discardImage(Image const&) {} virtual void inputModeChanged(ViMode /*mode*/) {} @@ -276,6 +290,7 @@ class Terminal void requestWindowResize(Width, Height) override {} void requestShowHostWritableStatusLine() override {} void setWindowTitle(std::string_view /*title*/) override {} + void setTabName(std::string_view /*title*/) override {} void setTerminalProfile(std::string const& /*configProfileName*/) override {} void discardImage(Image const&) override {} void inputModeChanged(ViMode /*mode*/) override {} @@ -357,7 +372,7 @@ class Terminal [[nodiscard]] CellLocation clampToScreen(CellLocation coord) const noexcept { - return { clampedLine(coord.line), clampedColumn(coord.column) }; + return { .line = clampedLine(coord.line), .column = clampedColumn(coord.column) }; } // Tests if given coordinate is within the visible screen area. @@ -797,10 +812,14 @@ class Terminal void setMouseTransport(MouseTransport transport); void setMouseWheelMode(InputGenerator::MouseWheelMode mode); void setWindowTitle(std::string_view title); + void setTabName(std::string_view title); [[nodiscard]] std::string const& windowTitle() const noexcept; + [[nodiscard]] std::optional tabName() const noexcept; [[nodiscard]] bool focused() const noexcept { return _focused; } [[nodiscard]] Search& search() noexcept { return _search; } [[nodiscard]] Search const& search() const noexcept { return _search; } + [[nodiscard]] Prompt& prompt() noexcept { return _prompt; } + [[nodiscard]] Prompt const& prompt() const noexcept { return _prompt; } void saveWindowTitle(); void restoreWindowTitle(); void setTerminalProfile(std::string const& configProfileName); @@ -882,6 +901,9 @@ class Terminal bool setNewSearchTerm(std::u32string text, bool initiatedByDoubleClick); void clearSearch(); + void setPrompt(std::string prompt); + void setPromptText(std::string text); + // Tests if the grid cell at the given location does contain a word delimiter. [[nodiscard]] bool wordDelimited(CellLocation position) const noexcept; [[nodiscard]] bool wordDelimited(CellLocation position, @@ -966,7 +988,10 @@ class Terminal void resetStatusLineDefinition(); TabsInfo guiTabsInfoForStatusLine() const noexcept { return _guiTabInfoForStatusLine; } - void setGuiTabInfoForStatusLine(TabsInfo info) { _guiTabInfoForStatusLine = info; } + void setGuiTabInfoForStatusLine(TabsInfo&& info) { _guiTabInfoForStatusLine = std::move(info); } + + TabsNamingMode getTabsNamingMode() const noexcept { return _settings.tabNamingMode; } + void requestTabName(); private: void mainLoop(); @@ -1080,7 +1105,9 @@ class Terminal Viewport _viewport; StatusLineDefinition _indicatorStatusLineDefinition; + // {{{ tabs info TabsInfo _guiTabInfoForStatusLine; + // }}} // {{{ selection states std::unique_ptr _selection; @@ -1147,6 +1174,7 @@ class Terminal ActiveStatusDisplay _activeStatusDisplay = ActiveStatusDisplay::Main; Search _search; + Prompt _prompt; CursorDisplay _cursorDisplay = CursorDisplay::Steady; CursorShape _cursorShape = CursorShape::Block; @@ -1165,6 +1193,8 @@ class Terminal std::string _windowTitle {}; std::stack _savedWindowTitles {}; + std::optional _tabName {}; + struct ModeDependantSequenceHandler { Terminal& terminal; diff --git a/src/vtbackend/ViCommands.cpp b/src/vtbackend/ViCommands.cpp index 61639d25df..0540980c1e 100644 --- a/src/vtbackend/ViCommands.cpp +++ b/src/vtbackend/ViCommands.cpp @@ -192,6 +192,28 @@ void ViCommands::searchCancel() _terminal->screenUpdated(); } +void ViCommands::promptStart(std::string const& query) +{ + _terminal->setPrompt(query); + _terminal->screenUpdated(); +} + +void ViCommands::promptDone() +{ + _terminal->screenUpdated(); +} + +void ViCommands::promptCancel() +{ + _terminal->screenUpdated(); +} + +void ViCommands::updatePromptText(std::string const& text) +{ + _terminal->setPromptText(text); + _terminal->screenUpdated(); +} + bool ViCommands::jumpToNextMatch(unsigned count) { for (unsigned i = 0; i < count; ++i) diff --git a/src/vtbackend/ViCommands.h b/src/vtbackend/ViCommands.h index 92ff55d31c..1398a107ba 100644 --- a/src/vtbackend/ViCommands.h +++ b/src/vtbackend/ViCommands.h @@ -46,6 +46,12 @@ class ViCommands: public ViInputHandler::Executor void searchDone() override; void searchCancel() override; void updateSearchTerm(std::u32string const& text) override; + + void promptStart(std::string const& query) override; + void promptDone() override; + void promptCancel() override; + void updatePromptText(std::string const& text) override; + bool jumpToNextMatch(unsigned count); bool jumpToPreviousMatch(unsigned count); diff --git a/src/vtbackend/ViInputHandler.cpp b/src/vtbackend/ViInputHandler.cpp index d7300d70f4..63a90b7323 100644 --- a/src/vtbackend/ViInputHandler.cpp +++ b/src/vtbackend/ViInputHandler.cpp @@ -337,7 +337,20 @@ Handled ViInputHandler::sendKeyPressEvent(Key key, Modifiers modifiers, Keyboard if (eventType == KeyboardEventType::Release) return Handled { true }; - if (_searchEditMode != SearchEditMode::Disabled) + if (_promptEditMode != PromptMode::Disabled) + { + // TODO: support cursor movements. + switch (key) + { + case Key::Backspace: return handlePromptEditor('\x08', modifiers); + case Key::Enter: return handlePromptEditor('\x0D', modifiers); + case Key::Escape: return handlePromptEditor('\x1B', modifiers); + default: break; + } + return Handled { true }; + } + + if (_searchEditMode != PromptMode::Disabled) { // TODO: support cursor movements. switch (key) @@ -432,61 +445,110 @@ void ViInputHandler::startSearchExternally() _executor->searchStart(); if (_viMode != ViMode::Insert) - _searchEditMode = SearchEditMode::Enabled; + _searchEditMode = PromptMode::Enabled; else { - _searchEditMode = SearchEditMode::ExternallyEnabled; + _searchEditMode = PromptMode::ExternallyEnabled; setMode(ViMode::Normal); // ^^^ So that we can see the statusline (which contains the search edit field), // AND it's weird to be in insert mode while typing in the search term anyways. } } -Handled ViInputHandler::handleSearchEditor(char32_t ch, Modifiers modifiers) +auto handleEditor(char32_t ch, + Modifiers modifiers, + auto& where, + PromptMode& promptEditMode, + auto& settings, + auto setViMode, + auto cancel, + auto done, + auto update) { - assert(_searchEditMode != SearchEditMode::Disabled); - switch (InputMatch { .modifiers = modifiers, .ch = ch }) { - case '\x1B'_key: - _searchTerm.clear(); - if (_searchEditMode == SearchEditMode::ExternallyEnabled) - setMode(ViMode::Insert); - _searchEditMode = SearchEditMode::Disabled; - _executor->searchCancel(); - _executor->updateSearchTerm(_searchTerm); - break; - case '\x0D'_key: - if (_settings.fromSearchIntoInsertMode && _searchEditMode == SearchEditMode::ExternallyEnabled) - setMode(ViMode::Insert); - _searchEditMode = SearchEditMode::Disabled; - _executor->searchDone(); - _executor->updateSearchTerm(_searchTerm); - break; + case '\x1B'_key: { + where.clear(); + if (promptEditMode == PromptMode::ExternallyEnabled) + setViMode(ViMode::Insert); + promptEditMode = PromptMode::Enabled; + cancel(); + update(where); + } + break; + case '\x0D'_key: { + if (settings.fromSearchIntoInsertMode && promptEditMode == PromptMode::ExternallyEnabled) + setViMode(ViMode::Insert); + promptEditMode = PromptMode::Disabled; + done(); + } + break; case '\x08'_key: case '\x7F'_key: - if (!_searchTerm.empty()) - _searchTerm.resize(_searchTerm.size() - 1); - _executor->updateSearchTerm(_searchTerm); + if (!where.empty()) + where.resize(where.size() - 1); + update(where); break; case Modifier::Control | 'L': case Modifier::Control | 'U': - _searchTerm.clear(); - _executor->updateSearchTerm(_searchTerm); + where.clear(); + update(where); break; case Modifier::Control | 'A': // TODO: move cursor to BOL case Modifier::Control | 'E': // TODO: move cursor to EOL default: if (ch >= 0x20 && modifiers.without(Modifier::Shift).none()) { - _searchTerm += ch; - _executor->updateSearchTerm(_searchTerm); + where += ch; + update(where); } else errorLog()("ViInputHandler: Receiving control code {}+0x{:02X} in search mode. Ignoring.", modifiers, (unsigned) ch); } +} + +Handled ViInputHandler::handleSearchEditor(char32_t ch, Modifiers modifiers) +{ + assert(_searchEditMode != PromptMode::Disabled); + + handleEditor( + ch, + modifiers, + _searchTerm, + _searchEditMode, + _settings, + [&](auto mode) { setMode(mode); }, + [&]() { _executor->searchCancel(); }, + [&]() { _executor->searchDone(); }, + [&](const auto& val) { _executor->updateSearchTerm(val); }); + + return Handled { true }; +} + +Handled ViInputHandler::handlePromptEditor(char32_t ch, Modifiers modifiers) +{ + assert(_promptEditMode != PromptMode::Disabled); + + handleEditor( + ch, + modifiers, + _promptText, + _promptEditMode, + _settings, + [&](auto mode) { setMode(mode); }, + [&]() { _executor->promptCancel(); }, + [&]() { + _executor->promptDone(); + if (_setTabNameCallback) + { + _setTabNameCallback.value()(_promptText); + setMode(ViMode::Insert); + _setTabNameCallback = std::nullopt; + } + }, + [&](const auto& val) { _executor->updatePromptText(val); }); return Handled { true }; } @@ -496,9 +558,12 @@ Handled ViInputHandler::sendCharPressEvent(char32_t ch, Modifiers modifiers, Key if (eventType == KeyboardEventType::Release) return Handled { true }; - if (_searchEditMode != SearchEditMode::Disabled) + if (_searchEditMode != PromptMode::Disabled) return handleSearchEditor(ch, modifiers); + if (_promptEditMode != PromptMode::Disabled) + return handlePromptEditor(ch, modifiers); + if (_viMode == ViMode::Insert) return Handled { false }; @@ -568,7 +633,7 @@ bool ViInputHandler::parseCount(char32_t ch, Modifiers modifiers) void ViInputHandler::startSearch() { - _searchEditMode = SearchEditMode::Enabled; + _searchEditMode = PromptMode::Enabled; _executor->searchStart(); } diff --git a/src/vtbackend/ViInputHandler.h b/src/vtbackend/ViInputHandler.h index 7abb3f23f0..e93bd49927 100644 --- a/src/vtbackend/ViInputHandler.h +++ b/src/vtbackend/ViInputHandler.h @@ -144,7 +144,7 @@ struct RectangularHighlight { CellLocation from; CellLocation to; }; using HighlightRange = std::variant; -enum class SearchEditMode : uint8_t +enum class PromptMode : uint8_t { Disabled, Enabled, @@ -177,6 +177,11 @@ class ViInputHandler: public InputHandler virtual void searchCancel() = 0; virtual void updateSearchTerm(std::u32string const& text) = 0; + virtual void promptStart(std::string const& query) = 0; + virtual void promptDone() = 0; + virtual void promptCancel() = 0; + virtual void updatePromptText(std::string const& text) = 0; + virtual void scrollViewport(ScrollOffset delta) = 0; // Starts searching for the word under the cursor position in reverse order. @@ -212,13 +217,29 @@ class ViInputHandler: public InputHandler crispy::unreachable(); } - [[nodiscard]] bool isEditingSearch() const noexcept - { - return _searchEditMode != SearchEditMode::Disabled; - } + [[nodiscard]] bool isEditingSearch() const noexcept { return _searchEditMode != PromptMode::Disabled; } + [[nodiscard]] bool isEditingPrompt() const noexcept { return _promptEditMode != PromptMode::Disabled; } void startSearchExternally(); + void setTabName(auto&& callback) + { + _promptText.clear(); + _executor->promptStart("Tab name: "); + + if (_viMode != ViMode::Insert) + _promptEditMode = PromptMode::Enabled; + else + { + _promptEditMode = PromptMode::ExternallyEnabled; + setMode(ViMode::Normal); + // ^^^ So that we can see the statusline (which contains the search edit field), + // AND it's weird to be in insert mode while typing in the prompt term anyways. + } + + _setTabNameCallback = callback; + } + void setSearchModeSwitch(bool enabled); void clearSearch(); @@ -250,14 +271,17 @@ class ViInputHandler: public InputHandler bool parseCount(char32_t ch, Modifiers modifiers); bool parseTextObject(char32_t ch, Modifiers modifiers); Handled handleSearchEditor(char32_t ch, Modifiers modifiers); + Handled handlePromptEditor(char32_t ch, Modifiers modifiers); Handled handleModeSwitches(char32_t ch, Modifiers modifiers); void startSearch(); ViMode _viMode = ViMode::Normal; - SearchEditMode _searchEditMode = SearchEditMode::Disabled; + PromptMode _searchEditMode = PromptMode::Disabled; + PromptMode _promptEditMode = PromptMode::Disabled; bool _searchExternallyActivated = false; std::u32string _searchTerm; + std::string _promptText; std::string _pendingInput; CommandHandlerMap _normalMode; @@ -265,6 +289,7 @@ class ViInputHandler: public InputHandler unsigned _count = 0; char32_t _lastChar = 0; gsl::not_null _executor; + std::optional> _setTabNameCallback { std::nullopt }; }; } // namespace vtbackend