mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-07-27 11:06:31 +00:00
build: generate various resources at build run, not build graph
This is stomping towards minimizing our build.zig dependencies so that it can be cached more often. Right now, touching almost any file in the project forces the build.zig to rebuild which is destroying my productivity.
This commit is contained in:
338
src/extra/bash.zig
Normal file
338
src/extra/bash.zig
Normal file
@@ -0,0 +1,338 @@
|
||||
const std = @import("std");
|
||||
|
||||
const Config = @import("../config/Config.zig");
|
||||
const Action = @import("../cli.zig").ghostty.Action;
|
||||
|
||||
/// A bash completions configuration that contains all the available commands
|
||||
/// and options.
|
||||
///
|
||||
/// Notes: bash completion support for --<key>=<value> depends on setting the completion
|
||||
/// system to _not_ print a space following each successful completion (see -o nospace).
|
||||
/// This results leading or tailing spaces being necessary to move onto the next match.
|
||||
///
|
||||
/// bash completion will read = as it's own completiong word regardless of whether or not
|
||||
/// it's part of an on going completion like --<key>=. Working around this requires looking
|
||||
/// backward in the command line args to pretend the = is an empty string
|
||||
/// see: https://www.gnu.org/software/gnuastro/manual/html_node/Bash-TAB-completion-tutorial.html
|
||||
pub const completions = comptimeGenerateBashCompletions();
|
||||
|
||||
fn comptimeGenerateBashCompletions() []const u8 {
|
||||
comptime {
|
||||
@setEvalBranchQuota(50000);
|
||||
var counter = std.io.countingWriter(std.io.null_writer);
|
||||
try writeBashCompletions(&counter.writer());
|
||||
|
||||
var buf: [counter.bytes_written]u8 = undefined;
|
||||
var stream = std.io.fixedBufferStream(&buf);
|
||||
try writeBashCompletions(stream.writer());
|
||||
const final = buf;
|
||||
return final[0..stream.getWritten().len];
|
||||
}
|
||||
}
|
||||
|
||||
fn writeBashCompletions(writer: anytype) !void {
|
||||
const pad1 = " ";
|
||||
const pad2 = pad1 ++ pad1;
|
||||
const pad3 = pad2 ++ pad1;
|
||||
const pad4 = pad3 ++ pad1;
|
||||
const pad5 = pad4 ++ pad1;
|
||||
|
||||
try writer.writeAll(
|
||||
\\_ghostty() {
|
||||
\\
|
||||
\\ # -o nospace requires we add back a space when a completion is finished
|
||||
\\ # and not part of a --key= completion
|
||||
\\ _add_spaces() {
|
||||
\\ for idx in "${!COMPREPLY[@]}"; do
|
||||
\\ [ -n "${COMPREPLY[idx]}" ] && COMPREPLY[idx]="${COMPREPLY[idx]} ";
|
||||
\\ done
|
||||
\\ }
|
||||
\\
|
||||
\\ _fonts() {
|
||||
\\ local IFS=$'\n'
|
||||
\\ mapfile -t COMPREPLY < <( compgen -P '"' -S '"' -W "$($ghostty +list-fonts | grep '^[A-Z]' )" -- "$cur")
|
||||
\\ }
|
||||
\\
|
||||
\\ _themes() {
|
||||
\\ local IFS=$'\n'
|
||||
\\ mapfile -t COMPREPLY < <( compgen -P '"' -S '"' -W "$($ghostty +list-themes | sed -E 's/^(.*) \(.*$/\1/')" -- "$cur")
|
||||
\\ }
|
||||
\\
|
||||
\\ _files() {
|
||||
\\ mapfile -t COMPREPLY < <( compgen -o filenames -f -- "$cur" )
|
||||
\\ for i in "${!COMPREPLY[@]}"; do
|
||||
\\ if [[ -d "${COMPREPLY[i]}" ]]; then
|
||||
\\ COMPREPLY[i]="${COMPREPLY[i]}/";
|
||||
\\ fi
|
||||
\\ if [[ -f "${COMPREPLY[i]}" ]]; then
|
||||
\\ COMPREPLY[i]="${COMPREPLY[i]} ";
|
||||
\\ fi
|
||||
\\ done
|
||||
\\ }
|
||||
\\
|
||||
\\ _dirs() {
|
||||
\\ mapfile -t COMPREPLY < <( compgen -o dirnames -d -- "$cur" )
|
||||
\\ for i in "${!COMPREPLY[@]}"; do
|
||||
\\ if [[ -d "${COMPREPLY[i]}" ]]; then
|
||||
\\ COMPREPLY[i]="${COMPREPLY[i]}/";
|
||||
\\ fi
|
||||
\\ done
|
||||
\\ if [[ "${#COMPREPLY[@]}" == 0 && -d "$cur" ]]; then
|
||||
\\ COMPREPLY=( "$cur " )
|
||||
\\ fi
|
||||
\\ }
|
||||
\\
|
||||
\\ _handle_config() {
|
||||
\\ local config="--help"
|
||||
\\ config+=" --version"
|
||||
\\
|
||||
);
|
||||
|
||||
for (@typeInfo(Config).@"struct".fields) |field| {
|
||||
if (field.name[0] == '_') continue;
|
||||
switch (field.type) {
|
||||
bool, ?bool => try writer.writeAll(pad2 ++ "config+=\" '--" ++ field.name ++ " '\"\n"),
|
||||
else => try writer.writeAll(pad2 ++ "config+=\" --" ++ field.name ++ "=\"\n"),
|
||||
}
|
||||
}
|
||||
|
||||
try writer.writeAll(
|
||||
\\
|
||||
\\ case "$prev" in
|
||||
\\
|
||||
);
|
||||
|
||||
for (@typeInfo(Config).@"struct".fields) |field| {
|
||||
if (field.name[0] == '_') continue;
|
||||
try writer.writeAll(pad3 ++ "--" ++ field.name ++ ") ");
|
||||
|
||||
if (std.mem.startsWith(u8, field.name, "font-family"))
|
||||
try writer.writeAll("_fonts ;;")
|
||||
else if (std.mem.eql(u8, "theme", field.name))
|
||||
try writer.writeAll("_themes ;;")
|
||||
else if (std.mem.eql(u8, "working-directory", field.name))
|
||||
try writer.writeAll("_dirs ;;")
|
||||
else if (field.type == Config.RepeatablePath)
|
||||
try writer.writeAll("_files ;;")
|
||||
else {
|
||||
const compgenPrefix = "mapfile -t COMPREPLY < <( compgen -W \"";
|
||||
const compgenSuffix = "\" -- \"$cur\" ); _add_spaces ;;";
|
||||
switch (@typeInfo(field.type)) {
|
||||
.bool => try writer.writeAll("return ;;"),
|
||||
.@"enum" => |info| {
|
||||
try writer.writeAll(compgenPrefix);
|
||||
for (info.fields, 0..) |f, i| {
|
||||
if (i > 0) try writer.writeAll(" ");
|
||||
try writer.writeAll(f.name);
|
||||
}
|
||||
try writer.writeAll(compgenSuffix);
|
||||
},
|
||||
.@"struct" => |info| {
|
||||
if (!@hasDecl(field.type, "parseCLI") and info.layout == .@"packed") {
|
||||
try writer.writeAll(compgenPrefix);
|
||||
for (info.fields, 0..) |f, i| {
|
||||
if (i > 0) try writer.writeAll(" ");
|
||||
try writer.writeAll(f.name ++ " no-" ++ f.name);
|
||||
}
|
||||
try writer.writeAll(compgenSuffix);
|
||||
} else {
|
||||
try writer.writeAll("return ;;");
|
||||
}
|
||||
},
|
||||
else => try writer.writeAll("return ;;"),
|
||||
}
|
||||
}
|
||||
|
||||
try writer.writeAll("\n");
|
||||
}
|
||||
|
||||
try writer.writeAll(
|
||||
\\ *) mapfile -t COMPREPLY < <( compgen -W "$config" -- "$cur" ) ;;
|
||||
\\ esac
|
||||
\\
|
||||
\\ return 0
|
||||
\\ }
|
||||
\\
|
||||
\\ _handle_actions() {
|
||||
\\
|
||||
);
|
||||
|
||||
for (@typeInfo(Action).@"enum".fields) |field| {
|
||||
if (std.mem.eql(u8, "help", field.name)) continue;
|
||||
if (std.mem.eql(u8, "version", field.name)) continue;
|
||||
|
||||
const options = @field(Action, field.name).options();
|
||||
// assumes options will never be created with only <_name> members
|
||||
if (@typeInfo(options).@"struct".fields.len == 0) continue;
|
||||
|
||||
var buffer: [field.name.len]u8 = undefined;
|
||||
const bashName: []u8 = buffer[0..field.name.len];
|
||||
@memcpy(bashName, field.name);
|
||||
|
||||
std.mem.replaceScalar(u8, bashName, '-', '_');
|
||||
try writer.writeAll(pad2 ++ "local " ++ bashName ++ "=\"");
|
||||
|
||||
{
|
||||
var count = 0;
|
||||
for (@typeInfo(options).@"struct".fields) |opt| {
|
||||
if (opt.name[0] == '_') continue;
|
||||
if (count > 0) try writer.writeAll(" ");
|
||||
switch (opt.type) {
|
||||
bool, ?bool => try writer.writeAll("'--" ++ opt.name ++ " '"),
|
||||
else => try writer.writeAll("--" ++ opt.name ++ "="),
|
||||
}
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
try writer.writeAll(" --help\"\n");
|
||||
}
|
||||
|
||||
try writer.writeAll(
|
||||
\\
|
||||
\\ case "${COMP_WORDS[1]}" in
|
||||
\\
|
||||
);
|
||||
|
||||
for (@typeInfo(Action).@"enum".fields) |field| {
|
||||
if (std.mem.eql(u8, "help", field.name)) continue;
|
||||
if (std.mem.eql(u8, "version", field.name)) continue;
|
||||
|
||||
const options = @field(Action, field.name).options();
|
||||
if (@typeInfo(options).@"struct".fields.len == 0) continue;
|
||||
|
||||
// bash doesn't allow variable names containing '-' so replace them
|
||||
var buffer: [field.name.len]u8 = undefined;
|
||||
const bashName: []u8 = buffer[0..field.name.len];
|
||||
_ = std.mem.replace(u8, field.name, "-", "_", bashName);
|
||||
|
||||
try writer.writeAll(pad3 ++ "+" ++ field.name ++ ")\n");
|
||||
try writer.writeAll(pad4 ++ "case $prev in\n");
|
||||
for (@typeInfo(options).@"struct".fields) |opt| {
|
||||
if (opt.name[0] == '_') continue;
|
||||
|
||||
try writer.writeAll(pad5 ++ "--" ++ opt.name ++ ") ");
|
||||
|
||||
const compgenPrefix = "mapfile -t COMPREPLY < <( compgen -W \"";
|
||||
const compgenSuffix = "\" -- \"$cur\" ); _add_spaces ;;";
|
||||
switch (@typeInfo(opt.type)) {
|
||||
.bool => try writer.writeAll("return ;;"),
|
||||
.@"enum" => |info| {
|
||||
try writer.writeAll(compgenPrefix);
|
||||
for (info.fields, 0..) |f, i| {
|
||||
if (i > 0) try writer.writeAll(" ");
|
||||
try writer.writeAll(f.name);
|
||||
}
|
||||
try writer.writeAll(compgenSuffix);
|
||||
},
|
||||
.optional => |optional| {
|
||||
switch (@typeInfo(optional.child)) {
|
||||
.@"enum" => |info| {
|
||||
try writer.writeAll(compgenPrefix);
|
||||
for (info.fields, 0..) |f, i| {
|
||||
if (i > 0) try writer.writeAll(" ");
|
||||
try writer.writeAll(f.name);
|
||||
}
|
||||
try writer.writeAll(compgenSuffix);
|
||||
},
|
||||
else => {
|
||||
if (std.mem.eql(u8, "config-file", opt.name)) {
|
||||
try writer.writeAll("return ;;");
|
||||
} else try writer.writeAll("return;;");
|
||||
},
|
||||
}
|
||||
},
|
||||
else => {
|
||||
if (std.mem.eql(u8, "config-file", opt.name)) {
|
||||
try writer.writeAll("_files ;;");
|
||||
} else try writer.writeAll("return;;");
|
||||
},
|
||||
}
|
||||
try writer.writeAll("\n");
|
||||
}
|
||||
try writer.writeAll(pad5 ++ "*) mapfile -t COMPREPLY < <( compgen -W \"$" ++ bashName ++ "\" -- \"$cur\" ) ;;\n");
|
||||
try writer.writeAll(
|
||||
\\ esac
|
||||
\\ ;;
|
||||
\\
|
||||
);
|
||||
}
|
||||
|
||||
try writer.writeAll(
|
||||
\\ *) mapfile -t COMPREPLY < <( compgen -W "--help" -- "$cur" ) ;;
|
||||
\\ esac
|
||||
\\
|
||||
\\ return 0
|
||||
\\ }
|
||||
\\
|
||||
\\ # begin main logic
|
||||
\\ local topLevel="-e"
|
||||
\\ topLevel+=" --help"
|
||||
\\ topLevel+=" --version"
|
||||
\\
|
||||
);
|
||||
|
||||
for (@typeInfo(Action).@"enum".fields) |field| {
|
||||
if (std.mem.eql(u8, "help", field.name)) continue;
|
||||
if (std.mem.eql(u8, "version", field.name)) continue;
|
||||
|
||||
try writer.writeAll(pad1 ++ "topLevel+=\" +" ++ field.name ++ "\"\n");
|
||||
}
|
||||
|
||||
try writer.writeAll(
|
||||
\\
|
||||
\\ local cur=""; local prev=""; local prevWasEq=false; COMPREPLY=()
|
||||
\\ local ghostty="$1"
|
||||
\\
|
||||
\\ # script assumes default COMP_WORDBREAKS of roughly $' \t\n"\'><=;|&(:'
|
||||
\\ # if = is missing this script will degrade to matching on keys only.
|
||||
\\ # eg: --key=
|
||||
\\ # this can be improved if needed see: https://github.com/ghostty-org/ghostty/discussions/2994
|
||||
\\
|
||||
\\ if [ "$2" = "=" ]; then cur=""
|
||||
\\ else cur="$2"
|
||||
\\ fi
|
||||
\\
|
||||
\\ if [ "$3" = "=" ]; then prev="${COMP_WORDS[COMP_CWORD-2]}"; prevWasEq=true;
|
||||
\\ else prev="${COMP_WORDS[COMP_CWORD-1]}"
|
||||
\\ fi
|
||||
\\
|
||||
\\ # current completion is double quoted add a space so the curor progresses
|
||||
\\ if [[ "$2" == \"*\" ]]; then
|
||||
\\ COMPREPLY=( "$cur " );
|
||||
\\ return;
|
||||
\\ fi
|
||||
\\
|
||||
\\ case "$COMP_CWORD" in
|
||||
\\ 1)
|
||||
\\ case "${COMP_WORDS[1]}" in
|
||||
\\ -e | --help | --version) return 0 ;;
|
||||
\\ --*) _handle_config ;;
|
||||
\\ *) mapfile -t COMPREPLY < <( compgen -W "${topLevel}" -- "$cur" ); _add_spaces ;;
|
||||
\\ esac
|
||||
\\ ;;
|
||||
\\ *)
|
||||
\\ case "$prev" in
|
||||
\\ -e | --help | --version) return 0 ;;
|
||||
\\ *)
|
||||
\\ if [[ "=" != "${COMP_WORDS[COMP_CWORD]}" && $prevWasEq != true ]]; then
|
||||
\\ # must be completing with a space after the key eg: '--<key> '
|
||||
\\ # clear out prev so we don't run any of the key specific completions
|
||||
\\ prev=""
|
||||
\\ fi
|
||||
\\
|
||||
\\ case "${COMP_WORDS[1]}" in
|
||||
\\ --*) _handle_config ;;
|
||||
\\ +*) _handle_actions ;;
|
||||
\\ esac
|
||||
\\ ;;
|
||||
\\ esac
|
||||
\\ ;;
|
||||
\\ esac
|
||||
\\
|
||||
\\ return 0
|
||||
\\}
|
||||
\\
|
||||
\\complete -o nospace -o bashdefault -F _ghostty ghostty
|
||||
\\
|
||||
);
|
||||
}
|
||||
145
src/extra/fish.zig
Normal file
145
src/extra/fish.zig
Normal file
@@ -0,0 +1,145 @@
|
||||
//! Fish completions.
|
||||
const std = @import("std");
|
||||
|
||||
const Config = @import("../config/Config.zig");
|
||||
const Action = @import("../cli.zig").ghostty.Action;
|
||||
|
||||
/// A fish completions configuration that contains all the available commands
|
||||
/// and options.
|
||||
pub const completions = comptimeGenerateCompletions();
|
||||
|
||||
fn comptimeGenerateCompletions() []const u8 {
|
||||
comptime {
|
||||
@setEvalBranchQuota(50000);
|
||||
var counter = std.io.countingWriter(std.io.null_writer);
|
||||
try writeCompletions(&counter.writer());
|
||||
|
||||
var buf: [counter.bytes_written]u8 = undefined;
|
||||
var stream = std.io.fixedBufferStream(&buf);
|
||||
try writeCompletions(stream.writer());
|
||||
const final = buf;
|
||||
return final[0..stream.getWritten().len];
|
||||
}
|
||||
}
|
||||
|
||||
fn writeCompletions(writer: anytype) !void {
|
||||
{
|
||||
try writer.writeAll("set -l commands \"");
|
||||
var count: usize = 0;
|
||||
for (@typeInfo(Action).@"enum".fields) |field| {
|
||||
if (std.mem.eql(u8, "help", field.name)) continue;
|
||||
if (std.mem.eql(u8, "version", field.name)) continue;
|
||||
if (count > 0) try writer.writeAll(" ");
|
||||
try writer.writeAll("+");
|
||||
try writer.writeAll(field.name);
|
||||
count += 1;
|
||||
}
|
||||
try writer.writeAll("\"\n");
|
||||
}
|
||||
|
||||
try writer.writeAll("complete -c ghostty -f\n");
|
||||
|
||||
try writer.writeAll("complete -c ghostty -s e -l help -f\n");
|
||||
try writer.writeAll("complete -c ghostty -n \"not __fish_seen_subcommand_from $commands\" -l version -f\n");
|
||||
|
||||
for (@typeInfo(Config).@"struct".fields) |field| {
|
||||
if (field.name[0] == '_') continue;
|
||||
|
||||
try writer.writeAll("complete -c ghostty -n \"not __fish_seen_subcommand_from $commands\" -l ");
|
||||
try writer.writeAll(field.name);
|
||||
try writer.writeAll(if (field.type != bool) " -r" else " ");
|
||||
if (std.mem.startsWith(u8, field.name, "font-family"))
|
||||
try writer.writeAll(" -f -a \"(ghostty +list-fonts | grep '^[A-Z]')\"")
|
||||
else if (std.mem.eql(u8, "theme", field.name))
|
||||
try writer.writeAll(" -f -a \"(ghostty +list-themes | sed -E 's/^(.*) \\(.*\\$/\\1/')\"")
|
||||
else if (std.mem.eql(u8, "working-directory", field.name))
|
||||
try writer.writeAll(" -f -k -a \"(__fish_complete_directories)\"")
|
||||
else {
|
||||
try writer.writeAll(if (field.type != Config.RepeatablePath) " -f" else " -F");
|
||||
switch (@typeInfo(field.type)) {
|
||||
.bool => {},
|
||||
.@"enum" => |info| {
|
||||
try writer.writeAll(" -a \"");
|
||||
for (info.fields, 0..) |f, i| {
|
||||
if (i > 0) try writer.writeAll(" ");
|
||||
try writer.writeAll(f.name);
|
||||
}
|
||||
try writer.writeAll("\"");
|
||||
},
|
||||
.@"struct" => |info| {
|
||||
if (!@hasDecl(field.type, "parseCLI") and info.layout == .@"packed") {
|
||||
try writer.writeAll(" -a \"");
|
||||
for (info.fields, 0..) |f, i| {
|
||||
if (i > 0) try writer.writeAll(" ");
|
||||
try writer.writeAll(f.name);
|
||||
try writer.writeAll(" no-");
|
||||
try writer.writeAll(f.name);
|
||||
}
|
||||
try writer.writeAll("\"");
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
try writer.writeAll("\n");
|
||||
}
|
||||
|
||||
{
|
||||
try writer.writeAll("complete -c ghostty -n \"string match -q -- '+*' (commandline -pt)\" -f -a \"");
|
||||
var count: usize = 0;
|
||||
for (@typeInfo(Action).@"enum".fields) |field| {
|
||||
if (std.mem.eql(u8, "help", field.name)) continue;
|
||||
if (std.mem.eql(u8, "version", field.name)) continue;
|
||||
if (count > 0) try writer.writeAll(" ");
|
||||
try writer.writeAll("+");
|
||||
try writer.writeAll(field.name);
|
||||
count += 1;
|
||||
}
|
||||
try writer.writeAll("\"\n");
|
||||
}
|
||||
|
||||
for (@typeInfo(Action).@"enum".fields) |field| {
|
||||
if (std.mem.eql(u8, "help", field.name)) continue;
|
||||
if (std.mem.eql(u8, "version", field.name)) continue;
|
||||
|
||||
const options = @field(Action, field.name).options();
|
||||
for (@typeInfo(options).@"struct".fields) |opt| {
|
||||
if (opt.name[0] == '_') continue;
|
||||
try writer.writeAll("complete -c ghostty -n \"__fish_seen_subcommand_from +" ++ field.name ++ "\" -l ");
|
||||
try writer.writeAll(opt.name);
|
||||
try writer.writeAll(if (opt.type != bool) " -r" else "");
|
||||
|
||||
// special case +validate_config --config-file
|
||||
if (std.mem.eql(u8, "config-file", opt.name)) {
|
||||
try writer.writeAll(" -F");
|
||||
} else try writer.writeAll(" -f");
|
||||
|
||||
switch (@typeInfo(opt.type)) {
|
||||
.bool => {},
|
||||
.@"enum" => |info| {
|
||||
try writer.writeAll(" -a \"");
|
||||
for (info.fields, 0..) |f, i| {
|
||||
if (i > 0) try writer.writeAll(" ");
|
||||
try writer.writeAll(f.name);
|
||||
}
|
||||
try writer.writeAll("\"");
|
||||
},
|
||||
.optional => |optional| {
|
||||
switch (@typeInfo(optional.child)) {
|
||||
.@"enum" => |info| {
|
||||
try writer.writeAll(" -a \"");
|
||||
for (info.fields, 0..) |f, i| {
|
||||
if (i > 0) try writer.writeAll(" ");
|
||||
try writer.writeAll(f.name);
|
||||
}
|
||||
try writer.writeAll("\"");
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
try writer.writeAll("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
51
src/extra/sublime.zig
Normal file
51
src/extra/sublime.zig
Normal file
@@ -0,0 +1,51 @@
|
||||
const std = @import("std");
|
||||
const Config = @import("../config/Config.zig");
|
||||
|
||||
const Template = struct {
|
||||
const header =
|
||||
\\%YAML 1.2
|
||||
\\---
|
||||
\\# See http://www.sublimetext.com/docs/syntax.html
|
||||
\\name: Ghostty Config
|
||||
\\file_extensions:
|
||||
\\ - ghostty
|
||||
\\scope: source.ghostty
|
||||
\\
|
||||
\\contexts:
|
||||
\\ main:
|
||||
\\ # Comments
|
||||
\\ - match: '^\s*#.*$'
|
||||
\\ scope: comment.line.number-sign.ghostty
|
||||
\\
|
||||
\\ # Keywords
|
||||
\\ - match: '\b(
|
||||
;
|
||||
const footer =
|
||||
\\)\b'
|
||||
\\ scope: keyword.other.ghostty
|
||||
\\
|
||||
;
|
||||
};
|
||||
|
||||
/// Check if a field is internal (starts with underscore)
|
||||
fn isInternal(name: []const u8) bool {
|
||||
return name.len > 0 and name[0] == '_';
|
||||
}
|
||||
|
||||
/// Generate keywords from Config fields
|
||||
fn generateKeywords() []const u8 {
|
||||
@setEvalBranchQuota(5000);
|
||||
var keywords: []const u8 = "";
|
||||
const config_fields = @typeInfo(Config).@"struct".fields;
|
||||
|
||||
for (config_fields) |field| {
|
||||
if (isInternal(field.name)) continue;
|
||||
if (keywords.len > 0) keywords = keywords ++ "|";
|
||||
keywords = keywords ++ field.name;
|
||||
}
|
||||
|
||||
return keywords;
|
||||
}
|
||||
|
||||
/// Complete Sublime syntax file content
|
||||
pub const syntax = Template.header ++ generateKeywords() ++ Template.footer;
|
||||
117
src/extra/vim.zig
Normal file
117
src/extra/vim.zig
Normal file
@@ -0,0 +1,117 @@
|
||||
const std = @import("std");
|
||||
const Config = @import("../config/Config.zig");
|
||||
|
||||
/// This is the associated Vim file as named by the variable.
|
||||
pub const syntax = comptimeGenSyntax();
|
||||
pub const ftdetect =
|
||||
\\" Vim filetype detect file
|
||||
\\" Language: Ghostty config file
|
||||
\\" Maintainer: Ghostty <https://github.com/ghostty-org/ghostty>
|
||||
\\"
|
||||
\\" THIS FILE IS AUTO-GENERATED
|
||||
\\
|
||||
\\au BufRead,BufNewFile */ghostty/config,*/ghostty/themes/* set ft=ghostty
|
||||
\\
|
||||
;
|
||||
pub const ftplugin =
|
||||
\\" Vim filetype plugin file
|
||||
\\" Language: Ghostty config file
|
||||
\\" Maintainer: Ghostty <https://github.com/ghostty-org/ghostty>
|
||||
\\"
|
||||
\\" THIS FILE IS AUTO-GENERATED
|
||||
\\
|
||||
\\if exists('b:did_ftplugin')
|
||||
\\ finish
|
||||
\\endif
|
||||
\\let b:did_ftplugin = 1
|
||||
\\
|
||||
\\setlocal commentstring=#\ %s
|
||||
\\setlocal iskeyword+=-
|
||||
\\
|
||||
\\" Use syntax keywords for completion
|
||||
\\setlocal omnifunc=syntaxcomplete#Complete
|
||||
\\
|
||||
\\let b:undo_ftplugin = 'setl cms< isk< ofu<'
|
||||
\\
|
||||
\\if !exists('current_compiler')
|
||||
\\ compiler ghostty
|
||||
\\ let b:undo_ftplugin .= " makeprg< errorformat<"
|
||||
\\endif
|
||||
\\
|
||||
;
|
||||
pub const compiler =
|
||||
\\" Vim compiler file
|
||||
\\" Language: Ghostty config file
|
||||
\\" Maintainer: Ghostty <https://github.com/ghostty-org/ghostty>
|
||||
\\"
|
||||
\\" THIS FILE IS AUTO-GENERATED
|
||||
\\
|
||||
\\if exists("current_compiler")
|
||||
\\ finish
|
||||
\\endif
|
||||
\\let current_compiler = "ghostty"
|
||||
\\
|
||||
\\CompilerSet makeprg=ghostty\ +validate-config\ --config-file=%:S
|
||||
\\CompilerSet errorformat=%f:%l:%m,%m
|
||||
\\
|
||||
;
|
||||
|
||||
/// Generates the syntax file at comptime.
|
||||
fn comptimeGenSyntax() []const u8 {
|
||||
comptime {
|
||||
var counting_writer = std.io.countingWriter(std.io.null_writer);
|
||||
try writeSyntax(&counting_writer.writer());
|
||||
|
||||
var buf: [counting_writer.bytes_written]u8 = undefined;
|
||||
var stream = std.io.fixedBufferStream(&buf);
|
||||
try writeSyntax(stream.writer());
|
||||
const final = buf;
|
||||
return final[0..stream.getWritten().len];
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes the syntax file to the given writer.
|
||||
fn writeSyntax(writer: anytype) !void {
|
||||
try writer.writeAll(
|
||||
\\" Vim syntax file
|
||||
\\" Language: Ghostty config file
|
||||
\\" Maintainer: Ghostty <https://github.com/ghostty-org/ghostty>
|
||||
\\"
|
||||
\\" THIS FILE IS AUTO-GENERATED
|
||||
\\
|
||||
\\if exists('b:current_syntax')
|
||||
\\ finish
|
||||
\\endif
|
||||
\\
|
||||
\\let b:current_syntax = 'ghostty'
|
||||
\\
|
||||
\\let s:cpo_save = &cpo
|
||||
\\set cpo&vim
|
||||
\\
|
||||
\\syn iskeyword @,48-57,-
|
||||
\\syn keyword ghosttyConfigKeyword
|
||||
);
|
||||
|
||||
const config_fields = @typeInfo(Config).@"struct".fields;
|
||||
inline for (config_fields) |field| {
|
||||
if (field.name[0] == '_') continue;
|
||||
try writer.print("\n\t\\ {s}", .{field.name});
|
||||
}
|
||||
|
||||
try writer.writeAll(
|
||||
\\
|
||||
\\
|
||||
\\syn match ghosttyConfigComment /^\s*#.*/ contains=@Spell
|
||||
\\
|
||||
\\hi def link ghosttyConfigComment Comment
|
||||
\\hi def link ghosttyConfigKeyword Keyword
|
||||
\\
|
||||
\\let &cpo = s:cpo_save
|
||||
\\unlet s:cpo_save
|
||||
\\
|
||||
);
|
||||
}
|
||||
|
||||
test {
|
||||
_ = syntax;
|
||||
}
|
||||
237
src/extra/zsh.zig
Normal file
237
src/extra/zsh.zig
Normal file
@@ -0,0 +1,237 @@
|
||||
const std = @import("std");
|
||||
|
||||
const Config = @import("../config/Config.zig");
|
||||
const Action = @import("../cli.zig").ghostty.Action;
|
||||
|
||||
/// A zsh completions configuration that contains all the available commands
|
||||
/// and options.
|
||||
pub const completions = comptimeGenerateZshCompletions();
|
||||
|
||||
const equals_required = "=-:::";
|
||||
|
||||
fn comptimeGenerateZshCompletions() []const u8 {
|
||||
comptime {
|
||||
@setEvalBranchQuota(50000);
|
||||
var counter = std.io.countingWriter(std.io.null_writer);
|
||||
try writeZshCompletions(&counter.writer());
|
||||
|
||||
var buf: [counter.bytes_written]u8 = undefined;
|
||||
var stream = std.io.fixedBufferStream(&buf);
|
||||
try writeZshCompletions(stream.writer());
|
||||
const final = buf;
|
||||
return final[0..stream.getWritten().len];
|
||||
}
|
||||
}
|
||||
|
||||
fn writeZshCompletions(writer: anytype) !void {
|
||||
try writer.writeAll(
|
||||
\\#compdef ghostty
|
||||
\\
|
||||
\\_fonts () {
|
||||
\\ local font_list=$(ghostty +list-fonts | grep -Z '^[A-Z]')
|
||||
\\ local fonts=(${(f)font_list})
|
||||
\\ _describe -t fonts 'fonts' fonts
|
||||
\\}
|
||||
\\
|
||||
\\_themes() {
|
||||
\\ local theme_list=$(ghostty +list-themes | sed -E 's/^(.*) \(.*$/\1/')
|
||||
\\ local themes=(${(f)theme_list})
|
||||
\\ _describe -t themes 'themes' themes
|
||||
\\}
|
||||
\\
|
||||
);
|
||||
|
||||
try writer.writeAll("_config() {\n");
|
||||
try writer.writeAll(" _arguments \\\n");
|
||||
try writer.writeAll(" \"--help\" \\\n");
|
||||
try writer.writeAll(" \"--version\" \\\n");
|
||||
for (@typeInfo(Config).@"struct".fields) |field| {
|
||||
if (field.name[0] == '_') continue;
|
||||
try writer.writeAll(" \"--");
|
||||
try writer.writeAll(field.name);
|
||||
|
||||
if (std.mem.startsWith(u8, field.name, "font-family")) {
|
||||
try writer.writeAll(equals_required);
|
||||
try writer.writeAll("_fonts");
|
||||
} else if (std.mem.eql(u8, "theme", field.name)) {
|
||||
try writer.writeAll(equals_required);
|
||||
try writer.writeAll("_themes");
|
||||
} else if (std.mem.eql(u8, "working-directory", field.name)) {
|
||||
try writer.writeAll(equals_required);
|
||||
try writer.writeAll("{_files -/}");
|
||||
} else if (field.type == Config.RepeatablePath) {
|
||||
try writer.writeAll(equals_required);
|
||||
try writer.writeAll("_files"); // todo check if this is needed
|
||||
} else {
|
||||
switch (@typeInfo(field.type)) {
|
||||
.bool => {},
|
||||
.@"enum" => |info| {
|
||||
try writer.writeAll(equals_required);
|
||||
try writer.writeAll("(");
|
||||
for (info.fields, 0..) |f, i| {
|
||||
if (i > 0) try writer.writeAll(" ");
|
||||
try writer.writeAll(f.name);
|
||||
}
|
||||
try writer.writeAll(")");
|
||||
},
|
||||
.@"struct" => |info| {
|
||||
try writer.writeAll(equals_required);
|
||||
if (!@hasDecl(field.type, "parseCLI") and info.layout == .@"packed") {
|
||||
try writer.writeAll("(");
|
||||
for (info.fields, 0..) |f, i| {
|
||||
if (i > 0) try writer.writeAll(" ");
|
||||
try writer.writeAll(f.name);
|
||||
try writer.writeAll(" no-");
|
||||
try writer.writeAll(f.name);
|
||||
}
|
||||
try writer.writeAll(")");
|
||||
} else {
|
||||
//resize-overlay-duration
|
||||
//keybind
|
||||
//window-padding-x ...-y
|
||||
//link
|
||||
//palette
|
||||
//background
|
||||
//foreground
|
||||
//font-variation*
|
||||
//font-feature
|
||||
try writer.writeAll("( )");
|
||||
}
|
||||
},
|
||||
else => {
|
||||
try writer.writeAll(equals_required);
|
||||
try writer.writeAll("( )");
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
try writer.writeAll("\" \\\n");
|
||||
}
|
||||
try writer.writeAll("\n}\n\n");
|
||||
|
||||
try writer.writeAll(
|
||||
\\_ghostty() {
|
||||
\\ typeset -A opt_args
|
||||
\\ local context state line
|
||||
\\ local opt=('-e' '--help' '--version')
|
||||
\\
|
||||
\\ _arguments -C \
|
||||
\\ '1:actions:->actions' \
|
||||
\\ '*:: :->rest' \
|
||||
\\
|
||||
\\ if [[ "$line[1]" == "--help" || "$line[1]" == "--version" || "$line[1]" == "-e" ]]; then
|
||||
\\ return
|
||||
\\ fi
|
||||
\\
|
||||
\\ if [[ "$line[1]" == -* ]]; then
|
||||
\\ _config
|
||||
\\ return
|
||||
\\ fi
|
||||
\\
|
||||
\\ case "$state" in
|
||||
\\ (actions)
|
||||
\\ local actions; actions=(
|
||||
\\
|
||||
);
|
||||
|
||||
{
|
||||
// how to get 'commands'
|
||||
var count: usize = 0;
|
||||
const padding = " ";
|
||||
for (@typeInfo(Action).@"enum".fields) |field| {
|
||||
if (std.mem.eql(u8, "help", field.name)) continue;
|
||||
if (std.mem.eql(u8, "version", field.name)) continue;
|
||||
|
||||
try writer.writeAll(padding ++ "'+");
|
||||
try writer.writeAll(field.name);
|
||||
try writer.writeAll("'\n");
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
try writer.writeAll(
|
||||
\\ )
|
||||
\\ _describe '' opt
|
||||
\\ _describe -t action 'action' actions
|
||||
\\ ;;
|
||||
\\ (rest)
|
||||
\\ if [[ "$line[2]" == "--help" ]]; then
|
||||
\\ return
|
||||
\\ fi
|
||||
\\
|
||||
\\ local help=('--help')
|
||||
\\ _describe '' help
|
||||
\\
|
||||
\\ case $line[1] in
|
||||
\\
|
||||
);
|
||||
{
|
||||
const padding = " ";
|
||||
for (@typeInfo(Action).@"enum".fields) |field| {
|
||||
if (std.mem.eql(u8, "help", field.name)) continue;
|
||||
if (std.mem.eql(u8, "version", field.name)) continue;
|
||||
|
||||
const options = @field(Action, field.name).options();
|
||||
// assumes options will never be created with only <_name> members
|
||||
if (@typeInfo(options).@"struct".fields.len == 0) continue;
|
||||
|
||||
try writer.writeAll(padding ++ "(+" ++ field.name ++ ")\n");
|
||||
try writer.writeAll(padding ++ " _arguments \\\n");
|
||||
for (@typeInfo(options).@"struct".fields) |opt| {
|
||||
if (opt.name[0] == '_') continue;
|
||||
|
||||
try writer.writeAll(padding ++ " '--");
|
||||
try writer.writeAll(opt.name);
|
||||
|
||||
switch (@typeInfo(opt.type)) {
|
||||
.bool => {},
|
||||
.@"enum" => |info| {
|
||||
try writer.writeAll(equals_required);
|
||||
try writer.writeAll("(");
|
||||
for (info.fields, 0..) |f, i| {
|
||||
if (i > 0) try writer.writeAll(" ");
|
||||
try writer.writeAll(f.name);
|
||||
}
|
||||
try writer.writeAll(")");
|
||||
},
|
||||
.optional => |optional| {
|
||||
try writer.writeAll(equals_required);
|
||||
switch (@typeInfo(optional.child)) {
|
||||
.@"enum" => |info| {
|
||||
try writer.writeAll("(");
|
||||
for (info.fields, 0..) |f, i| {
|
||||
if (i > 0) try writer.writeAll(" ");
|
||||
try writer.writeAll(f.name);
|
||||
}
|
||||
try writer.writeAll(")");
|
||||
},
|
||||
else => {
|
||||
if (std.mem.eql(u8, "config-file", opt.name)) {
|
||||
try writer.writeAll("_files");
|
||||
} else try writer.writeAll("( )");
|
||||
},
|
||||
}
|
||||
},
|
||||
else => {
|
||||
try writer.writeAll(equals_required);
|
||||
if (std.mem.eql(u8, "config-file", opt.name)) {
|
||||
try writer.writeAll("_files");
|
||||
} else try writer.writeAll("( )");
|
||||
},
|
||||
}
|
||||
|
||||
try writer.writeAll("' \\\n");
|
||||
}
|
||||
try writer.writeAll(padding ++ ";;\n");
|
||||
}
|
||||
}
|
||||
try writer.writeAll(
|
||||
\\ esac
|
||||
\\ ;;
|
||||
\\ esac
|
||||
\\}
|
||||
\\
|
||||
\\_ghostty "$@"
|
||||
\\
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user