Increase modelinefmt configuration power

This commit is contained in:
Dan Rosén 2017-03-10 10:06:37 +01:00
parent b9317ba38c
commit 5a403a9611
8 changed files with 63 additions and 30 deletions

View File

@ -970,7 +970,10 @@ Some options are built in Kakoune, and can be used to control it's behaviour:
on the filesystem. on the filesystem.
* `modelinefmt` _string_: A format string used to generate the mode line, that * `modelinefmt` _string_: A format string used to generate the mode line, that
string is first expanded as a command line would be (expanding `%...{...}` 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 * `ui_options`: colon separated list of key=value pairs that are forwarded to
the user interface implementation. The NCurses UI support the following options: the user interface implementation. The NCurses UI support the following options:
- `ncurses_set_title`: if `yes` or `true`, the terminal emulator title will - `ncurses_set_title`: if `yes` or `true`, the terminal emulator title will

View File

@ -137,7 +137,19 @@ Builtin options
*modelinefmt* 'string':: *modelinefmt* 'string'::
A format string used to generate the mode line, that string is first A format string used to generate the mode line, that string is first
expanded as a command line would be (expanding '%...{...}' strings), 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*:: *ui_options*::
colon separated list of key=value pairs that are forwarded to the user colon separated list of key=value pairs that are forwarded to the user

View File

@ -11,6 +11,7 @@
#include "event_manager.hh" #include "event_manager.hh"
#include "user_interface.hh" #include "user_interface.hh"
#include "window.hh" #include "window.hh"
#include "hash_map.hh"
#include <csignal> #include <csignal>
#include <unistd.h> #include <unistd.h>
@ -117,15 +118,33 @@ DisplayCoord Client::dimensions() const
return m_ui->dimensions(); 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;
}
DisplayLine Client::generate_mode_line() const DisplayLine Client::generate_mode_line() const
{ {
DisplayLine modeline; DisplayLine modeline;
try try
{ {
const String& modelinefmt = context().options()["modelinefmt"].get<String>(); const String& modelinefmt = context().options()["modelinefmt"].get<String>();
HashMap<String, DisplayLine> atoms{{ "mode_info", context().client().input_handler().mode_line() },
modeline = parse_display_line(expand(modelinefmt, context(), ShellContext{}, { "context_info", {generate_context_info(context()), get_face("Information")}}};
[](String s) { return escape(s, '{', '\\'); })); auto expanded = expand(modelinefmt, context(), ShellContext{},
[](String s) { return escape(s, '{', '\\'); });
modeline = parse_display_line(expanded, atoms);
} }
catch (runtime_error& err) catch (runtime_error& err)
{ {
@ -133,23 +152,6 @@ DisplayLine Client::generate_mode_line() const
modeline.push_back({ "modelinefmt error, see *debug* buffer", get_face("Error") }); 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; return modeline;
} }

View File

@ -286,7 +286,7 @@ void DisplayBuffer::optimize()
line.optimize(); line.optimize();
} }
DisplayLine parse_display_line(StringView line) DisplayLine parse_display_line(StringView line, const HashMap<String, DisplayLine>& builtins)
{ {
DisplayLine res; DisplayLine res;
bool was_antislash = false; bool was_antislash = false;
@ -312,7 +312,18 @@ DisplayLine parse_display_line(StringView line)
auto closing = std::find(it+1, end, '}'); auto closing = std::find(it+1, end, '}');
if (closing == end) if (closing == end)
throw runtime_error("unclosed face definition"); 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; it = closing;
pos = closing + 1; pos = closing + 1;
} }

View File

@ -6,6 +6,7 @@
#include "coord.hh" #include "coord.hh"
#include "string.hh" #include "string.hh"
#include "vector.hh" #include "vector.hh"
#include "hash_map.hh"
namespace Kakoune namespace Kakoune
{ {
@ -143,7 +144,7 @@ private:
AtomList m_atoms; 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> class DisplayBuffer : public UseMemoryDomain<MemoryDomain::Display>
{ {

View File

@ -43,7 +43,9 @@ static const char* startup_info =
" * The `identifier` face has been replaced with `variable`,\n" " * The `identifier` face has been replaced with `variable`,\n"
" `function` and `module`, update your custom colorschemes\n" " `function` and `module`, update your custom colorschemes\n"
" * BufNew and BufOpen hooks have been renamed to BufNewFile\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 struct startup_error : runtime_error
{ {
@ -154,7 +156,8 @@ void register_env_vars()
"window_height", false, "window_height", false,
[](StringView name, const Context& context) -> String [](StringView name, const Context& context) -> String
{ return to_string(context.window().dimensions().line); } { return to_string(context.window().dimensions().line); }
} }; }
};
ShellManager& shell_manager = ShellManager::instance(); ShellManager& shell_manager = ShellManager::instance();
for (auto& env_var : env_vars) for (auto& env_var : env_vars)
@ -306,7 +309,8 @@ void register_options()
" ncurses_wheel_down_button int\n", " ncurses_wheel_down_button int\n",
UserInterface::Options{}); UserInterface::Options{});
reg.declare_option("modelinefmt", "format string used to generate the modeline", 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("debug", "various debug flags", DebugFlags::None);
reg.declare_option("readonly", "prevent buffers from being modified", false); reg.declare_option("readonly", "prevent buffers from being modified", false);
reg.declare_option<String, check_extra_word_char>( reg.declare_option<String, check_extra_word_char>(

View File

@ -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": "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": "menu_hide", "params": [] }
{ "jsonrpc": "2.0", "method": "info_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] } { "jsonrpc": "2.0", "method": "refresh", "params": [true] }

View File

@ -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": "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": "menu_hide", "params": [] }
{ "jsonrpc": "2.0", "method": "info_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] } { "jsonrpc": "2.0", "method": "refresh", "params": [true] }