Compare commits

..

10 Commits

Author SHA1 Message Date
ad9a4b25d5 Add zero-indexed register 2023-10-11 19:36:16 +02:00
f94e8e49ed Add flake files 2023-10-11 19:35:55 +02:00
Maxime Coste
5c793a6a1e Merge remote-tracking branch 'Screwtapello/hint-git-next-hunk' 2023-10-04 21:04:27 +11:00
Maxime Coste
18902b15af Refactor regex_prompt logic and fix function being called on abort
Fixes #4993
2023-10-04 21:03:10 +11:00
Maxime Coste
7be996e5a4 Merge remote-tracking branch 'raiguard/rc/lua-highlighting' 2023-10-04 20:31:38 +11:00
Tim Allen
9286a7ee49 Make "git next-hunk" hint about how it works. 2023-10-04 10:53:20 +11:00
Tim Allen
87787defd8 The command is called "git prev-hunk", not "git previous-hunk". 2023-10-04 10:52:15 +11:00
Caleb Heuer
5ec40c63b2 Add highlighting of Lua variables, labels, and self keyword 2023-09-30 11:57:30 -06:00
Loric Brevet
a2fd401cfa
Add an InlineInformation face distinct from Information 2023-09-28 15:06:29 +02:00
Maxime Coste
23afed056b Add a daemonize-session command and refactor local client handling
Make it possible to move the current session to a daemon one after
the fact, which is useful to ensure the session state survives client
disconnecting, for example when working from ssh.
2023-09-26 17:50:56 +10:00
14 changed files with 184 additions and 63 deletions

View File

@ -8,6 +8,9 @@ released versions.
* `+` only duplicates identical selections a single time to avoid surprising * `+` only duplicates identical selections a single time to avoid surprising
and slow exponential growth in the number of selections. and slow exponential growth in the number of selections.
* `daemonize-session` command makes it possible to convert the current session
to a deamon one (which will not exit on last client disconnecting)
== Kakoune 2023.08.08 == Kakoune 2023.08.08
* Fix compilation errors on FreeBSD and MacOS using clang * Fix compilation errors on FreeBSD and MacOS using clang

View File

@ -107,6 +107,9 @@ the user interface:
*Information*:: *Information*::
Face for windows and messages displaying other information. Face for windows and messages displaying other information.
*InlineInformation*::
Face for windows and messages displaying inline information.
*Error*:: *Error*::
Face for errors reported by Kakoune in the status line. Face for errors reported by Kakoune in the status line.

59
flake.lock Normal file
View File

@ -0,0 +1,59 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1694529238,
"narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "ff7b65b44d01cf9ba6a71320833626af21126384",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1696757521,
"narHash": "sha256-cfgtLNCBLFx2qOzRLI6DHfqTdfWI+UbvsKYa3b3fvaA=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "2646b294a146df2781b1ca49092450e8a32814e1",
"type": "github"
},
"original": {
"id": "nixpkgs",
"type": "indirect"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

43
flake.nix Normal file
View File

@ -0,0 +1,43 @@
{
description = "kakoune (patch)";
inputs = {
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (sys:
let
pkgs = nixpkgs.legacyPackages.${sys};
kak = pkgs.stdenv.mkDerivation rec {
pname = "kakoune";
version = "2022.10.31";
src = ./. ;
makeFlags = [ "debug=no" "PREFIX=${placeholder "out"}" ];
preConfigure = ''
export version="v${version}"
'';
enableParallelBuilding = true;
doInstallCheck = true;
installCheckPhase = ''
$out/bin/kak -ui json -e "kill 0"
'';
postInstall = ''
# make share/kak/autoload a directory, so we can use symlinkJoin with plugins
cd "$out/share/kak"
autoload_target=$(readlink autoload)
rm autoload
mkdir autoload
ln -s --relative "$autoload_target" autoload
'';
};
in rec {
packages.kak = kak;
packages.default = packages.kak;
}
);
}

View File

@ -46,14 +46,17 @@ add-highlighter shared/lua/double_string region '"' (?<!\\)(?:\\\\)*" fill str
add-highlighter shared/lua/single_string region "'" (?<!\\)(?:\\\\)*' fill string add-highlighter shared/lua/single_string region "'" (?<!\\)(?:\\\\)*' fill string
add-highlighter shared/lua/comment region '--' $ fill comment add-highlighter shared/lua/comment region '--' $ fill comment
add-highlighter shared/lua/code/variable regex \b\w*\b 0:variable # Everything in Lua is a variable!
add-highlighter shared/lua/code/function_declaration regex \b(?:function\h+)(?:\w+\h*\.\h*)*([a-zA-Z_]\w*)\( 1:function add-highlighter shared/lua/code/function_declaration regex \b(?:function\h+)(?:\w+\h*\.\h*)*([a-zA-Z_]\w*)\( 1:function
add-highlighter shared/lua/code/function_call regex \b([a-zA-Z_]\w*)\h*(?=[\(\{]) 1:function add-highlighter shared/lua/code/function_call regex \b([a-zA-Z_]\w*)\h*(?=[\(\{]) 1:function
add-highlighter shared/lua/code/keyword regex \b(break|do|else|elseif|end|for|function|goto|if|in|local|repeat|return|then|until|while)\b 0:keyword add-highlighter shared/lua/code/keyword regex \b(break|do|else|elseif|end|for|function|goto|if|in|local|repeat|return|then|until|while)\b 0:keyword
add-highlighter shared/lua/code/value regex \b(false|nil|true|[0-9]+(:?\.[0-9])?(:?[eE]-?[0-9]+)?|0x[0-9a-fA-F])\b 0:value add-highlighter shared/lua/code/value regex \b(false|nil|true|self|[0-9]+(:?\.[0-9])?(:?[eE]-?[0-9]+)?|0x[0-9a-fA-F])\b 0:value
add-highlighter shared/lua/code/symbolic_operator regex (\+|-|\*|/|%|\^|==?|~=|<=?|>=?|\.\.|\.\.\.|#) 0:operator add-highlighter shared/lua/code/symbolic_operator regex (\+|-|\*|/|%|\^|==?|~=|<=?|>=?|\.\.|\.\.\.|#) 0:operator
add-highlighter shared/lua/code/keyword_operator regex \b(and|or|not)\b 0:operator add-highlighter shared/lua/code/keyword_operator regex \b(and|or|not)\b 0:operator
add-highlighter shared/lua/code/module regex \b(_G|_ENV)\b 0:module add-highlighter shared/lua/code/module regex \b(_G|_ENV)\b 0:module
add-highlighter shared/lua/code/attribute regex \B(<[a-zA-Z_]\w*>)\B 0:attribute add-highlighter shared/lua/code/attribute regex \B(<[a-zA-Z_]\w*>)\B 0:attribute
add-highlighter shared/lua/code/label regex \s(::\w*::) 1:meta
add-highlighter shared/lua/code/goto_label regex "\bgoto (\w*)\b" 1:meta
# Commands # Commands
# ‾‾‾‾‾‾‾‾ # ‾‾‾‾‾‾‾‾

View File

@ -60,7 +60,7 @@ define-command -params 1.. \
init init
log log
next-hunk next-hunk
previous-hunk prev-hunk
show show
show-branch show-branch
show-diff show-diff
@ -225,7 +225,7 @@ define-command -params 1.. \
shift shift
if [ $# -lt 1 ]; then if [ $# -lt 1 ]; then
echo "fail 'no git hunks found'" echo "fail 'no git hunks found, try \":git show-diff\" first'"
exit exit
fi fi

View File

@ -278,7 +278,8 @@ void Client::redraw_ifn()
if (m_ui_pending & InfoShow and m_info.ui_anchor) if (m_ui_pending & InfoShow and m_info.ui_anchor)
m_ui->info_show(m_info.title, m_info.content, *m_info.ui_anchor, m_ui->info_show(m_info.title, m_info.content, *m_info.ui_anchor,
faces["Information"], m_info.style); faces[(is_inline(m_info.style) || m_info.style == InfoStyle::MenuDoc)
? "InlineInformation" : "Information"], m_info.style);
if (m_ui_pending & InfoHide) if (m_ui_pending & InfoHide)
m_ui->info_hide(); m_ui->info_hide();

View File

@ -688,6 +688,17 @@ const CommandDesc force_kill_cmd = {
kill<true> kill<true>
}; };
const CommandDesc daemonize_session_cmd = {
"daemonize-session",
nullptr,
"daemonize-session: set the session server not to quit on last client exit",
{ {} },
CommandFlags::None,
CommandHelper{},
CommandCompleter{},
[](const ParametersParser&, Context&, const ShellContext&) { Server::instance().daemonize(); }
};
template<bool force> template<bool force>
void quit(const ParametersParser& parser, Context& context, const ShellContext&) void quit(const ParametersParser& parser, Context& context, const ShellContext&)
{ {
@ -2756,6 +2767,7 @@ void register_commands()
register_command(write_all_quit_cmd); register_command(write_all_quit_cmd);
register_command(kill_cmd); register_command(kill_cmd);
register_command(force_kill_cmd); register_command(force_kill_cmd);
register_command(daemonize_session_cmd);
register_command(quit_cmd); register_command(quit_cmd);
register_command(force_quit_cmd); register_command(force_quit_cmd);
register_command(write_quit_cmd); register_command(write_quit_cmd);

View File

@ -191,6 +191,7 @@ FaceRegistry::FaceRegistry()
{ "MenuBackground", {Face{ Color::Blue, Color::White }} }, { "MenuBackground", {Face{ Color::Blue, Color::White }} },
{ "MenuInfo", {Face{ Color::Cyan, Color::Default }} }, { "MenuInfo", {Face{ Color::Cyan, Color::Default }} },
{ "Information", {Face{ Color::Black, Color::Yellow }} }, { "Information", {Face{ Color::Black, Color::Yellow }} },
{ "InlineInformation", {Face{}, "Information"} },
{ "Error", {Face{ Color::Black, Color::Red }} }, { "Error", {Face{ Color::Black, Color::Red }} },
{ "DiagnosticError", {Face{ Color::Red, Color::Default }} }, { "DiagnosticError", {Face{ Color::Red, Color::Default }} },
{ "DiagnosticWarning", {Face{ Color::Yellow, Color::Default }} }, { "DiagnosticWarning", {Face{ Color::Yellow, Color::Default }} },

View File

@ -198,6 +198,7 @@ constexpr StringView register_doc =
"%: buffer name\n" "%: buffer name\n"
".: selection contents\n" ".: selection contents\n"
"#: selection index\n" "#: selection index\n"
"$: selection index (zero-indexed)\n"
"_: null register\n" "_: null register\n"
"\": default yank/paste register\n" "\": default yank/paste register\n"
"@: default macro register\n" "@: default macro register\n"

View File

@ -47,6 +47,7 @@ struct {
} constexpr version_notes[] = { { } constexpr version_notes[] = { {
0, 0,
"» {+b}+{} only duplicates identical selections a single time\n" "» {+b}+{} only duplicates identical selections a single time\n"
"» {+u}daemonize-session{} command\n"
}, { }, {
20230805, 20230805,
"» Fix FreeBSD/MacOS clang compilation\n" "» Fix FreeBSD/MacOS clang compilation\n"
@ -435,6 +436,17 @@ void register_registers()
return res; return res;
})); }));
register_manager.add_register('$', make_dyn_reg(
"$",
[](const Context& context) {
const size_t count = context.selections().size();
StringList res;
res.reserve(count);
for (size_t i = 0; i < count; ++i)
res.push_back(to_string((int)i));
return res;
}));
for (size_t i = 0; i < 10; ++i) for (size_t i = 0; i < 10; ++i)
{ {
register_manager.add_register('0'+i, make_dyn_reg( register_manager.add_register('0'+i, make_dyn_reg(
@ -603,7 +615,6 @@ void register_options()
} }
static Client* local_client = nullptr; static Client* local_client = nullptr;
static int local_client_exit = 0;
static bool convert_to_client_pending = false; static bool convert_to_client_pending = false;
enum class UIType enum class UIType
@ -671,38 +682,7 @@ pid_t fork_server_to_background()
std::unique_ptr<UserInterface> create_local_ui(UIType ui_type) std::unique_ptr<UserInterface> create_local_ui(UIType ui_type)
{ {
if (ui_type != UIType::Terminal) if (ui_type == UIType::Terminal and not isatty(0))
return make_ui(ui_type);
struct LocalUI : TerminalUI
{
LocalUI()
{
set_signal_handler(SIGTSTP, [](int) {
if (ClientManager::instance().count() == 1 and
*ClientManager::instance().begin() == local_client)
TerminalUI::instance().suspend();
else
convert_to_client_pending = true;
});
}
~LocalUI() override
{
local_client = nullptr;
if (convert_to_client_pending or
ClientManager::instance().empty())
return;
if (fork_server_to_background())
{
this->TerminalUI::~TerminalUI();
exit(local_client_exit);
}
}
};
if (not isatty(0))
{ {
// move stdin to another fd, and restore tty as stdin // move stdin to another fd, and restore tty as stdin
int fd = dup(0); int fd = dup(0);
@ -712,7 +692,19 @@ std::unique_ptr<UserInterface> create_local_ui(UIType ui_type)
create_fifo_buffer("*stdin*", fd, Buffer::Flags::None); create_fifo_buffer("*stdin*", fd, Buffer::Flags::None);
} }
return std::make_unique<LocalUI>(); auto ui = make_ui(ui_type);
static SignalHandler old_handler = set_signal_handler(SIGTSTP, [](int sig) {
if (ClientManager::instance().count() == 1 and
*ClientManager::instance().begin() == local_client)
old_handler(sig);
else
{
convert_to_client_pending = true;
set_signal_handler(SIGTSTP, old_handler);
}
});
return ui;
} }
int run_client(StringView session, StringView name, StringView client_init, int run_client(StringView session, StringView name, StringView client_init,
@ -875,13 +867,14 @@ int run_server(StringView session, StringView server_init,
write_to_debug_buffer(format("error while opening command line files: {}", error.what())); write_to_debug_buffer(format("error while opening command line files: {}", error.what()));
} }
int exit_status = 0;
try try
{ {
if (not server.is_daemon()) if (not server.is_daemon())
{ {
local_client = client_manager.create_client( local_client = client_manager.create_client(
create_local_ui(ui_type), getpid(), {}, get_env_vars(), client_init, init_buffer, std::move(init_coord), create_local_ui(ui_type), getpid(), {}, get_env_vars(), client_init, init_buffer, std::move(init_coord),
[](int status) { local_client_exit = status; }); [&](int status) { exit_status = status; });
if (startup_error and local_client) if (startup_error and local_client)
local_client->print_status({ local_client->print_status({
@ -920,24 +913,22 @@ int run_server(StringView session, StringView server_init,
global_scope.option_registry().clear_option_trash(); global_scope.option_registry().clear_option_trash();
if (local_client and not contains(client_manager, local_client)) if (local_client and not contains(client_manager, local_client))
local_client = nullptr;
else if (local_client and not local_client->is_ui_ok())
{ {
ClientManager::instance().remove_client(*local_client, false, -1);
local_client = nullptr; local_client = nullptr;
if (not client_manager.empty() and fork_server_to_background()) if ((not client_manager.empty() or server.is_daemon()) and fork_server_to_background())
return 0; exit(exit_status); // We do not want to run destructors and hooks here
} }
else if (convert_to_client_pending) else if (convert_to_client_pending)
{ {
kak_assert(local_client); kak_assert(local_client);
auto& local_context = local_client->context(); auto& local_context = local_client->context();
const String client_name = local_context.name(); String client_name = local_context.name();
const String buffer_name = local_context.buffer().name(); String buffer_name = local_context.buffer().name();
const String selections = selection_list_to_string(ColumnType::Byte, local_context.selections()); String selections = selection_list_to_string(ColumnType::Byte, local_context.selections());
ClientManager::instance().remove_client(*local_client, true, 0); ClientManager::instance().remove_client(*local_client, true, 0);
client_manager.clear_client_trash(); client_manager.clear_client_trash();
local_client = nullptr;
convert_to_client_pending = false; convert_to_client_pending = false;
if (fork_server_to_background()) if (fork_server_to_background())
@ -952,7 +943,7 @@ int run_server(StringView session, StringView server_init,
} }
catch (const kill_session& kill) catch (const kill_session& kill)
{ {
local_client_exit = kill.exit_status; exit_status = kill.exit_status;
} }
{ {
@ -960,7 +951,7 @@ int run_server(StringView session, StringView server_init,
global_scope.hooks().run_hook(Hook::KakEnd, "", empty_context); global_scope.hooks().run_hook(Hook::KakEnd, "", empty_context);
} }
return local_client_exit; return exit_status;
} }
int run_filter(StringView keystr, ConstArrayView<StringView> files, bool quiet, StringView suffix_backup) int run_filter(StringView keystr, ConstArrayView<StringView> files, bool quiet, StringView suffix_backup)

View File

@ -854,20 +854,22 @@ void regex_prompt(Context& context, String prompt, char reg, T func)
context.input_handler().set_prompt_face(context.faces()["Prompt"]); context.input_handler().set_prompt_face(context.faces()["Prompt"]);
} }
switch (event)
if (not incsearch and event == PromptEvent::Change) {
case PromptEvent::Change:
if (not incsearch or str.empty())
return; return;
if (event == PromptEvent::Validate)
context.push_jump();
else
RegisterManager::instance()[reg].restore(context, saved_reg);
if (not str.empty() and event == PromptEvent::Change) // Ensure search register based highlighters work incrementally
RegisterManager::instance()[reg].set(context, str.str()); RegisterManager::instance()[reg].set(context, str.str());
func(Regex{str, direction_flags(mode)}, event, context);
if (not str.empty() or event == PromptEvent::Validate) return;
case PromptEvent::Abort:
RegisterManager::instance()[reg].restore(context, saved_reg);
return;
case PromptEvent::Validate:
context.push_jump();
func(Regex{str.empty() ? default_regex : str, direction_flags(mode)}, event, context); func(Regex{str.empty() ? default_regex : str, direction_flags(mode)}, event, context);
return;
}
} }
catch (regex_error& err) catch (regex_error& err)
{ {

View File

@ -67,6 +67,7 @@ static const HashMap<StringView, Codepoint> reg_names {
{ "percent", '%' }, { "percent", '%' },
{ "dot", '.' }, { "dot", '.' },
{ "hash", '#' }, { "hash", '#' },
{ "dollar", '$' },
{ "underscore", '_' }, { "underscore", '_' },
{ "colon", ':' } { "colon", ':' }
}; };

View File

@ -59,6 +59,7 @@ struct Server : public Singleton<Server>
bool negotiating() const { return not m_accepters.empty(); } bool negotiating() const { return not m_accepters.empty(); }
void daemonize() { m_is_daemon = true; }
bool is_daemon() const { return m_is_daemon; } bool is_daemon() const { return m_is_daemon; }
private: private: