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
* menu_select(int selected)
* 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:
- prompt: display the info as a prompt info (anchor is ignored)
- 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
* `info` supports markup with the `-markup` switch
== Kakoune 2019.07.01
* Re-organized bundled script files directory hierarchy.

View File

@ -323,6 +323,10 @@ but not really useful in that context.
*-title* <text>:::
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>]...::
prevent an error in *commands* from aborting the whole command

View File

@ -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<StringView>('\n')
| transform([](StringView s) { return DisplayLine{s.str(), Face{}}; })
| gather<DisplayLineList>(),
anchor, style);
}
void Client::info_hide(bool even_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_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<DisplayCoord> ui_anchor;
InfoStyle style;

View File

@ -2144,6 +2144,7 @@ const CommandDesc info_cmd = {
ParameterDesc{
{ { "anchor", { true, "set info anchoring <line>.<column>" } },
{ "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<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;
};
using DisplayLineList = Vector<DisplayLine>;
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<MemoryDomain::Display>
{
public:
using LineList = Vector<DisplayLine>;
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;
};

View File

@ -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<size_t> insert_idx;
auto line_end = display_buffer.lines().end();

View File

@ -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)
{

View File

@ -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;

View File

@ -517,7 +517,7 @@ std::unique_ptr<UserInterface> 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 {}

View File

@ -1032,11 +1032,26 @@ static DisplayCoord compute_pos(DisplayCoord anchor, DisplayCoord size,
struct InfoBox
{
DisplayCoord size;
Vector<String> contents;
DisplayLineList contents;
};
InfoBox make_info_box(StringView title, StringView message, ColumnCount max_width,
ConstArrayView<StringView> assistant)
template<typename... Args>
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;
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<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;
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;
}

View File

@ -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;

View File

@ -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<String>();
auto content = reader.read<String>();
auto title = reader.read<DisplayLine>();
auto content = reader.read<DisplayLineList>();
auto anchor = reader.read<DisplayCoord>();
auto face = reader.read<Face>();
auto style = reader.read<InfoStyle>();

View File

@ -12,6 +12,7 @@ namespace Kakoune
class String;
class DisplayBuffer;
class DisplayLine;
using DisplayLineList = Vector<DisplayLine, MemoryDomain::Display>;
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;

View File

@ -186,6 +186,18 @@ private:
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

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());
lines.clear();