Skip to content

Commit

Permalink
Increase modelinefmt configuration power
Browse files Browse the repository at this point in the history
  • Loading branch information
Dan Rosén committed Mar 10, 2017
1 parent da206d9 commit f31a464
Show file tree
Hide file tree
Showing 8 changed files with 63 additions and 30 deletions.
5 changes: 4 additions & 1 deletion README.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -968,7 +968,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;
}

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, HashMap<String, DisplayLine> atoms)
{
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 atom_it = atoms.find(String{it+2, closing});
if (atom_it == atoms.end())
throw runtime_error(format("undefined atom {}", String{it+2, closing}));
for (auto display_it = atom_it->value.begin(), end = atom_it->value.end(); display_it != end; ++display_it)
res.push_back(*display_it);
// 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, HashMap<String, DisplayLine> m_atoms = HashMap<String, DisplayLine>{});

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] }

0 comments on commit f31a464

Please sign in to comment.