Add support for markup in info boxes

Fixes #2552
This commit is contained in:
Maxime Coste 2019-11-22 21:48:26 +11:00
parent 22d9ffa63a
commit cb1b03c0db
17 changed files with 110 additions and 55 deletions

View File

@ -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 - inline: display the menu next to (above or below) the anchor coordinate
* menu_select(int selected) * menu_select(int selected)
* menu_hide() * menu_hide()
* info_show(String title, String content, Coord anchor, Face face, String style) * info_show(Line title, Array<Line> content, Coord anchor, Face face, String style)
style can be: style can be:
- prompt: display the info as a prompt info (anchor is ignored) - prompt: display the info as a prompt info (anchor is ignored)
- inline: display the info next to (above or below) the anchor coordinate - inline: display the info next to (above or below) the anchor coordinate

View File

@ -17,6 +17,8 @@ released versions.
* `WrapMarker` face used by `wrap -marker` highlighter * `WrapMarker` face used by `wrap -marker` highlighter
* `info` supports markup with the `-markup` switch
== Kakoune 2019.07.01 == Kakoune 2019.07.01
* Re-organized bundled script files directory hierarchy. * Re-organized bundled script files directory hierarchy.

View File

@ -324,6 +324,10 @@ but not really useful in that context.
*-title* <text>::: *-title* <text>:::
set the title of the message box set the title of the message box
*-markup*:::
parse markup in both title (if provided) and text. (See
<<faces#markup-strings,`:doc faces markup-strings`>>)
*try* <commands> [catch <on_error_commands>]...:: *try* <commands> [catch <on_error_commands>]...::
prevent an error in *commands* from aborting the whole command prevent an error in *commands* from aborting the whole command
execution, execute *on_error_commands* instead. If nothing is to be execution, execute *on_error_commands* instead. If nothing is to be

View File

@ -411,7 +411,7 @@ void Client::menu_hide()
m_ui_pending &= ~(MenuShow | MenuSelect); 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. if (m_info.style == InfoStyle::Modal) // We already have a modal info opened, do not touch it.
return; return;
@ -421,6 +421,15 @@ void Client::info_show(String title, String content, BufferCoord anchor, InfoSty
m_ui_pending &= ~InfoHide; m_ui_pending &= ~InfoHide;
} }
void Client::info_show(StringView title, StringView content, BufferCoord anchor, InfoStyle style)
{
info_show({title.str(), Face{}},
content | split<StringView>('\n')
| transform([](StringView s) { return DisplayLine{s.str(), Face{}}; })
| gather<DisplayLineList>(),
anchor, style);
}
void Client::info_hide(bool even_modal) void Client::info_hide(bool even_modal)
{ {
if (not even_modal and m_info.style == InfoStyle::Modal) if (not even_modal and m_info.style == InfoStyle::Modal)

View File

@ -45,7 +45,8 @@ public:
void menu_select(int selected); void menu_select(int selected);
void menu_hide(); 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 info_hide(bool even_modal = false);
void print_status(DisplayLine status_line); void print_status(DisplayLine status_line);
@ -119,8 +120,8 @@ private:
struct Info struct Info
{ {
String title; DisplayLine title;
String content; DisplayLineList content;
BufferCoord anchor; BufferCoord anchor;
Optional<DisplayCoord> ui_anchor; Optional<DisplayCoord> ui_anchor;
InfoStyle style; InfoStyle style;

View File

@ -2144,6 +2144,7 @@ const CommandDesc info_cmd = {
ParameterDesc{ ParameterDesc{
{ { "anchor", { true, "set info anchoring <line>.<column>" } }, { { "anchor", { true, "set info anchoring <line>.<column>" } },
{ "style", { true, "set info style (above, below, menu, modal)" } }, { "style", { true, "set info style (above, below, menu, modal)" } },
{ "markup", { false, "parse markup" } },
{ "title", { true, "set info title" } } }, { "title", { true, "set info title" } } },
ParameterDesc::Flags::None, 0, 1 ParameterDesc::Flags::None, 0, 1
}, },
@ -2179,7 +2180,16 @@ const CommandDesc info_cmd = {
}).value_or(BufferCoord{}); }).value_or(BufferCoord{});
auto title = parser.get_switch("title").value_or(StringView{}); 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<StringView>('\n')
| transform([&](StringView s) {
return parse_display_line(s, context.faces());
})
| gather<DisplayLineList>(),
pos, style);
else
context.client().info_show(title.str(), parser[0], pos, style);
} }
}; };

View File

@ -135,6 +135,7 @@ private:
AtomList m_atoms; AtomList m_atoms;
}; };
using DisplayLineList = Vector<DisplayLine>;
class FaceRegistry; class FaceRegistry;
String fix_atom_text(StringView str); String fix_atom_text(StringView str);
@ -143,11 +144,10 @@ DisplayLine parse_display_line(StringView line, const FaceRegistry& faces, const
class DisplayBuffer : public UseMemoryDomain<MemoryDomain::Display> class DisplayBuffer : public UseMemoryDomain<MemoryDomain::Display>
{ {
public: public:
using LineList = Vector<DisplayLine>;
DisplayBuffer() {} DisplayBuffer() {}
LineList& lines() { return m_lines; } DisplayLineList& lines() { return m_lines; }
const LineList& lines() const { return m_lines; } const DisplayLineList& lines() const { return m_lines; }
// returns the smallest BufferRange which contains every DisplayAtoms // returns the smallest BufferRange which contains every DisplayAtoms
const BufferRange& range() const { return m_range; } const BufferRange& range() const { return m_range; }
@ -160,7 +160,7 @@ public:
size_t timestamp() const { return m_timestamp; } size_t timestamp() const { return m_timestamp; }
private: private:
LineList m_lines; DisplayLineList m_lines;
BufferRange m_range; BufferRange m_range;
size_t m_timestamp = -1; size_t m_timestamp = -1;
}; };

View File

@ -133,7 +133,7 @@ void apply_highlighter(HighlightContext context,
if (begin == end) if (begin == end)
return; return;
using LineIterator = DisplayBuffer::LineList::iterator; using LineIterator = DisplayLineList::iterator;
LineIterator first_line; LineIterator first_line;
Vector<size_t> insert_idx; Vector<size_t> insert_idx;
auto line_end = display_buffer.lines().end(); auto line_end = display_buffer.lines().end();

View File

@ -217,7 +217,7 @@ void JsonUI::menu_hide()
rpc_call("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, DisplayCoord anchor, Face face,
InfoStyle style) InfoStyle style)
{ {

View File

@ -35,7 +35,7 @@ public:
void menu_select(int selected) override; void menu_select(int selected) override;
void menu_hide() 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, DisplayCoord anchor, Face face,
InfoStyle style) override; InfoStyle style) override;
void info_hide() override; void info_hide() override;

View File

@ -517,7 +517,7 @@ std::unique_ptr<UserInterface> make_ui(UIType ui_type)
void menu_select(int) override {} void menu_select(int) override {}
void menu_hide() 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 info_hide() override {}
void draw(const DisplayBuffer&, const Face&, const Face&) override {} void draw(const DisplayBuffer&, const Face&, const Face&) override {}

View File

@ -1032,11 +1032,26 @@ static DisplayCoord compute_pos(DisplayCoord anchor, DisplayCoord size,
struct InfoBox struct InfoBox
{ {
DisplayCoord size; DisplayCoord size;
Vector<String> contents; DisplayLineList contents;
}; };
InfoBox make_info_box(StringView title, StringView message, ColumnCount max_width, template<typename... Args>
ConstArrayView<StringView> assistant) 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<StringView> assistant)
{ {
DisplayCoord assistant_size; DisplayCoord assistant_size;
if (not assistant.empty()) if (not assistant.empty())
@ -1048,71 +1063,70 @@ InfoBox make_info_box(StringView title, StringView message, ColumnCount max_widt
if (max_bubble_width < 4) if (max_bubble_width < 4)
return result; return result;
Vector<StringView> 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; auto line_count = max(assistant_size.line-1, LineCount{(int)content.size()} + 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);
result.size = DisplayCoord{line_count, bubble_width + assistant_size.column + 4}; result.size = DisplayCoord{line_count, bubble_width + assistant_size.column + 4};
const auto assistant_top_margin = (line_count - assistant_size.line+1) / 2; const auto assistant_top_margin = (line_count - assistant_size.line+1) / 2;
for (LineCount i = 0; i < line_count; ++i) for (LineCount i = 0; i < line_count; ++i)
{ {
String line;
constexpr Codepoint dash{L''}; constexpr Codepoint dash{L''};
DisplayLine line;
if (not assistant.empty()) if (not assistant.empty())
{ {
if (i >= assistant_top_margin) StringView assistant_line = (i >= assistant_top_margin) ?
line += assistant[(int)min(i - assistant_top_margin, assistant_size.line-1)]; assistant[(int)min(i - assistant_top_margin, assistant_size.line-1)]
else : assistant[(int)assistant_size.line-1];
line += assistant[(int)assistant_size.line-1];
append_atoms(line, assistant_line.str());
} }
if (i == 0) if (i == 0)
{ {
if (title.empty()) if (title.atoms().empty())
line += "╭─" + String{dash, bubble_width} + "─╮"; append_atoms(line, "╭─" + String{dash, bubble_width} + "─╮");
else 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 left{dash, dash_count / 2};
String right{dash, dash_count - 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]; auto& info_line = content[(int)i - 1];
const ColumnCount padding = bubble_width - info_line.column_length(); const ColumnCount padding = bubble_width - info_line.length();
line += "" + info_line + String{' ', padding} + ""; append_atoms(line, "", info_line, String{' ', padding} + "");
} }
else if (i == lines.size() + 1) else if (i == content.size() + 1)
line += "╰─" + String(dash, bubble_width) + "─╯"; append_atoms(line, "╰─" + String(dash, bubble_width) + "─╯");
result.contents.push_back(std::move(line)); result.contents.push_back(std::move(line));
} }
return result; 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{}; InfoBox info_box{};
for (auto& line : wrap_lines(contents, max_width)) for (auto& line : content)
{ {
++info_box.size.line; ++info_box.size.line;
info_box.size.column = std::max(line.column_length(), info_box.size.column); info_box.size.column = std::max(line.length(), info_box.size.column);
info_box.contents.push_back(line.str()); info_box.contents.push_back(line);
} }
return info_box; 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) DisplayCoord anchor, Face face, InfoStyle style)
{ {
info_hide(); info_hide();
m_info.title = title.str(); m_info.title = title;
m_info.content = content.str(); m_info.content = content;
m_info.anchor = anchor; m_info.anchor = anchor;
m_info.face = face; m_info.face = face;
m_info.style = style; 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) for (auto line = 0_line; line < info_box.size.line; ++line)
{ {
m_info.move_cursor(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; m_dirty = true;
} }

View File

@ -3,6 +3,7 @@
#include "array_view.hh" #include "array_view.hh"
#include "coord.hh" #include "coord.hh"
#include "display_buffer.hh"
#include "event_manager.hh" #include "event_manager.hh"
#include "face.hh" #include "face.hh"
#include "hash_map.hh" #include "hash_map.hh"
@ -44,7 +45,7 @@ public:
void menu_select(int selected) override; void menu_select(int selected) override;
void menu_hide() 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, DisplayCoord anchor, Face face,
InfoStyle style) override; InfoStyle style) override;
void info_hide() override; void info_hide() override;
@ -135,8 +136,8 @@ private:
struct Info : Window struct Info : Window
{ {
String title; DisplayLine title;
String content; DisplayLineList content;
Face face; Face face;
DisplayCoord anchor; DisplayCoord anchor;
InfoStyle style; InfoStyle style;

View File

@ -356,7 +356,7 @@ public:
void menu_select(int selected) override; void menu_select(int selected) override;
void menu_hide() 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, DisplayCoord anchor, Face face,
InfoStyle style) override; InfoStyle style) override;
void info_hide() override; void info_hide() override;
@ -483,7 +483,7 @@ void RemoteUI::menu_hide()
send_message(MessageType::MenuHide); 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, DisplayCoord anchor, Face face,
InfoStyle style) InfoStyle style)
{ {
@ -643,8 +643,8 @@ RemoteClient::RemoteClient(StringView session, StringView name, std::unique_ptr<
break; break;
case MessageType::InfoShow: case MessageType::InfoShow:
{ {
auto title = reader.read<String>(); auto title = reader.read<DisplayLine>();
auto content = reader.read<String>(); auto content = reader.read<DisplayLineList>();
auto anchor = reader.read<DisplayCoord>(); auto anchor = reader.read<DisplayCoord>();
auto face = reader.read<Face>(); auto face = reader.read<Face>();
auto style = reader.read<InfoStyle>(); auto style = reader.read<InfoStyle>();

View File

@ -12,6 +12,7 @@ namespace Kakoune
class String; class String;
class DisplayBuffer; class DisplayBuffer;
class DisplayLine; class DisplayLine;
using DisplayLineList = Vector<DisplayLine, MemoryDomain::Display>;
struct DisplayCoord; struct DisplayCoord;
struct Face; struct Face;
struct Key; struct Key;
@ -56,7 +57,8 @@ public:
virtual void menu_select(int selected) = 0; virtual void menu_select(int selected) = 0;
virtual void menu_hide() = 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, DisplayCoord anchor, Face face,
InfoStyle style) = 0; InfoStyle style) = 0;
virtual void info_hide() = 0; virtual void info_hide() = 0;

View File

@ -186,6 +186,18 @@ private:
Invoker* m_invoker; Invoker* m_invoker;
}; };
template<typename... Funcs>
struct Overload : Funcs...
{
using Funcs::operator()...;
};
template<typename... Funcs>
auto overload(Funcs&&... funcs)
{
return Overload<std::decay_t<Funcs>...>{std::forward<Funcs>(funcs)...};
}
} }
#endif // utils_hh_INCLUDED #endif // utils_hh_INCLUDED

View File

@ -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()); m_display_buffer.set_timestamp(buffer().timestamp());
lines.clear(); lines.clear();