Merge 103babecc2 into 4df593bd24
commit
80ff2d834a
|
|
@ -2807,7 +2807,10 @@ keybind: Keybinds = .{},
|
||||||
///
|
///
|
||||||
/// * `detect` - Detect the shell based on the filename.
|
/// * `detect` - Detect the shell based on the filename.
|
||||||
///
|
///
|
||||||
/// * `bash`, `elvish`, `fish`, `nushell`, `zsh` - Use this specific shell injection scheme.
|
/// * `bash`, `elvish`, `fish`, `nushell`, `zsh` - Use this specific shell
|
||||||
|
/// injection scheme if the command is this shell. Has no effect if the
|
||||||
|
/// command is not a shell, or is a different shell from the one specified
|
||||||
|
/// here.
|
||||||
///
|
///
|
||||||
/// The default value is `detect`.
|
/// The default value is `detect`.
|
||||||
@"shell-integration": ShellIntegration = .detect,
|
@"shell-integration": ShellIntegration = .detect,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const ArenaAllocator = std.heap.ArenaAllocator;
|
const ArenaAllocator = std.heap.ArenaAllocator;
|
||||||
const formatterpkg = @import("formatter.zig");
|
const formatterpkg = @import("formatter.zig");
|
||||||
|
|
@ -197,6 +199,87 @@ pub const Command = union(enum) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Shell types we support
|
||||||
|
pub const Shell = enum {
|
||||||
|
bash,
|
||||||
|
elvish,
|
||||||
|
fish,
|
||||||
|
nushell,
|
||||||
|
zsh,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Detect if this command is a known shell.
|
||||||
|
pub fn detectShell(self: Self) ?Shell {
|
||||||
|
var buf: [std.fs.max_path_bytes]u8 = undefined;
|
||||||
|
var fba: std.heap.FixedBufferAllocator = .init(&buf);
|
||||||
|
|
||||||
|
const arg0 = switch (self) {
|
||||||
|
.direct => |v| v[0],
|
||||||
|
.shell => |v| arg: {
|
||||||
|
var it = std.process.ArgIteratorGeneral(.{}).init(fba.allocator(), v) catch return null;
|
||||||
|
break :arg it.next() orelse return null;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const exe = std.fs.path.basename(arg0);
|
||||||
|
|
||||||
|
if (std.mem.eql(u8, "bash", exe)) {
|
||||||
|
// Apple distributes their own patched version of Bash 3.2
|
||||||
|
// on macOS that disables the ENV-based POSIX startup path.
|
||||||
|
// This means we're unable to perform our automatic shell
|
||||||
|
// integration sequence in this specific environment.
|
||||||
|
//
|
||||||
|
// If we're running "/bin/bash" on Darwin, we can assume
|
||||||
|
// we're using Apple's Bash because /bin is non-writable
|
||||||
|
// on modern macOS due to System Integrity Protection.
|
||||||
|
if (comptime builtin.target.os.tag.isDarwin()) {
|
||||||
|
if (std.mem.eql(u8, "/bin/bash", arg0)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return .bash;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std.mem.eql(u8, "elvish", exe)) return .elvish;
|
||||||
|
if (std.mem.eql(u8, "fish", exe)) return .fish;
|
||||||
|
if (std.mem.eql(u8, "nu", exe)) return .nushell;
|
||||||
|
if (std.mem.eql(u8, "zsh", exe)) return .zsh;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
test detectShell {
|
||||||
|
const testing = std.testing;
|
||||||
|
|
||||||
|
try testing.expect(detectShell(.{ .shell = "sh" }) == null);
|
||||||
|
try testing.expectEqual(.bash, detectShell(.{ .shell = "bash" }));
|
||||||
|
try testing.expectEqual(.elvish, detectShell(.{ .shell = "elvish" }));
|
||||||
|
try testing.expectEqual(.fish, detectShell(.{ .shell = "fish" }));
|
||||||
|
try testing.expectEqual(.nushell, detectShell(.{ .shell = "nu" }));
|
||||||
|
try testing.expectEqual(.zsh, detectShell(.{ .shell = "zsh" }));
|
||||||
|
|
||||||
|
if (comptime builtin.target.os.tag.isDarwin()) {
|
||||||
|
try testing.expect(detectShell(.{ .shell = "/bin/bash" }) == null);
|
||||||
|
}
|
||||||
|
|
||||||
|
try testing.expectEqual(.bash, detectShell(.{ .shell = "bash -c 'command'" }));
|
||||||
|
try testing.expectEqual(.bash, detectShell(.{ .shell = "\"/a b/bash\"" }));
|
||||||
|
|
||||||
|
try testing.expect(detectShell(.{ .direct = &.{"sh"} }) == null);
|
||||||
|
try testing.expectEqual(.bash, detectShell(.{ .direct = &.{"bash"} }));
|
||||||
|
try testing.expectEqual(.elvish, detectShell(.{ .direct = &.{"elvish"} }));
|
||||||
|
try testing.expectEqual(.fish, detectShell(.{ .direct = &.{"fish"} }));
|
||||||
|
try testing.expectEqual(.nushell, detectShell(.{ .direct = &.{"nu"} }));
|
||||||
|
try testing.expectEqual(.zsh, detectShell(.{ .direct = &.{"zsh"} }));
|
||||||
|
|
||||||
|
if (comptime builtin.target.os.tag.isDarwin()) {
|
||||||
|
try testing.expect(detectShell(.{&.{ .direct = "/bin/bash" }}) == null);
|
||||||
|
}
|
||||||
|
|
||||||
|
try testing.expectEqual(.bash, detectShell(.{ .direct = &.{ "bash", "-c", "command" } }));
|
||||||
|
try testing.expectEqual(.bash, detectShell(.{ .direct = &.{"/a b/bash"} }));
|
||||||
|
}
|
||||||
|
|
||||||
test "Command: parseCLI errors" {
|
test "Command: parseCLI errors" {
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
var arena = ArenaAllocator.init(testing.allocator);
|
var arena = ArenaAllocator.init(testing.allocator);
|
||||||
|
|
|
||||||
|
|
@ -10,13 +10,7 @@ const internal_os = @import("../os/main.zig");
|
||||||
const log = std.log.scoped(.shell_integration);
|
const log = std.log.scoped(.shell_integration);
|
||||||
|
|
||||||
/// Shell types we support
|
/// Shell types we support
|
||||||
pub const Shell = enum {
|
pub const Shell = config.Command.Shell;
|
||||||
bash,
|
|
||||||
elvish,
|
|
||||||
fish,
|
|
||||||
nushell,
|
|
||||||
zsh,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// The result of setting up a shell integration.
|
/// The result of setting up a shell integration.
|
||||||
pub const ShellIntegration = struct {
|
pub const ShellIntegration = struct {
|
||||||
|
|
@ -46,10 +40,21 @@ pub fn setup(
|
||||||
env: *EnvMap,
|
env: *EnvMap,
|
||||||
force_shell: ?Shell,
|
force_shell: ?Shell,
|
||||||
) !?ShellIntegration {
|
) !?ShellIntegration {
|
||||||
|
const actual_shell = command.detectShell();
|
||||||
const shell: Shell = force_shell orelse
|
const shell: Shell = force_shell orelse
|
||||||
try detectShell(alloc_arena, command) orelse
|
actual_shell orelse
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
// Don't do any shell integration if we're not actually running the shell.
|
||||||
|
// This prevents problems when the command is overridden by `+new-window`
|
||||||
|
// or some other mechanism and `shell-integration` is forced to a specific
|
||||||
|
// shell rather than being detected.
|
||||||
|
//
|
||||||
|
// This means that `shell-integration=<shell>` has no effect if we detect
|
||||||
|
// that the command is not a shell, or is not the shell specified by
|
||||||
|
// `shell-integration`.
|
||||||
|
if (shell != actual_shell) return null;
|
||||||
|
|
||||||
const new_command: config.Command = switch (shell) {
|
const new_command: config.Command = switch (shell) {
|
||||||
.bash => try setupBash(
|
.bash => try setupBash(
|
||||||
alloc_arena,
|
alloc_arena,
|
||||||
|
|
@ -107,7 +112,7 @@ test "force shell" {
|
||||||
&env,
|
&env,
|
||||||
shell,
|
shell,
|
||||||
);
|
);
|
||||||
try testing.expectEqual(shell, result.?.shell);
|
try testing.expect(result == null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -133,57 +138,6 @@ test "shell integration failure" {
|
||||||
try testing.expectEqual(0, env.count());
|
try testing.expectEqual(0, env.count());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn detectShell(alloc: Allocator, command: config.Command) !?Shell {
|
|
||||||
var arg_iter = try command.argIterator(alloc);
|
|
||||||
defer arg_iter.deinit();
|
|
||||||
|
|
||||||
const arg0 = arg_iter.next() orelse return null;
|
|
||||||
const exe = std.fs.path.basename(arg0);
|
|
||||||
|
|
||||||
if (std.mem.eql(u8, "bash", exe)) {
|
|
||||||
// Apple distributes their own patched version of Bash 3.2
|
|
||||||
// on macOS that disables the ENV-based POSIX startup path.
|
|
||||||
// This means we're unable to perform our automatic shell
|
|
||||||
// integration sequence in this specific environment.
|
|
||||||
//
|
|
||||||
// If we're running "/bin/bash" on Darwin, we can assume
|
|
||||||
// we're using Apple's Bash because /bin is non-writable
|
|
||||||
// on modern macOS due to System Integrity Protection.
|
|
||||||
if (comptime builtin.target.os.tag.isDarwin()) {
|
|
||||||
if (std.mem.eql(u8, "/bin/bash", arg0)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return .bash;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (std.mem.eql(u8, "elvish", exe)) return .elvish;
|
|
||||||
if (std.mem.eql(u8, "fish", exe)) return .fish;
|
|
||||||
if (std.mem.eql(u8, "nu", exe)) return .nushell;
|
|
||||||
if (std.mem.eql(u8, "zsh", exe)) return .zsh;
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
test detectShell {
|
|
||||||
const testing = std.testing;
|
|
||||||
const alloc = testing.allocator;
|
|
||||||
|
|
||||||
try testing.expect(try detectShell(alloc, .{ .shell = "sh" }) == null);
|
|
||||||
try testing.expectEqual(.bash, try detectShell(alloc, .{ .shell = "bash" }));
|
|
||||||
try testing.expectEqual(.elvish, try detectShell(alloc, .{ .shell = "elvish" }));
|
|
||||||
try testing.expectEqual(.fish, try detectShell(alloc, .{ .shell = "fish" }));
|
|
||||||
try testing.expectEqual(.nushell, try detectShell(alloc, .{ .shell = "nu" }));
|
|
||||||
try testing.expectEqual(.zsh, try detectShell(alloc, .{ .shell = "zsh" }));
|
|
||||||
|
|
||||||
if (comptime builtin.target.os.tag.isDarwin()) {
|
|
||||||
try testing.expect(try detectShell(alloc, .{ .shell = "/bin/bash" }) == null);
|
|
||||||
}
|
|
||||||
|
|
||||||
try testing.expectEqual(.bash, try detectShell(alloc, .{ .shell = "bash -c 'command'" }));
|
|
||||||
try testing.expectEqual(.bash, try detectShell(alloc, .{ .shell = "\"/a b/bash\"" }));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set up the shell integration features environment variable.
|
/// Set up the shell integration features environment variable.
|
||||||
pub fn setupFeatures(
|
pub fn setupFeatures(
|
||||||
env: *EnvMap,
|
env: *EnvMap,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue