Compare commits

...
Sign in to create a new pull request.

12 commits

14 changed files with 646 additions and 88 deletions

View file

@ -6,8 +6,8 @@
.dependencies = .{
.syntax = .{
.url = "git+https://github.com/neurocyte/flow-syntax?ref=master#56929f0c523b59153e17919be2cd09d8bef32cd0",
.hash = "flow_syntax-0.7.2-X8jOoeFTAQBeP2Tn08Tw1jsMdifLEDBgPLqPqNelAupy",
.url = "git+https://github.com/neurocyte/flow-syntax?ref=master#7b1fd3a97f00aba3a95cc65b95f34162347ed1ea",
.hash = "flow_syntax-0.7.2-X8jOoQhWAQBPt1rBRmttAGI0Z2QC-hCSZuoBZoZgr6Vv",
},
.flags = .{
.url = "git+https://github.com/neurocyte/flags?ref=main#984b27948da3e4e40a253f76c85b51ec1a9ada11",
@ -22,8 +22,8 @@
.hash = "thespian-0.0.1-owFOjlgiBgC8w4XqkCOegxz5vMy6kNErcssWQWf2QHeE",
},
.themes = .{
.url = "https://github.com/neurocyte/flow-themes/releases/download/master-c6c7f18cfb2e3945cd0b71dab24271465074dbc3/flow-themes.tar.gz",
.hash = "N-V-__8AAOKzJACguNxU76WX9M7RIhOYGuLnlasJ1-GDdhqT",
.url = "https://github.com/neurocyte/flow-themes/releases/download/master-750400d02ea8cacaabc869cd4d34dcebf04a53c8/flow-themes.tar.gz",
.hash = "N-V-__8AAEWxJQAyUV_rvRIWHB8EhIBxpQXqCB68SpilIjEt",
},
.fuzzig = .{
.url = "https://github.com/fjebaker/fuzzig/archive/4251fe4230d38e721514394a485db62ee1667ff3.tar.gz",

View file

@ -116,7 +116,7 @@ pub const make = .{};
pub const markdown = .{
.language_server = .{ "marksman", "server" },
.formatter = .{ "prettier", "--parser", "markdown" },
.formatter = .{ "prettier", "--parser", "markdown", "--prose-wrap", "always", "--print-width", "{{reflow_width}}" },
};
pub const @"markdown-inline" = .{};
@ -183,7 +183,9 @@ pub const python = .{
pub const regex = .{};
pub const rpmspec = .{};
pub const rpmspec = .{
.language_server = .{ "python3", "-mrpm_spec_language_server", "--stdio" },
};
pub const rst = .{
.language_server = .{"esbonio"},
@ -214,7 +216,10 @@ pub const verilog = .{
.formatter = .{ "verible-verilog-format", "-" },
};
pub const toml = .{};
pub const toml = .{
.language_server = .{ "tombi", "lsp" },
.formatter = .{ "tombi", "format" },
};
pub const typescript = .{
.language_server = .{ "typescript-language-server", "--stdio" },
@ -227,13 +232,21 @@ pub const typst = .{
pub const uxntal = .{};
pub const v = .{
.language_server = .{"v-analyzer"},
.formatter = .{ "v", "fmt", "-" },
};
pub const vim = .{};
pub const xml = .{
.formatter = .{ "xmllint", "--format", "-" },
};
pub const yaml = .{};
pub const yaml = .{
.language_server = .{ "yaml-language-server", "--stdio" },
.formatter = .{ "prettier", "--parser", "yaml" },
};
pub const zig = .{
.language_server = .{"zls"},

View file

@ -81,14 +81,62 @@
["dgg", "cut_buffer_begin"],
["\"_dd", "delete_line"],
["diw", "cut_inside_word"],
["di(", "cut_inside_parentheses"],
["di)", "cut_inside_parentheses"],
["di[", "cut_inside_square_brackets"],
["di]", "cut_inside_square_brackets"],
["di{", "cut_inside_braces"],
["di}", "cut_inside_braces"],
["daw", "cut_around_word"],
["da(", "cut_around_parentheses"],
["da)", "cut_around_parentheses"],
["da[", "cut_around_square_brackets"],
["da]", "cut_around_square_brackets"],
["da{", "cut_around_braces"],
["da}", "cut_around_braces"],
["cc", ["enter_mode", "insert"], ["cut_internal_vim"]],
["C", ["enter_mode", "insert"], ["cut_to_end_vim"]],
["D", "cut_to_end_vim"],
["cw", ["enter_mode", "insert"], ["cut_word_right_vim"]],
["cb", ["enter_mode", "insert"], ["cut_word_left_vim"]],
["ciw", ["enter_mode", "insert"], ["cut_inside_word"]],
["ci(", ["enter_mode", "insert"], ["cut_inside_parentheses"]],
["ci)", ["enter_mode", "insert"], ["cut_inside_parentheses"]],
["ci[", ["enter_mode", "insert"], ["cut_inside_square_brackets"]],
["ci]", ["enter_mode", "insert"], ["cut_inside_square_brackets"]],
["ci{", ["enter_mode", "insert"], ["cut_inside_braces"]],
["ci}", ["enter_mode", "insert"], ["cut_inside_braces"]],
["caw", ["enter_mode", "insert"], ["cut_around_word"]],
["ca(", ["enter_mode", "insert"], ["cut_around_parentheses"]],
["ca)", ["enter_mode", "insert"], ["cut_around_parentheses"]],
["ca[", ["enter_mode", "insert"], ["cut_around_square_brackets"]],
["ca]", ["enter_mode", "insert"], ["cut_around_square_brackets"]],
["ca{", ["enter_mode", "insert"], ["cut_around_braces"]],
["ca}", ["enter_mode", "insert"], ["cut_around_braces"]],
["yy", ["copy_line_internal_vim"], ["cancel"]],
["yiw", ["copy_inside_word"], ["cancel"]],
["yi(", ["copy_inside_parentheses"], ["cancel"]],
["yi)", ["copy_inside_parentheses"], ["cancel"]],
["yi[", ["copy_inside_square_brackets"], ["cancel"]],
["yi]", ["copy_inside_square_brackets"], ["cancel"]],
["yi{", ["copy_inside_braces"], ["cancel"]],
["yi}", ["copy_inside_braces"], ["cancel"]],
["yaw", ["copy_around_word"], ["cancel"]],
["ya(", ["copy_around_parentheses"], ["cancel"]],
["ya)", ["copy_around_parentheses"], ["cancel"]],
["ya[", ["copy_around_square_brackets"], ["cancel"]],
["ya]", ["copy_around_square_brackets"], ["cancel"]],
["ya{", ["copy_around_braces"], ["cancel"]],
["ya}", ["copy_around_braces"], ["cancel"]],
["<C-u>", "move_scroll_half_page_up_vim"],
["<C-d>", "move_scroll_half_page_down_vim"],
@ -159,6 +207,22 @@
["B", "select_word_left"],
["e", "select_word_right_end_vim"],
["iw", "select_inside_word"],
["i(", "select_inside_parentheses"],
["i)", "select_inside_parentheses"],
["i[", "select_inside_square_brackets"],
["i]", "select_inside_square_brackets"],
["i{", "select_inside_braces"],
["i}", "select_inside_braces"],
["aw", "select_around_word"],
["a(", "select_around_parentheses"],
["a)", "select_around_parentheses"],
["a[", "select_around_square_brackets"],
["a]", "select_around_square_brackets"],
["a{", "select_around_braces"],
["a}", "select_around_braces"],
["^", "smart_move_begin"],
["$", "select_end"],
[":", "open_command_palette"],

View file

@ -882,7 +882,10 @@ pub fn read_theme(allocator: std.mem.Allocator, theme_name: []const u8) ?[]const
const file_name = get_theme_file_name(theme_name) catch return null;
var file = std.fs.openFileAbsolute(file_name, .{ .mode = .read_only }) catch return null;
defer file.close();
return file.readToEndAlloc(allocator, 64 * 1024) catch null;
return file.readToEndAlloc(allocator, 512 * 1024) catch |e| {
std.log.err("Error reading theme file: {t}", .{e});
return null;
};
}
pub fn write_theme(theme_name: []const u8, content: []const u8) !void {
@ -895,15 +898,15 @@ pub fn write_theme(theme_name: []const u8, content: []const u8) !void {
pub fn list_themes(allocator: std.mem.Allocator) ![]const []const u8 {
var dir = try std.fs.openDirAbsolute(try get_theme_directory(), .{ .iterate = true });
defer dir.close();
var result = std.ArrayList([]const u8).init(allocator);
var result: std.ArrayList([]const u8) = .empty;
var iter = dir.iterateAssumeFirstIteration();
while (try iter.next()) |entry| {
switch (entry.kind) {
.file, .sym_link => try result.append(try allocator.dupe(u8, std.fs.path.stem(entry.name))),
.file, .sym_link => try result.append(allocator, try allocator.dupe(u8, std.fs.path.stem(entry.name))),
else => continue,
}
}
return result.toOwnedSlice();
return result.toOwnedSlice(allocator);
}
pub fn get_config_dir() ConfigDirError![]const u8 {

View file

@ -627,6 +627,9 @@ fn filter_mods(key_: vaxis.Key) vaxis.Key {
.shift = key_.mods.shift,
.alt = key_.mods.alt,
.ctrl = key_.mods.ctrl,
.super = key_.mods.super,
.hyper = key_.mods.hyper,
.meta = key_.mods.meta,
};
return key__;
}

View file

@ -29,6 +29,7 @@ pub const root = struct {
pub const read_theme = if (@hasDecl(hard_root, "read_theme")) hard_root.read_theme else dummy.read_theme;
pub const write_theme = if (@hasDecl(hard_root, "write_theme")) hard_root.write_theme else dummy.write_theme;
pub const list_themes = if (@hasDecl(hard_root, "list_themes")) hard_root.list_themes else dummy.list_themes;
pub const get_theme_file_name = if (@hasDecl(hard_root, "get_theme_file_name")) hard_root.get_theme_file_name else dummy.get_theme_file_name;
pub const exit = if (@hasDecl(hard_root, "exit")) hard_root.exit else dummy.exit;
@ -109,6 +110,10 @@ const dummy = struct {
pub fn write_theme(_: []const u8, _: []const u8) !void {
@panic("dummy write_theme call");
}
pub fn list_themes(_: std.mem.Allocator) ![]const []const u8 {
@panic("dummy list_themes call");
}
pub fn get_theme_file_name(_: []const u8) ![]const u8 {
@panic("dummy get_theme_file_name call");
}

View file

@ -1,6 +1,7 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const tp = @import("thespian");
const root = @import("soft_root").root;
const Plane = @import("renderer").Plane;
const EventHandler = @import("EventHandler");
@ -9,7 +10,6 @@ const tui = @import("tui.zig");
pub const Box = @import("Box.zig");
pub const Pos = struct { y: i32 = 0, x: i32 = 0 };
pub const Theme = @import("theme");
pub const themes = @import("themes").themes;
pub const scopes = @import("themes").scopes;
pub const Type = @import("config").WidgetType;
pub const StyleTag = @import("config").WidgetStyle;
@ -42,6 +42,114 @@ pub const Layout = union(enum) {
}
};
pub const ThemeInfo = struct {
name: []const u8,
storage: ?std.json.Parsed(Theme) = null,
pub fn get(self: *@This(), allocator: std.mem.Allocator) ?Theme {
if (load_theme_file(allocator, self.name) catch null) |parsed_theme| {
self.storage = parsed_theme;
return self.storage.?.value;
}
for (static_themes) |theme_| {
if (std.mem.eql(u8, theme_.name, self.name))
return theme_;
}
return null;
}
fn load_theme_file(allocator: std.mem.Allocator, theme_name: []const u8) !?std.json.Parsed(Theme) {
return load_theme_file_internal(allocator, theme_name) catch |e| {
std.log.err("Error loading theme '{s}' from file: {t}", .{ theme_name, e });
return e;
};
}
fn load_theme_file_internal(allocator: std.mem.Allocator, theme_name: []const u8) !?std.json.Parsed(Theme) {
const json_str = root.read_theme(allocator, theme_name) orelse return null;
defer allocator.free(json_str);
return try std.json.parseFromSlice(Theme, allocator, json_str, .{ .allocate = .alloc_always });
}
};
var themes_: ?std.StringHashMap(*ThemeInfo) = null;
var theme_names_: ?[]const []const u8 = null;
const static_themes = @import("themes").themes;
fn get_themes(allocator: std.mem.Allocator) *std.StringHashMap(*ThemeInfo) {
if (themes_) |*themes__| return themes__;
const theme_files = root.list_themes(allocator) catch @panic("OOM get_themes");
var themes: std.StringHashMap(*ThemeInfo) = .init(allocator);
defer allocator.free(theme_files);
for (theme_files) |file| {
const theme_info = allocator.create(ThemeInfo) catch @panic("OOM get_themes");
theme_info.* = .{
.name = file,
};
themes.put(theme_info.name, theme_info) catch @panic("OOM get_themes");
}
for (static_themes) |theme_| if (!themes.contains(theme_.name)) {
const theme_info = allocator.create(ThemeInfo) catch @panic("OOM get_themes");
theme_info.* = .{
.name = theme_.name,
};
themes.put(theme_info.name, theme_info) catch @panic("OOM get_themes");
};
themes_ = themes;
return &themes_.?;
}
fn get_theme_names() []const []const u8 {
if (theme_names_) |names_| return names_;
const themes = themes_ orelse return &.{};
var i = get_themes(themes.allocator).iterator();
var names: std.ArrayList([]const u8) = .empty;
while (i.next()) |theme_| names.append(themes.allocator, theme_.value_ptr.*.name) catch @panic("OOM get_theme_names");
std.mem.sort([]const u8, names.items, {}, struct {
fn cmp(_: void, lhs: []const u8, rhs: []const u8) bool {
return std.mem.order(u8, lhs, rhs) == .lt;
}
}.cmp);
theme_names_ = names.toOwnedSlice(themes.allocator) catch @panic("OOM get_theme_names");
return theme_names_.?;
}
pub fn get_theme_by_name(allocator: std.mem.Allocator, name_: []const u8) ?Theme {
const themes = get_themes(allocator);
const theme = themes.get(name_) orelse return null;
return theme.get(allocator);
}
pub fn get_next_theme_by_name(name_: []const u8) []const u8 {
const theme_names = get_theme_names();
var next = false;
for (theme_names) |theme_name| {
if (next)
return theme_name;
if (std.mem.eql(u8, theme_name, name_))
next = true;
}
return theme_names[0];
}
pub fn get_prev_theme_by_name(name_: []const u8) []const u8 {
const theme_names = get_theme_names();
const last = theme_names[theme_names.len - 1];
var prev: ?[]const u8 = null;
for (theme_names) |theme_name| {
if (std.mem.eql(u8, theme_name, name_))
return prev orelse last;
prev = theme_name;
}
return last;
}
pub fn list_themes() []const []const u8 {
return get_theme_names();
}
pub const VTable = struct {
deinit: *const fn (ctx: *anyopaque, allocator: Allocator) void,
send: *const fn (ctx: *anyopaque, from: tp.pid_ref, m: tp.message) error{Exit}!bool,

View file

@ -41,7 +41,7 @@ const double_click_time_ms = 350;
const syntax_full_reparse_time_limit = 0; // ms (0 = always use incremental)
const syntax_full_reparse_error_threshold = 3; // number of tree-sitter errors that trigger a full reparse
const bracket_search_radius = if (builtin.mode == std.builtin.OptimizeMode.Debug) 8_192 else 65_536;
pub const bracket_search_radius = if (builtin.mode == std.builtin.OptimizeMode.Debug) 8_192 else 65_536;
pub const max_matches = if (builtin.mode == std.builtin.OptimizeMode.Debug) 10_000 else 100_000;
pub const max_match_lines = 15;
@ -2628,7 +2628,15 @@ pub const Editor = struct {
return cursor.test_at(root, is_whitespace, metrics);
}
fn is_non_whitespace_at_cursor(root: Buffer.Root, cursor: *const Cursor, metrics: Buffer.Metrics) bool {
pub fn is_whitespace_or_eol_at_cursor(root: Buffer.Root, cursor: *const Cursor, metrics: Buffer.Metrics) bool {
return cursor.test_at(root, is_whitespace_or_eol, metrics);
}
pub fn is_non_whitespace_at_cursor(root: Buffer.Root, cursor: *const Cursor, metrics: Buffer.Metrics) bool {
return !cursor.test_at(root, is_whitespace, metrics);
}
pub fn is_non_whitespace_or_eol_at_cursor(root: Buffer.Root, cursor: *const Cursor, metrics: Buffer.Metrics) bool {
return !cursor.test_at(root, is_whitespace_or_eol, metrics);
}
@ -3745,7 +3753,7 @@ pub const Editor = struct {
}
pub fn move_cursor_right_until_non_whitespace(root: Buffer.Root, cursor: *Cursor, metrics: Buffer.Metrics) error{Stop}!void {
move_cursor_right_until(root, cursor, is_non_whitespace_at_cursor, metrics);
move_cursor_right_until(root, cursor, is_non_whitespace_or_eol_at_cursor, metrics);
}
pub fn move_word_left(self: *Self, ctx: Context) Result {

View file

@ -9,6 +9,7 @@
/// {{selections*}} - All current selections expanded to multiple quoted arguments
/// {{indent_mode}} - The current indent mode ("tabs" or "spaces")
/// {{indent_size}} - The current indent size (in columns)
/// {{reflow_width}} - The current reflow width (in columns)
/// {{blame_commit}} - The blame commit ID at the line number of the primary cursor
pub fn expand(allocator: Allocator, arg: []const u8) Error![]const u8 {
var result: std.Io.Writer.Allocating = .init(allocator);
@ -162,6 +163,15 @@ const functions = struct {
return stream.toOwnedSlice();
}
/// {{reflow_width}} - The current reflow width (in columns)
pub fn reflow_width(allocator: Allocator) Error![]const u8 {
const mv = tui.mainview() orelse return &.{};
const ed = mv.get_active_editor() orelse return &.{};
var stream: std.Io.Writer.Allocating = .init(allocator);
try stream.writer.print("{d}", .{ed.reflow_width orelse tui.config().reflow_width});
return stream.toOwnedSlice();
}
/// {{blame_commit}} - The blame commit ID at the line number of the primary cursor
pub fn blame_commit(allocator: Allocator) Error![]const u8 {
const mv = tui.mainview() orelse return &.{};

View file

@ -87,8 +87,9 @@ pub fn load_entries(self: *Type) !usize {
const less_fn = struct {
fn less_fn(_: void, lhs: Entry, rhs: Entry) bool {
const lhs_str = if (lhs.sort_text.len > 0) lhs.sort_text else lhs.label;
const rhs_str = if (rhs.sort_text.len > 0) rhs.sort_text else rhs.label;
const sort_text_equal = std.mem.eql(u8, lhs.sort_text, rhs.sort_text);
const lhs_str = if (!sort_text_equal and lhs.sort_text.len > 0) lhs.sort_text else lhs.label;
const rhs_str = if (!sort_text_equal and rhs.sort_text.len > 0) rhs.sort_text else rhs.label;
return std.mem.order(u8, lhs_str, rhs_str) == .lt;
}
}.less_fn;

View file

@ -69,8 +69,9 @@ pub fn load_entries(palette: *Type) !usize {
const less_fn = struct {
fn less_fn(_: void, lhs: Entry, rhs: Entry) bool {
const lhs_str = if (lhs.sort_text.len > 0) lhs.sort_text else lhs.label;
const rhs_str = if (rhs.sort_text.len > 0) rhs.sort_text else rhs.label;
const sort_text_equal = std.mem.eql(u8, lhs.sort_text, rhs.sort_text);
const lhs_str = if (!sort_text_equal and lhs.sort_text.len > 0) lhs.sort_text else lhs.label;
const rhs_str = if (!sort_text_equal and rhs.sort_text.len > 0) rhs.sort_text else rhs.label;
return std.mem.order(u8, lhs_str, rhs_str) == .lt;
}
}.less_fn;

View file

@ -33,7 +33,8 @@ pub fn load_entries(palette: *Type) !usize {
var longest_hint: usize = 0;
var idx: usize = 0;
try set_previous_theme(palette, tui.theme().name);
for (Widget.themes) |theme| {
for (Widget.list_themes()) |theme_name_| {
const theme = Widget.get_theme_by_name(palette.allocator, theme_name_) orelse continue;
idx += 1;
(try palette.entries.addOne(palette.allocator)).* = .{
.label = theme.description,

View file

@ -2,6 +2,14 @@ const std = @import("std");
const command = @import("command");
const cmd = command.executeName;
const tui = @import("../tui.zig");
const Buffer = @import("Buffer");
const Cursor = Buffer.Cursor;
const CurSel = @import("../editor.zig").CurSel;
const Editor = @import("../editor.zig").Editor;
const bracket_search_radius = @import("../editor.zig").bracket_search_radius;
var commands: Commands = undefined;
pub fn init() !void {
@ -138,6 +146,398 @@ const cmds_ = struct {
//TODO
return undefined;
}
pub const copy_line_meta: Meta = .{ .description = "Copies the current line" };
pub fn select_inside_word(_: *void, _: Ctx) Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
const root = ed.buf_root() catch return;
try ed.with_cursels_const(root, select_inside_word_textobject, ed.metrics);
}
pub const select_inside_word_meta: Meta = .{ .description = "Select inside word" };
pub fn select_around_word(_: *void, _: Ctx) Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
const root = ed.buf_root() catch return;
try ed.with_cursels_const(root, select_around_word_textobject, ed.metrics);
}
pub const select_around_word_meta: Meta = .{ .description = "Select around word" };
pub fn select_inside_parentheses(_: *void, _: Ctx) Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
const root = ed.buf_root() catch return;
try ed.with_cursels_const(root, select_inside_parentheses_textobject, ed.metrics);
}
pub const select_inside_parentheses_meta: Meta = .{ .description = "Select inside ()" };
pub fn select_around_parentheses(_: *void, _: Ctx) Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
const root = ed.buf_root() catch return;
try ed.with_cursels_const(root, select_around_parentheses_textobject, ed.metrics);
}
pub const select_around_parentheses_meta: Meta = .{ .description = "Select around ()" };
pub fn select_inside_square_brackets(_: *void, _: Ctx) Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
const root = ed.buf_root() catch return;
try ed.with_cursels_const(root, select_inside_square_brackets_textobject, ed.metrics);
}
pub const select_inside_square_brackets_meta: Meta = .{ .description = "Select inside []" };
pub fn select_around_square_brackets(_: *void, _: Ctx) Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
const root = ed.buf_root() catch return;
try ed.with_cursels_const(root, select_around_square_brackets_textobject, ed.metrics);
}
pub const select_around_square_brackets_meta: Meta = .{ .description = "Select around []" };
pub fn select_inside_braces(_: *void, _: Ctx) Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
const root = ed.buf_root() catch return;
try ed.with_cursels_const(root, select_inside_braces_textobject, ed.metrics);
}
pub const select_inside_braces_meta: Meta = .{ .description = "Select inside {}" };
pub fn select_around_braces(_: *void, _: Ctx) Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
const root = ed.buf_root() catch return;
try ed.with_cursels_const(root, select_around_braces_textobject, ed.metrics);
}
pub const select_around_braces_meta: Meta = .{ .description = "Select around {}" };
pub fn cut_inside_word(_: *void, ctx: Ctx) Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
const root = ed.buf_root() catch return;
try ed.with_cursels_const(root, select_inside_word_textobject, ed.metrics);
try ed.cut_internal_vim(ctx);
}
pub const cut_inside_word_meta: Meta = .{ .description = "Cut inside word" };
pub fn cut_around_word(_: *void, ctx: Ctx) Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
const root = ed.buf_root() catch return;
try ed.with_cursels_const(root, select_around_word_textobject, ed.metrics);
try ed.cut_internal_vim(ctx);
}
pub const cut_around_word_meta: Meta = .{ .description = "Cut around word" };
pub fn cut_inside_parentheses(_: *void, ctx: Ctx) Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
const root = ed.buf_root() catch return;
try ed.with_cursels_const(root, select_inside_parentheses_textobject, ed.metrics);
try ed.cut_internal_vim(ctx);
}
pub const cut_inside_parentheses_meta: Meta = .{ .description = "Cut inside ()" };
pub fn cut_around_parentheses(_: *void, ctx: Ctx) Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
const root = ed.buf_root() catch return;
try ed.with_cursels_const(root, select_around_parentheses_textobject, ed.metrics);
try ed.cut_internal_vim(ctx);
}
pub const cut_around_parentheses_meta: Meta = .{ .description = "Cut around ()" };
pub fn cut_inside_square_brackets(_: *void, ctx: Ctx) Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
const root = ed.buf_root() catch return;
try ed.with_cursels_const(root, select_inside_square_brackets_textobject, ed.metrics);
try ed.cut_internal_vim(ctx);
}
pub const cut_inside_square_brackets_meta: Meta = .{ .description = "Cut inside []" };
pub fn cut_around_square_brackets(_: *void, ctx: Ctx) Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
const root = ed.buf_root() catch return;
try ed.with_cursels_const(root, select_around_square_brackets_textobject, ed.metrics);
try ed.cut_internal_vim(ctx);
}
pub const cut_around_square_brackets_meta: Meta = .{ .description = "Cut around []" };
pub fn cut_inside_braces(_: *void, ctx: Ctx) Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
const root = ed.buf_root() catch return;
try ed.with_cursels_const(root, select_inside_braces_textobject, ed.metrics);
try ed.cut_internal_vim(ctx);
}
pub const cut_inside_braces_meta: Meta = .{ .description = "Cut inside {}" };
pub fn cut_around_braces(_: *void, ctx: Ctx) Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
const root = ed.buf_root() catch return;
try ed.with_cursels_const(root, select_around_braces_textobject, ed.metrics);
try ed.cut_internal_vim(ctx);
}
pub const cut_around_braces_meta: Meta = .{ .description = "Cut around {}" };
pub fn copy_inside_word(_: *void, ctx: Ctx) Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
const root = ed.buf_root() catch return;
try ed.with_cursels_const(root, select_inside_word_textobject, ed.metrics);
try ed.copy_internal_vim(ctx);
}
pub const copy_inside_word_meta: Meta = .{ .description = "Copy inside word" };
pub fn copy_around_word(_: *void, ctx: Ctx) Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
const root = ed.buf_root() catch return;
try ed.with_cursels_const(root, select_around_word_textobject, ed.metrics);
try ed.copy_internal_vim(ctx);
}
pub const copy_around_word_meta: Meta = .{ .description = "Copy around word" };
pub fn copy_inside_parentheses(_: *void, ctx: Ctx) Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
const root = ed.buf_root() catch return;
try ed.with_cursels_const(root, select_inside_parentheses_textobject, ed.metrics);
try ed.copy_internal_vim(ctx);
}
pub const copy_inside_parentheses_meta: Meta = .{ .description = "Copy inside ()" };
pub fn copy_around_parentheses(_: *void, ctx: Ctx) Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
const root = ed.buf_root() catch return;
try ed.with_cursels_const(root, select_around_parentheses_textobject, ed.metrics);
try ed.copy_internal_vim(ctx);
}
pub const copy_around_parentheses_meta: Meta = .{ .description = "Copy around ()" };
pub fn copy_inside_square_brackets(_: *void, ctx: Ctx) Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
const root = ed.buf_root() catch return;
try ed.with_cursels_const(root, select_inside_square_brackets_textobject, ed.metrics);
try ed.copy_internal_vim(ctx);
}
pub const copy_inside_square_brackets_meta: Meta = .{ .description = "Copy inside []" };
pub fn copy_around_square_brackets(_: *void, ctx: Ctx) Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
const root = ed.buf_root() catch return;
try ed.with_cursels_const(root, select_around_square_brackets_textobject, ed.metrics);
try ed.copy_internal_vim(ctx);
}
pub const copy_around_square_brackets_meta: Meta = .{ .description = "Copy around []" };
pub fn copy_inside_braces(_: *void, ctx: Ctx) Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
const root = ed.buf_root() catch return;
try ed.with_cursels_const(root, select_inside_braces_textobject, ed.metrics);
try ed.copy_internal_vim(ctx);
}
pub const copy_inside_braces_meta: Meta = .{ .description = "Copy inside {}" };
pub fn copy_around_braces(_: *void, ctx: Ctx) Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
const root = ed.buf_root() catch return;
try ed.with_cursels_const(root, select_around_braces_textobject, ed.metrics);
try ed.copy_internal_vim(ctx);
}
pub const copy_around_braces_meta: Meta = .{ .description = "Copy around {}" };
};
fn is_tab_or_space(c: []const u8) bool {
return (c[0] == ' ') or (c[0] == '\t');
}
fn is_tab_or_space_at_cursor(root: Buffer.Root, cursor: *const Cursor, metrics: Buffer.Metrics) bool {
return cursor.test_at(root, is_tab_or_space, metrics);
}
fn is_not_tab_or_space_at_cursor(root: Buffer.Root, cursor: *const Cursor, metrics: Buffer.Metrics) bool {
return !cursor.test_at(root, is_tab_or_space, metrics);
}
fn select_inside_word_textobject(root: Buffer.Root, cursel: *CurSel, metrics: Buffer.Metrics) !void {
return try select_word_textobject(root, cursel, metrics, .inside);
}
fn select_around_word_textobject(root: Buffer.Root, cursel: *CurSel, metrics: Buffer.Metrics) !void {
return try select_word_textobject(root, cursel, metrics, .around);
}
fn select_word_textobject(root: Buffer.Root, cursel: *CurSel, metrics: Buffer.Metrics, scope: enum { inside, around }) !void {
var prev = cursel.cursor;
var next = cursel.cursor;
if (cursel.cursor.test_at(root, Editor.is_non_word_char, metrics)) {
if (cursel.cursor.test_at(root, Editor.is_whitespace_or_eol, metrics)) {
Editor.move_cursor_left_until(root, &prev, Editor.is_non_whitespace_at_cursor, metrics);
Editor.move_cursor_right_until(root, &next, Editor.is_non_whitespace_at_cursor, metrics);
} else {
Editor.move_cursor_left_until(root, &prev, Editor.is_whitespace_or_eol_at_cursor, metrics);
Editor.move_cursor_right_until(root, &next, Editor.is_whitespace_or_eol_at_cursor, metrics);
}
prev.move_right(root, metrics) catch {};
} else {
Editor.move_cursor_left_until(root, &prev, Editor.is_word_boundary_left_vim, metrics);
Editor.move_cursor_right_until(root, &next, Editor.is_word_boundary_right_vim, metrics);
next.move_right(root, metrics) catch {};
}
if (scope == .around) {
const inside_prev = prev;
const inside_next = next;
if (next.test_at(root, is_tab_or_space, metrics)) {
Editor.move_cursor_right_until(root, &next, is_not_tab_or_space_at_cursor, metrics);
} else {
next = inside_next;
prev.move_left(root, metrics) catch {};
if (prev.test_at(root, is_tab_or_space, metrics)) {
Editor.move_cursor_left_until(root, &prev, is_not_tab_or_space_at_cursor, metrics);
prev.move_right(root, metrics) catch {};
} else {
prev = inside_prev;
}
}
}
const sel = cursel.enable_selection(root, metrics);
sel.begin = prev;
sel.end = next;
cursel.*.cursor = next;
}
fn select_inside_parentheses_textobject(root: Buffer.Root, cursel: *CurSel, metrics: Buffer.Metrics) !void {
return try select_bracket_textobject(root, cursel, metrics, "(", ")", .inside);
}
fn select_around_parentheses_textobject(root: Buffer.Root, cursel: *CurSel, metrics: Buffer.Metrics) !void {
return try select_bracket_textobject(root, cursel, metrics, "(", ")", .around);
}
fn select_inside_square_brackets_textobject(root: Buffer.Root, cursel: *CurSel, metrics: Buffer.Metrics) !void {
return try select_bracket_textobject(root, cursel, metrics, "[", "]", .inside);
}
fn select_around_square_brackets_textobject(root: Buffer.Root, cursel: *CurSel, metrics: Buffer.Metrics) !void {
return try select_bracket_textobject(root, cursel, metrics, "[", "]", .around);
}
fn select_inside_braces_textobject(root: Buffer.Root, cursel: *CurSel, metrics: Buffer.Metrics) !void {
return try select_bracket_textobject(root, cursel, metrics, "{", "}", .inside);
}
fn select_around_braces_textobject(root: Buffer.Root, cursel: *CurSel, metrics: Buffer.Metrics) !void {
return try select_bracket_textobject(root, cursel, metrics, "{", "}", .around);
}
fn select_bracket_textobject(root: Buffer.Root, cursel: *CurSel, metrics: Buffer.Metrics, opening_char: []const u8, closing_char: []const u8, scope: enum { inside, around }) !void {
const current = cursel.cursor;
var prev = cursel.cursor;
var next = cursel.cursor;
const bracket_egc, _, _ = root.egc_at(current.row, current.col, metrics) catch {
return error.Stop;
};
if (std.mem.eql(u8, bracket_egc, opening_char)) {
const closing_row, const closing_col = try Editor.match_bracket(root, current, metrics);
prev = current;
next.row = closing_row;
next.col = closing_col;
} else if (std.mem.eql(u8, bracket_egc, closing_char)) {
const opening_row, const opening_col = try Editor.match_bracket(root, current, metrics);
prev.row = opening_row;
prev.col = opening_col;
next = current;
} else {
const opening_pos, const closing_pos = find_bracket_pair(root, cursel, metrics, .left, opening_char) catch try find_bracket_pair(root, cursel, metrics, .right, opening_char);
prev.row = opening_pos[0];
prev.col = opening_pos[1];
next.row = closing_pos[0];
next.col = closing_pos[1];
}
prev.move_right(root, metrics) catch {};
if (scope == .around) {
prev.move_left(root, metrics) catch {};
next.move_right(root, metrics) catch {};
}
const sel = cursel.enable_selection(root, metrics);
sel.begin = prev;
sel.end = next;
cursel.*.cursor = next;
}
fn find_bracket_pair(root: Buffer.Root, cursel: *CurSel, metrics: Buffer.Metrics, direction: enum { left, right }, char: []const u8) error{Stop}!struct { struct { usize, usize }, struct { usize, usize } } {
const start = cursel.cursor;
var moving_cursor = cursel.cursor;
var i: usize = 0;
while (i < bracket_search_radius) : (i += 1) {
switch (direction) {
.left => try moving_cursor.move_left(root, metrics),
.right => try moving_cursor.move_right(root, metrics),
}
const curr_egc, _, _ = root.egc_at(moving_cursor.row, moving_cursor.col, metrics) catch {
return error.Stop;
};
if (std.mem.eql(u8, char, curr_egc)) {
const closing_row, const closing_col = try Editor.match_bracket(root, moving_cursor, metrics);
switch (direction) {
.left => if (closing_row > start.row or (closing_row == start.row and closing_col > start.col)) {
return .{ .{ moving_cursor.row, moving_cursor.col }, .{ closing_row, closing_col } };
} else {
continue;
},
.right => {
return .{ .{ moving_cursor.row, moving_cursor.col }, .{ closing_row, closing_col } };
},
}
}
}
return error.Stop;
}

View file

@ -62,9 +62,7 @@ logger: log.Logger,
drag_source: ?Widget = null,
drag_button: input.MouseType = 0,
dark_theme: Widget.Theme,
dark_parsed_theme: ?std.json.Parsed(Widget.Theme),
light_theme: Widget.Theme,
light_parsed_theme: ?std.json.Parsed(Widget.Theme),
idle_frame_count: usize = 0,
unrendered_input_events_count: usize = 0,
init_timer: ?tp.timeout,
@ -160,9 +158,9 @@ fn init(allocator: Allocator) InitError!*Self {
if (@hasDecl(renderer, "install_crash_handler") and conf.start_debugger_on_crash)
renderer.jit_debugger_enabled = true;
const dark_theme, const dark_parsed_theme = get_theme_by_name(allocator, conf.theme) orelse get_theme_by_name(allocator, "dark_modern") orelse return error.UnknownTheme;
const dark_theme = Widget.get_theme_by_name(allocator, conf.theme) orelse Widget.get_theme_by_name(allocator, "dark_modern") orelse return error.UnknownTheme;
conf.theme = dark_theme.name;
const light_theme, const light_parsed_theme = get_theme_by_name(allocator, conf.light_theme) orelse get_theme_by_name(allocator, "default-light") orelse return error.UnknownTheme;
const light_theme = Widget.get_theme_by_name(allocator, conf.light_theme) orelse Widget.get_theme_by_name(allocator, "default-light") orelse return error.UnknownTheme;
conf.light_theme = light_theme.name;
if (build_options.gui) conf.enable_terminal_cursor = false;
@ -203,8 +201,6 @@ fn init(allocator: Allocator) InitError!*Self {
.query_cache_ = try syntax.QueryCache.create(allocator, .{}),
.dark_theme = dark_theme,
.light_theme = light_theme,
.dark_parsed_theme = dark_parsed_theme,
.light_parsed_theme = light_parsed_theme,
};
instance_ = self;
defer instance_ = null;
@ -987,21 +983,13 @@ fn refresh_input_mode(self: *Self) command.Result {
}
fn set_theme_by_name(self: *Self, name: []const u8, action: enum { none, store }) !void {
const theme_, const parsed_theme = get_theme_by_name(self.allocator, name) orelse {
const theme_ = Widget.get_theme_by_name(self.allocator, name) orelse {
self.logger.print("theme not found: {s}", .{name});
return;
};
switch (self.color_scheme) {
.dark => {
if (self.dark_parsed_theme) |p| p.deinit();
self.dark_parsed_theme = parsed_theme;
self.dark_theme = theme_;
},
.light => {
if (self.light_parsed_theme) |p| p.deinit();
self.light_parsed_theme = parsed_theme;
self.light_theme = theme_;
},
.dark => self.dark_theme = theme_,
.light => self.light_theme = theme_,
}
self.set_terminal_style(&theme_);
self.logger.print("theme: {s}", .{theme_.description});
@ -1141,13 +1129,13 @@ const cmds = struct {
pub const set_theme_meta: Meta = .{ .arguments = &.{.string} };
pub fn theme_next(self: *Self, _: Ctx) Result {
const name = get_next_theme_by_name(self.current_theme().name);
const name = Widget.get_next_theme_by_name(self.current_theme().name);
return self.set_theme_by_name(name, .store);
}
pub const theme_next_meta: Meta = .{ .description = "Next color theme" };
pub fn theme_prev(self: *Self, _: Ctx) Result {
const name = get_prev_theme_by_name(self.current_theme().name);
const name = Widget.get_prev_theme_by_name(self.current_theme().name);
return self.set_theme_by_name(name, .store);
}
pub const theme_prev_meta: Meta = .{ .description = "Previous color theme" };
@ -2020,40 +2008,6 @@ pub fn theme() *const Widget.Theme {
return current().current_theme();
}
pub fn get_theme_by_name(allocator: std.mem.Allocator, name: []const u8) ?struct { Widget.Theme, ?std.json.Parsed(Widget.Theme) } {
if (load_theme_file(allocator, name) catch null) |parsed_theme| {
std.log.info("loaded theme from file: {s}", .{name});
return .{ parsed_theme.value, parsed_theme };
}
for (Widget.themes) |theme_| {
if (std.mem.eql(u8, theme_.name, name))
return .{ theme_, null };
}
return null;
}
fn get_next_theme_by_name(name: []const u8) []const u8 {
var next = false;
for (Widget.themes) |theme_| {
if (next)
return theme_.name;
if (std.mem.eql(u8, theme_.name, name))
next = true;
}
return Widget.themes[0].name;
}
fn get_prev_theme_by_name(name: []const u8) []const u8 {
var prev: ?Widget.Theme = null;
for (Widget.themes) |theme_| {
if (std.mem.eql(u8, theme_.name, name))
return (prev orelse Widget.themes[Widget.themes.len - 1]).name;
prev = theme_;
}
return Widget.themes[Widget.themes.len - 1].name;
}
pub fn find_scope_style(theme_: *const Widget.Theme, scope: []const u8) ?Widget.Theme.Token {
return if (find_scope_fallback(scope)) |tm_scope|
scope_to_theme_token(theme_, tm_scope) orelse
@ -2442,19 +2396,6 @@ fn get_or_create_theme_file(self: *Self, allocator: std.mem.Allocator) ![]const
return try root.get_theme_file_name(theme_name);
}
fn load_theme_file(allocator: std.mem.Allocator, theme_name: []const u8) !?std.json.Parsed(Widget.Theme) {
return load_theme_file_internal(allocator, theme_name) catch |e| {
std.log.err("loaded theme from file failed: {}", .{e});
return e;
};
}
fn load_theme_file_internal(allocator: std.mem.Allocator, theme_name: []const u8) !?std.json.Parsed(Widget.Theme) {
_ = std.json.Scanner;
const json_str = root.read_theme(allocator, theme_name) orelse return null;
defer allocator.free(json_str);
return try std.json.parseFromSlice(Widget.Theme, allocator, json_str, .{ .allocate = .alloc_always });
}
pub const WidgetType = @import("config").WidgetType;
pub const ConfigWidgetStyle = @import("config").WidgetStyle;
pub const WidgetStyle = @import("WidgetStyle.zig");