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

feat(libcmd): add zig based readline #20

Merged
merged 1 commit into from
Feb 8, 2025
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: 5 additions & 0 deletions src/libcmd/libcmd.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pub const repl = @import("repl.zig");

comptime {
_ = repl;
}
25 changes: 24 additions & 1 deletion src/libcmd/meson.build
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
project('nix-cmd', 'cpp',
project('nix-cmd', 'cpp', 'c',
version : files('.zix-version'),
default_options : [
'cpp_std=c++2a',
Expand All @@ -10,6 +10,8 @@ project('nix-cmd', 'cpp',
license : 'LGPL-2.1-or-later',
)

fs = import('fs')

cxx = meson.get_compiler('cpp')

subdir('nix-meson-build-support/deps-lists')
Expand Down Expand Up @@ -49,6 +51,12 @@ elif readline_flavor == 'readline'
1,
description: 'Use readline instead of editline',
)
elif readline_flavor == 'zig'
configdata.set(
'USE_ZIG_REPL',
1,
description: 'Use zig implementation for repl',
)
else
error('illegal editline flavor', readline_flavor)
endif
Expand Down Expand Up @@ -113,10 +121,25 @@ headers = [config_h] + files(
'repl.hh',
)

subdir('nix-meson-build-support/zig')

sources += custom_target(
'zig build-obj',
command: [zig, 'build-lib', '-femit-h=@OUTDIR@/libcmd-zig.h', '-femit-bin=@OUTPUT@', '-ofmt=c', '-lc', zig_args, '@INPUT@'],
output: 'libcmd-zig.c',
input: 'libcmd.zig',
depend_files: [
'repl.zig',
],
)

include_dirs += fs.parent(fs.parent(zig.full_path())) / 'lib' / 'zig'

this_library = library(
'nixcmd',
sources,
dependencies : deps_public + deps_private + deps_other,
include_directories : include_dirs,
prelink : true, # For C++ static initializers
install : true,
)
Expand Down
2 changes: 1 addition & 1 deletion src/libcmd/meson.options
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ option(
option(
'readline-flavor',
type : 'combo',
choices : ['editline', 'readline'],
choices : ['editline', 'readline', 'zig'],
value : 'editline',
description : 'Which library to use for nice line editing with the Nix language REPL',
)
17 changes: 15 additions & 2 deletions src/libcmd/package.nix
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,13 @@
#
# - editline (default)
# - readline
readlineFlavor ? if stdenv.hostPlatform.isWindows then "readline" else "editline",
readlineFlavor ?
if stdenv.hostPlatform.isWindows then
"readline"
else if stdenv.hostPlatform.isLinux then
"zig"
else
"editline",
}:

let
Expand All @@ -51,10 +57,17 @@ mkMesonLibrary (finalAttrs: {
./meson.options
(fileset.fileFilter (file: file.hasExt "cc") ./.)
(fileset.fileFilter (file: file.hasExt "hh") ./.)
(fileset.fileFilter (file: file.hasExt "zig") ./.)
];

buildInputs = [
({ inherit editline readline; }.${readlineFlavor})
(
{
inherit editline readline;
zig = null;
}
.${readlineFlavor}
)
] ++ lib.optional enableMarkdown lowdown;

propagatedBuildInputs = [
Expand Down
14 changes: 12 additions & 2 deletions src/libcmd/repl-interacter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
#include <readline/history.h>
#include <readline/readline.h>
#else
#ifdef USE_ZIG_REPL
extern "C" char* readline(const char* prompt);
#else
// editline < 1.15.2 don't wrap their API for C++ usage
// (added in https://github.com/troglobit/editline/commit/91398ceb3427b730995357e9d120539fb9bb7461).
// This results in linker errors due to to name-mangling of editline C symbols.
Expand All @@ -13,6 +16,7 @@ extern "C" {
#include <editline.h>
}
#endif
#endif

#include "signals.hh"
#include "finally.hh"
Expand Down Expand Up @@ -107,20 +111,24 @@ static int listPossibleCallback(char * s, char *** avp)
ReadlineLikeInteracter::Guard ReadlineLikeInteracter::init(detail::ReplCompleterMixin * repl)
{
// Allow nix-repl specific settings in .inputrc
#ifndef USE_ZIG_REPL
rl_readline_name = "nix-repl";
#endif
try {
createDirs(dirOf(historyFile));
} catch (SystemError & e) {
logWarning(e.info());
}
#ifndef USE_READLINE
#if !defined(USE_READLINE) && !defined(USE_ZIG_REPL)
el_hist_size = 1000;
#endif
#ifndef USE_ZIG_REPL
read_history(historyFile.c_str());
#endif
auto oldRepl = curRepl;
curRepl = repl;
Guard restoreRepl([oldRepl] { curRepl = oldRepl; });
#ifndef USE_READLINE
#if !defined(USE_READLINE) && !defined(USE_ZIG_REPL)
rl_set_complete_func(completionCallback);
rl_set_list_possib_func(listPossibleCallback);
#endif
Expand Down Expand Up @@ -200,7 +208,9 @@ bool ReadlineLikeInteracter::getLine(std::string & input, ReplPromptType promptT

ReadlineLikeInteracter::~ReadlineLikeInteracter()
{
#ifndef USE_ZIG_REPL
write_history(historyFile.c_str());
#endif
}

};
31 changes: 31 additions & 0 deletions src/libcmd/repl.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
const std = @import("std");

var has_prompt = true;

pub fn readline(prompt: [*:0]const u8) callconv(.C) ?[*:0]const u8 {
const stdout = std.io.getStdOut();
if (!has_prompt) {
stdout.writeAll(prompt[0..std.mem.len(prompt)]) catch @panic("Failed to write prompt");
has_prompt = true;
}

const stdin = std.io.getStdIn();

var line = std.ArrayList(u8).init(std.heap.c_allocator);
defer line.deinit();

stdin.reader().readUntilDelimiterArrayList(&line, '\n', std.math.maxInt(usize)) catch |err| switch (err) {
error.StreamTooLong => {
std.log.err("readline(\"{s}\") failed {}", .{ prompt[0..std.mem.len(prompt)], err });
return null;
},
else => return null,
};

has_prompt = line.items.len > 0;
return line.toOwnedSliceSentinel(0) catch null;
}

comptime {
@export(&readline, .{ .name = "readline" });
}
Loading