Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Increase modelinefmt configuration power #1267

Merged
merged 1 commit into from
Mar 12, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion README.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -970,7 +970,10 @@ Some options are built in Kakoune, and can be used to control it's behaviour:
on the filesystem.
* `modelinefmt` _string_: A format string used to generate the mode line, that
string is first expanded as a command line would be (expanding `%...{...}`
strings), then markup tags are applied (see <<Markup strings>>).
strings), then markup tags are applied (see <<Markup strings>>). Two special
atom are available as markup: `{{mode_info}}` with information about the current
mode (example `insert 3 sel`), and `{{context_info}}` with information such as
if the file has been modified (with `[+]`), or if it is new (with `[new file]`).
* `ui_options`: colon separated list of key=value pairs that are forwarded to
the user interface implementation. The NCurses UI support the following options:
- `ncurses_set_title`: if `yes` or `true`, the terminal emulator title will
Expand Down
14 changes: 13 additions & 1 deletion doc/manpages/options.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,19 @@ Builtin options
*modelinefmt* 'string'::
A format string used to generate the mode line, that string is first
expanded as a command line would be (expanding '%...{...}' strings),
then markup tags are applied (c.f. the 'Expansions' documentation page)
then markup tags are applied (c.f. the 'Expansions' documentation page.)
Two special atoms are available as markup:

*`{{mode_info}}`*:::
Information about the current mode, such as `insert 3 sel` or
`prompt`. The faces used are StatusLineMode, StatusLineInfo,
and StatusLineValue.

*`{{context_info}}`*:::
Information such as `[+][recording (@)][no-hooks][new file][fifo]`,
in face Information.

The default value is '%val{bufname} %val{cursor_line}:%val{cursor_char_column} {{context_info}} {{mode_info}} - %val{client}@[%val{session}]'

*ui_options*::
colon separated list of key=value pairs that are forwarded to the user
Expand Down
42 changes: 22 additions & 20 deletions src/client.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "event_manager.hh"
#include "user_interface.hh"
#include "window.hh"
#include "hash_map.hh"

#include <csignal>
#include <unistd.h>
Expand Down Expand Up @@ -117,39 +118,40 @@ DisplayCoord Client::dimensions() const
return m_ui->dimensions();
}

String generate_context_info(const Context& context)
{
String s = "";
if (context.buffer().is_modified())
s += "[+]";
if (context.client().input_handler().is_recording())
s += format("[recording ({})]", context.client().input_handler().recording_reg());
if (context.buffer().flags() & Buffer::Flags::New)
s += "[new file]";
if (context.hooks_disabled())
s += "[no-hooks]";
if (context.buffer().flags() & Buffer::Flags::Fifo)
s += "[fifo]";
return s;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have just realized I don't know C++. Where is this string allocated? When will it be deleted? Is it safe to return it like this? It is sent to parse_display_line below.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The String class takes care of all that, if its small enough (<= 22 bytes), it should end up embeded in the string object, else it will end up heap allocated, but the String is taking care of the ownership of that allocation, so this code is safe.

In Kakoune code, and generally in modern style C++, these problems are solved, its only the building block classes that should take care of memory allocation/freeing and provide a safe abstraction that prevent leaks.

}

DisplayLine Client::generate_mode_line() const
{
DisplayLine modeline;
try
{
const String& modelinefmt = context().options()["modelinefmt"].get<String>();

modeline = parse_display_line(expand(modelinefmt, context(), ShellContext{},
[](String s) { return escape(s, '{', '\\'); }));
HashMap<String, DisplayLine> atoms{{ "mode_info", context().client().input_handler().mode_line() },
{ "context_info", {generate_context_info(context()), get_face("Information")}}};
auto expanded = expand(modelinefmt, context(), ShellContext{},
[](String s) { return escape(s, '{', '\\'); });
modeline = parse_display_line(expanded, atoms);
}
catch (runtime_error& err)
{
write_to_debug_buffer(format("Error while parsing modelinefmt: {}", err.what()));
modeline.push_back({ "modelinefmt error, see *debug* buffer", get_face("Error") });
}

Face info_face = get_face("Information");

if (context().buffer().is_modified())
modeline.push_back({ "[+]", info_face });
if (m_input_handler.is_recording())
modeline.push_back({ format("[recording ({})]", m_input_handler.recording_reg()), info_face });
if (context().buffer().flags() & Buffer::Flags::New)
modeline.push_back({ "[new file]", info_face });
if (context().hooks_disabled())
modeline.push_back({ "[no-hooks]", info_face });
if (context().buffer().flags() & Buffer::Flags::Fifo)
modeline.push_back({ "[fifo]", info_face });
modeline.push_back({ " " });
for (auto& atom : m_input_handler.mode_line())
modeline.push_back(std::move(atom));
modeline.push_back({ format(" - {}@[{}]", context().name(), Server::instance().session()) });

return modeline;
}

Expand Down
15 changes: 13 additions & 2 deletions src/display_buffer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ void DisplayBuffer::optimize()
line.optimize();
}

DisplayLine parse_display_line(StringView line)
DisplayLine parse_display_line(StringView line, const HashMap<String, DisplayLine>& builtins)
{
DisplayLine res;
bool was_antislash = false;
Expand All @@ -312,7 +312,18 @@ DisplayLine parse_display_line(StringView line)
auto closing = std::find(it+1, end, '}');
if (closing == end)
throw runtime_error("unclosed face definition");
face = get_face({it+1, closing});
if (*(it+1) == '{' and closing+1 != end and *(closing+1) == '}')
{
auto builtin_it = builtins.find(StringView{it+2, closing});
if (builtin_it == builtins.end())
throw runtime_error(format("undefined atom {}", StringView{it+2, closing}));
for (auto& atom : builtin_it->value)
res.push_back(atom);
// closing is now at the first char of "}}", advance it to the second
++closing;
}
else
face = get_face({it+1, closing});
it = closing;
pos = closing + 1;
}
Expand Down
3 changes: 2 additions & 1 deletion src/display_buffer.hh
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "coord.hh"
#include "string.hh"
#include "vector.hh"
#include "hash_map.hh"

namespace Kakoune
{
Expand Down Expand Up @@ -143,7 +144,7 @@ private:
AtomList m_atoms;
};

DisplayLine parse_display_line(StringView line);
DisplayLine parse_display_line(StringView line, const HashMap<String, DisplayLine>& builtins = {});

class DisplayBuffer : public UseMemoryDomain<MemoryDomain::Display>
{
Expand Down
10 changes: 7 additions & 3 deletions src/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ static const char* startup_info =
" * The `identifier` face has been replaced with `variable`,\n"
" `function` and `module`, update your custom colorschemes\n"
" * BufNew and BufOpen hooks have been renamed to BufNewFile\n"
" and BufOpenFile.\n";
" and BufOpenFile.\n"
" * The status line can be further customized.\n"
" See `help options modelinefmt`.\n";

struct startup_error : runtime_error
{
Expand Down Expand Up @@ -154,7 +156,8 @@ void register_env_vars()
"window_height", false,
[](StringView name, const Context& context) -> String
{ return to_string(context.window().dimensions().line); }
} };
}
};

ShellManager& shell_manager = ShellManager::instance();
for (auto& env_var : env_vars)
Expand Down Expand Up @@ -306,7 +309,8 @@ void register_options()
" ncurses_wheel_down_button int\n",
UserInterface::Options{});
reg.declare_option("modelinefmt", "format string used to generate the modeline",
"%val{bufname} %val{cursor_line}:%val{cursor_char_column} "_str);
"%val{bufname} %val{cursor_line}:%val{cursor_char_column} {{context_info}} {{mode_info}} - %val{client}@[%val{session}]"_str);

reg.declare_option("debug", "various debug flags", DebugFlags::None);
reg.declare_option("readonly", "prevent buffers from being modified", false);
reg.declare_option<String, check_extra_word_char>(
Expand Down
2 changes: 1 addition & 1 deletion test/highlight/regions/display
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{ "jsonrpc": "2.0", "method": "draw", "params": [[[{ "face": { "fg": "black", "bg": "white", "attributes": [] }, "contents": "\"" }, { "face": { "fg": "green", "bg": "default", "attributes": [] }, "contents": "abcdefgh\"" }, { "face": { "fg": "yellow", "bg": "default", "attributes": [] }, "contents": " hehe " }, { "face": { "fg": "red", "bg": "default", "attributes": [] }, "contents": "${ youhou{hihi} }" }, { "face": { "fg": "yellow", "bg": "default", "attributes": [] }, "contents": "\u000a" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "" }]], { "fg": "default", "bg": "default", "attributes": [] }, { "fg": "blue", "bg": "default", "attributes": [] }] }
{ "jsonrpc": "2.0", "method": "menu_hide", "params": [] }
{ "jsonrpc": "2.0", "method": "info_hide", "params": [] }
{ "jsonrpc": "2.0", "method": "draw_status", "params": [[], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "out 1:1 " }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " " }, { "face": { "fg": "blue", "bg": "default", "attributes": [] }, "contents": "1 sel" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " - unnamed0@[kak-tests]" }], { "fg": "cyan", "bg": "default", "attributes": [] }] }
{ "jsonrpc": "2.0", "method": "draw_status", "params": [[], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "out 1:1 " }, { "face": { "fg": "black", "bg": "yellow", "attributes": [] }, "contents": "" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " " }, { "face": { "fg": "blue", "bg": "default", "attributes": [] }, "contents": "1 sel" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " - unnamed0@[kak-tests]" }], { "fg": "cyan", "bg": "default", "attributes": [] }] }
{ "jsonrpc": "2.0", "method": "refresh", "params": [true] }
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{ "jsonrpc": "2.0", "method": "draw", "params": [[[{ "face": { "fg": "red", "bg": "default", "attributes": [] }, "contents": "“" }, { "face": { "fg": "white", "bg": "blue", "attributes": [] }, "contents": "We" }, { "face": { "fg": "black", "bg": "white", "attributes": [] }, "contents": " " }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "ought to scrape this planet clean of every living thing on it," }, { "face": { "fg": "red", "bg": "default", "attributes": [] }, "contents": "”" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "\u000a" }]], { "fg": "default", "bg": "default", "attributes": [] }, { "fg": "blue", "bg": "default", "attributes": [] }] }
{ "jsonrpc": "2.0", "method": "menu_hide", "params": [] }
{ "jsonrpc": "2.0", "method": "info_hide", "params": [] }
{ "jsonrpc": "2.0", "method": "draw_status", "params": [[], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "out 1:4 " }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " " }, { "face": { "fg": "blue", "bg": "default", "attributes": [] }, "contents": "1 sel" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " - unnamed0@[kak-tests]" }], { "fg": "cyan", "bg": "default", "attributes": [] }] }
{ "jsonrpc": "2.0", "method": "draw_status", "params": [[], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "out 1:4 " }, { "face": { "fg": "black", "bg": "yellow", "attributes": [] }, "contents": "" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " " }, { "face": { "fg": "blue", "bg": "default", "attributes": [] }, "contents": "1 sel" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " - unnamed0@[kak-tests]" }], { "fg": "cyan", "bg": "default", "attributes": [] }] }
{ "jsonrpc": "2.0", "method": "refresh", "params": [true] }