From cb1b03c0db91d85db3545a49f68d63fad0fe137c Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Fri, 22 Nov 2019 21:48:26 +1100 Subject: [PATCH] Add support for markup in info boxes Fixes #2552 --- doc/json_ui.asciidoc | 2 +- doc/pages/changelog.asciidoc | 2 + doc/pages/commands.asciidoc | 4 ++ src/client.cc | 11 ++++- src/client.hh | 7 ++-- src/commands.cc | 12 +++++- src/display_buffer.hh | 8 ++-- src/highlighters.cc | 2 +- src/json_ui.cc | 2 +- src/json_ui.hh | 2 +- src/main.cc | 2 +- src/ncurses_ui.cc | 78 +++++++++++++++++++++--------------- src/ncurses_ui.hh | 7 ++-- src/remote.cc | 8 ++-- src/user_interface.hh | 4 +- src/utils.hh | 12 ++++++ src/window.cc | 2 +- 17 files changed, 110 insertions(+), 55 deletions(-) diff --git a/doc/json_ui.asciidoc b/doc/json_ui.asciidoc index 67ca3c27..e8dcad09 100644 --- a/doc/json_ui.asciidoc +++ b/doc/json_ui.asciidoc @@ -35,7 +35,7 @@ Here are the requests that can be written by the json ui on stdout: - inline: display the menu next to (above or below) the anchor coordinate * menu_select(int selected) * menu_hide() -* info_show(String title, String content, Coord anchor, Face face, String style) +* info_show(Line title, Array content, Coord anchor, Face face, String style) style can be: - prompt: display the info as a prompt info (anchor is ignored) - inline: display the info next to (above or below) the anchor coordinate diff --git a/doc/pages/changelog.asciidoc b/doc/pages/changelog.asciidoc index 3312bc97..25494519 100644 --- a/doc/pages/changelog.asciidoc +++ b/doc/pages/changelog.asciidoc @@ -17,6 +17,8 @@ released versions. * `WrapMarker` face used by `wrap -marker` highlighter +* `info` supports markup with the `-markup` switch + == Kakoune 2019.07.01 * Re-organized bundled script files directory hierarchy. diff --git a/doc/pages/commands.asciidoc b/doc/pages/commands.asciidoc index f9e239e4..f81c3268 100644 --- a/doc/pages/commands.asciidoc +++ b/doc/pages/commands.asciidoc @@ -323,6 +323,10 @@ but not really useful in that context. *-title* ::: set the title of the message box + + *-markup*::: + parse markup in both title (if provided) and text. (See + <>) *try* [catch ]...:: prevent an error in *commands* from aborting the whole command diff --git a/src/client.cc b/src/client.cc index f4a85ccb..c4312aca 100644 --- a/src/client.cc +++ b/src/client.cc @@ -411,7 +411,7 @@ void Client::menu_hide() m_ui_pending &= ~(MenuShow | MenuSelect); } -void Client::info_show(String title, String content, BufferCoord anchor, InfoStyle style) +void Client::info_show(DisplayLine title, DisplayLineList content, BufferCoord anchor, InfoStyle style) { if (m_info.style == InfoStyle::Modal) // We already have a modal info opened, do not touch it. return; @@ -421,6 +421,15 @@ void Client::info_show(String title, String content, BufferCoord anchor, InfoSty m_ui_pending &= ~InfoHide; } +void Client::info_show(StringView title, StringView content, BufferCoord anchor, InfoStyle style) +{ + info_show({title.str(), Face{}}, + content | split('\n') + | transform([](StringView s) { return DisplayLine{s.str(), Face{}}; }) + | gather(), + anchor, style); +} + void Client::info_hide(bool even_modal) { if (not even_modal and m_info.style == InfoStyle::Modal) diff --git a/src/client.hh b/src/client.hh index e9da7588..3fa27627 100644 --- a/src/client.hh +++ b/src/client.hh @@ -45,7 +45,8 @@ public: void menu_select(int selected); void menu_hide(); - void info_show(String title, String content, BufferCoord anchor, InfoStyle style); + void info_show(DisplayLine title, DisplayLineList content, BufferCoord anchor, InfoStyle style); + void info_show(StringView title, StringView content, BufferCoord anchor, InfoStyle style); void info_hide(bool even_modal = false); void print_status(DisplayLine status_line); @@ -119,8 +120,8 @@ private: struct Info { - String title; - String content; + DisplayLine title; + DisplayLineList content; BufferCoord anchor; Optional ui_anchor; InfoStyle style; diff --git a/src/commands.cc b/src/commands.cc index dd105c69..728b4403 100644 --- a/src/commands.cc +++ b/src/commands.cc @@ -2144,6 +2144,7 @@ const CommandDesc info_cmd = { ParameterDesc{ { { "anchor", { true, "set info anchoring ." } }, { "style", { true, "set info style (above, below, menu, modal)" } }, + { "markup", { false, "parse markup" } }, { "title", { true, "set info title" } } }, ParameterDesc::Flags::None, 0, 1 }, @@ -2179,7 +2180,16 @@ const CommandDesc info_cmd = { }).value_or(BufferCoord{}); auto title = parser.get_switch("title").value_or(StringView{}); - context.client().info_show(title.str(), parser[0], pos, style); + if (parser.get_switch("markup")) + context.client().info_show(parse_display_line(title, context.faces()), + parser[0] | split('\n') + | transform([&](StringView s) { + return parse_display_line(s, context.faces()); + }) + | gather(), + pos, style); + else + context.client().info_show(title.str(), parser[0], pos, style); } }; diff --git a/src/display_buffer.hh b/src/display_buffer.hh index 1c8fc53d..01310b26 100644 --- a/src/display_buffer.hh +++ b/src/display_buffer.hh @@ -135,6 +135,7 @@ private: AtomList m_atoms; }; +using DisplayLineList = Vector; class FaceRegistry; String fix_atom_text(StringView str); @@ -143,11 +144,10 @@ DisplayLine parse_display_line(StringView line, const FaceRegistry& faces, const class DisplayBuffer : public UseMemoryDomain { public: - using LineList = Vector; DisplayBuffer() {} - LineList& lines() { return m_lines; } - const LineList& lines() const { return m_lines; } + DisplayLineList& lines() { return m_lines; } + const DisplayLineList& lines() const { return m_lines; } // returns the smallest BufferRange which contains every DisplayAtoms const BufferRange& range() const { return m_range; } @@ -160,7 +160,7 @@ public: size_t timestamp() const { return m_timestamp; } private: - LineList m_lines; + DisplayLineList m_lines; BufferRange m_range; size_t m_timestamp = -1; }; diff --git a/src/highlighters.cc b/src/highlighters.cc index 000befef..a8f4c433 100644 --- a/src/highlighters.cc +++ b/src/highlighters.cc @@ -133,7 +133,7 @@ void apply_highlighter(HighlightContext context, if (begin == end) return; - using LineIterator = DisplayBuffer::LineList::iterator; + using LineIterator = DisplayLineList::iterator; LineIterator first_line; Vector insert_idx; auto line_end = display_buffer.lines().end(); diff --git a/src/json_ui.cc b/src/json_ui.cc index 51fde617..1182c221 100644 --- a/src/json_ui.cc +++ b/src/json_ui.cc @@ -217,7 +217,7 @@ void JsonUI::menu_hide() rpc_call("menu_hide"); } -void JsonUI::info_show(StringView title, StringView content, +void JsonUI::info_show(const DisplayLine& title, const DisplayLineList& content, DisplayCoord anchor, Face face, InfoStyle style) { diff --git a/src/json_ui.hh b/src/json_ui.hh index 5e53ab42..7b1abf44 100644 --- a/src/json_ui.hh +++ b/src/json_ui.hh @@ -35,7 +35,7 @@ public: void menu_select(int selected) override; void menu_hide() override; - void info_show(StringView title, StringView content, + void info_show(const DisplayLine& title, const DisplayLineList& content, DisplayCoord anchor, Face face, InfoStyle style) override; void info_hide() override; diff --git a/src/main.cc b/src/main.cc index a6283b46..8400fb1c 100644 --- a/src/main.cc +++ b/src/main.cc @@ -517,7 +517,7 @@ std::unique_ptr make_ui(UIType ui_type) void menu_select(int) override {} void menu_hide() override {} - void info_show(StringView, StringView, DisplayCoord, Face, InfoStyle) override {} + void info_show(const DisplayLine&, const DisplayLineList&, DisplayCoord, Face, InfoStyle) override {} void info_hide() override {} void draw(const DisplayBuffer&, const Face&, const Face&) override {} diff --git a/src/ncurses_ui.cc b/src/ncurses_ui.cc index cd036b34..4e75d958 100644 --- a/src/ncurses_ui.cc +++ b/src/ncurses_ui.cc @@ -1032,11 +1032,26 @@ static DisplayCoord compute_pos(DisplayCoord anchor, DisplayCoord size, struct InfoBox { DisplayCoord size; - Vector contents; + DisplayLineList contents; }; -InfoBox make_info_box(StringView title, StringView message, ColumnCount max_width, - ConstArrayView assistant) +template +void append_atoms(DisplayLine& line, Args&&... args) +{ + auto append = overload( + [](DisplayLine& line, String str) { + line.push_back(DisplayAtom{std::move(str)}); + }, + [](DisplayLine& line, const DisplayLine& atoms) { + for (auto& atom : atoms) + line.push_back(atom); + }); + + (append(line, args), ...); +} + +InfoBox make_info_box(const DisplayLine& title, const DisplayLineList& content, + ColumnCount max_width, ConstArrayView assistant) { DisplayCoord assistant_size; if (not assistant.empty()) @@ -1048,71 +1063,70 @@ InfoBox make_info_box(StringView title, StringView message, ColumnCount max_widt if (max_bubble_width < 4) return result; - Vector lines = wrap_lines(message, max_bubble_width); + ColumnCount bubble_width = title.length() + 2; + for (auto& line : content) + bubble_width = max(bubble_width, line.length()); - ColumnCount bubble_width = title.column_length() + 2; - for (auto& line : lines) - bubble_width = max(bubble_width, line.column_length()); - - auto line_count = max(assistant_size.line-1, LineCount{(int)lines.size()} + 2); + auto line_count = max(assistant_size.line-1, LineCount{(int)content.size()} + 2); result.size = DisplayCoord{line_count, bubble_width + assistant_size.column + 4}; const auto assistant_top_margin = (line_count - assistant_size.line+1) / 2; for (LineCount i = 0; i < line_count; ++i) { - String line; constexpr Codepoint dash{L'─'}; + DisplayLine line; if (not assistant.empty()) { - if (i >= assistant_top_margin) - line += assistant[(int)min(i - assistant_top_margin, assistant_size.line-1)]; - else - line += assistant[(int)assistant_size.line-1]; + StringView assistant_line = (i >= assistant_top_margin) ? + assistant[(int)min(i - assistant_top_margin, assistant_size.line-1)] + : assistant[(int)assistant_size.line-1]; + + append_atoms(line, assistant_line.str()); } if (i == 0) { - if (title.empty()) - line += "╭─" + String{dash, bubble_width} + "─╮"; + if (title.atoms().empty()) + append_atoms(line, "╭─" + String{dash, bubble_width} + "─╮"); else { - auto dash_count = bubble_width - title.column_length() - 2; + auto dash_count = bubble_width - title.length() - 2; String left{dash, dash_count / 2}; String right{dash, dash_count - dash_count / 2}; - line += "╭─" + left + "┤" + title +"├" + right +"─╮"; + append_atoms(line, "╭─" + left + "┤", title, "├" + right +"─╮"); } } - else if (i < lines.size() + 1) + else if (i < content.size() + 1) { - auto& info_line = lines[(int)i - 1]; - const ColumnCount padding = bubble_width - info_line.column_length(); - line += "│ " + info_line + String{' ', padding} + " │"; + auto& info_line = content[(int)i - 1]; + const ColumnCount padding = bubble_width - info_line.length(); + append_atoms(line, "│ ", info_line, String{' ', padding} + " │"); } - else if (i == lines.size() + 1) - line += "╰─" + String(dash, bubble_width) + "─╯"; + else if (i == content.size() + 1) + append_atoms(line, "╰─" + String(dash, bubble_width) + "─╯"); result.contents.push_back(std::move(line)); } return result; } -InfoBox make_simple_info_box(StringView contents, ColumnCount max_width) +InfoBox make_simple_info_box(const DisplayLineList& content, ColumnCount max_width) { InfoBox info_box{}; - for (auto& line : wrap_lines(contents, max_width)) + for (auto& line : content) { ++info_box.size.line; - info_box.size.column = std::max(line.column_length(), info_box.size.column); - info_box.contents.push_back(line.str()); + info_box.size.column = std::max(line.length(), info_box.size.column); + info_box.contents.push_back(line); } return info_box; } -void NCursesUI::info_show(StringView title, StringView content, +void NCursesUI::info_show(const DisplayLine& title, const DisplayLineList& content, DisplayCoord anchor, Face face, InfoStyle style) { info_hide(); - m_info.title = title.str(); - m_info.content = content.str(); + m_info.title = title; + m_info.content = content; m_info.anchor = anchor; m_info.face = face; m_info.style = style; @@ -1170,7 +1184,7 @@ void NCursesUI::info_show(StringView title, StringView content, for (auto line = 0_line; line < info_box.size.line; ++line) { m_info.move_cursor(line); - m_info.draw(m_palette, DisplayAtom(info_box.contents[(int)line]), face); + m_info.draw(m_palette, info_box.contents[(int)line].atoms(), face); } m_dirty = true; } diff --git a/src/ncurses_ui.hh b/src/ncurses_ui.hh index d10470ec..7d51cdcb 100644 --- a/src/ncurses_ui.hh +++ b/src/ncurses_ui.hh @@ -3,6 +3,7 @@ #include "array_view.hh" #include "coord.hh" +#include "display_buffer.hh" #include "event_manager.hh" #include "face.hh" #include "hash_map.hh" @@ -44,7 +45,7 @@ public: void menu_select(int selected) override; void menu_hide() override; - void info_show(StringView title, StringView content, + void info_show(const DisplayLine& title, const DisplayLineList& content, DisplayCoord anchor, Face face, InfoStyle style) override; void info_hide() override; @@ -135,8 +136,8 @@ private: struct Info : Window { - String title; - String content; + DisplayLine title; + DisplayLineList content; Face face; DisplayCoord anchor; InfoStyle style; diff --git a/src/remote.cc b/src/remote.cc index 8d98533b..6b03edbd 100644 --- a/src/remote.cc +++ b/src/remote.cc @@ -356,7 +356,7 @@ public: void menu_select(int selected) override; void menu_hide() override; - void info_show(StringView title, StringView content, + void info_show(const DisplayLine& title, const DisplayLineList& content, DisplayCoord anchor, Face face, InfoStyle style) override; void info_hide() override; @@ -483,7 +483,7 @@ void RemoteUI::menu_hide() send_message(MessageType::MenuHide); } -void RemoteUI::info_show(StringView title, StringView content, +void RemoteUI::info_show(const DisplayLine& title, const DisplayLineList& content, DisplayCoord anchor, Face face, InfoStyle style) { @@ -643,8 +643,8 @@ RemoteClient::RemoteClient(StringView session, StringView name, std::unique_ptr< break; case MessageType::InfoShow: { - auto title = reader.read(); - auto content = reader.read(); + auto title = reader.read(); + auto content = reader.read(); auto anchor = reader.read(); auto face = reader.read(); auto style = reader.read(); diff --git a/src/user_interface.hh b/src/user_interface.hh index b216cbba..5bab3e89 100644 --- a/src/user_interface.hh +++ b/src/user_interface.hh @@ -12,6 +12,7 @@ namespace Kakoune class String; class DisplayBuffer; class DisplayLine; +using DisplayLineList = Vector; struct DisplayCoord; struct Face; struct Key; @@ -56,7 +57,8 @@ public: virtual void menu_select(int selected) = 0; virtual void menu_hide() = 0; - virtual void info_show(StringView title, StringView content, + virtual void info_show(const DisplayLine& title, + const DisplayLineList& content, DisplayCoord anchor, Face face, InfoStyle style) = 0; virtual void info_hide() = 0; diff --git a/src/utils.hh b/src/utils.hh index f9c53509..0979aebe 100644 --- a/src/utils.hh +++ b/src/utils.hh @@ -186,6 +186,18 @@ private: Invoker* m_invoker; }; +template +struct Overload : Funcs... +{ + using Funcs::operator()...; +}; + +template +auto overload(Funcs&&... funcs) +{ + return Overload...>{std::forward(funcs)...}; +} + } #endif // utils_hh_INCLUDED diff --git a/src/window.cc b/src/window.cc index 0ef445ca..cbc4deab 100644 --- a/src/window.cc +++ b/src/window.cc @@ -135,7 +135,7 @@ const DisplayBuffer& Window::update_display_buffer(const Context& context) } } - DisplayBuffer::LineList& lines = m_display_buffer.lines(); + DisplayLineList& lines = m_display_buffer.lines(); m_display_buffer.set_timestamp(buffer().timestamp()); lines.clear();