From 5286975257a9b04475513b2722f8b1b66cb763c9 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Fri, 22 Aug 2025 22:18:57 +0200 Subject: [PATCH 1/5] fix: move internal clipboard from buffer local to session wide closes #287 --- src/tui/editor.zig | 21 ++++++--------------- src/tui/mode/helix.zig | 2 +- src/tui/tui.zig | 14 ++++++++++++++ 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/tui/editor.zig b/src/tui/editor.zig index 393cd0e..06c0480 100644 --- a/src/tui/editor.zig +++ b/src/tui/editor.zig @@ -280,7 +280,6 @@ pub const Editor = struct { cursels_saved: CurSel.List = .empty, selection_mode: SelectMode = .char, selection_drag_initial: ?Selection = null, - clipboard: ?[]const u8 = null, target_column: ?Cursor = null, filter_: ?struct { before_root: Buffer.Root, @@ -385,9 +384,8 @@ pub const Editor = struct { } pub fn write_state(self: *const Self, writer: Buffer.MetaWriter) !void { - try cbor.writeArrayHeader(writer, 12); + try cbor.writeArrayHeader(writer, 11); try cbor.writeValue(writer, self.file_path orelse ""); - try cbor.writeValue(writer, self.clipboard orelse ""); try cbor.writeValue(writer, self.last_find_query orelse ""); try cbor.writeValue(writer, self.enable_format_on_save); try cbor.writeValue(writer, self.enable_auto_save); @@ -419,12 +417,10 @@ pub const Editor = struct { var file_path: []const u8 = undefined; var view_cbor: []const u8 = undefined; var cursels_cbor: []const u8 = undefined; - var clipboard: []const u8 = undefined; var last_find_query: []const u8 = undefined; var find_history: []const u8 = undefined; if (!try cbor.matchValue(iter, .{ tp.extract(&file_path), - tp.extract(&clipboard), tp.extract(&last_find_query), tp.extract(&self.enable_format_on_save), tp.extract(&self.enable_auto_save), @@ -440,7 +436,6 @@ pub const Editor = struct { self.refresh_tab_width(); if (op == .open_file) try self.open(file_path); - self.clipboard = if (clipboard.len > 0) try self.allocator.dupe(u8, clipboard) else null; self.last_find_query = if (last_find_query.len > 0) try self.allocator.dupe(u8, last_find_query) else null; const rows = self.view.rows; const cols = self.view.cols; @@ -2605,9 +2600,7 @@ pub const Editor = struct { pub const scroll_view_bottom_meta: Meta = .{}; fn set_clipboard(self: *Self, text: []const u8) void { - if (self.clipboard) |old| - self.allocator.free(old); - self.clipboard = text; + tui.set_clipboard(text); if (builtin.os.tag == .windows) { @import("renderer").copy_to_windows_clipboard(text) catch |e| self.logger.print_err("clipboard", "failed to set clipboard: {any}", .{e}); @@ -2616,10 +2609,8 @@ pub const Editor = struct { } } - pub fn set_clipboard_internal(self: *Self, text: []const u8) void { - if (self.clipboard) |old| - self.allocator.free(old); - self.clipboard = text; + pub fn set_clipboard_internal(_: *Self, text: []const u8) void { + tui.set_clipboard(text); } pub fn copy_selection(root: Buffer.Root, sel: Selection, text_allocator: Allocator, metrics: Buffer.Metrics) ![]u8 { @@ -2952,7 +2943,7 @@ pub const Editor = struct { pub fn paste(self: *Self, ctx: Context) Result { var text: []const u8 = undefined; if (!(ctx.args.buf.len > 0 and try ctx.args.match(.{tp.extract(&text)}))) { - if (self.clipboard) |text_| text = text_ else return; + if (tui.get_clipboard()) |text_| text = text_ else return; } self.logger.print("paste: {d} bytes", .{text.len}); const b = try self.buf_for_update(); @@ -2987,7 +2978,7 @@ pub const Editor = struct { pub fn paste_internal_vim(self: *Self, ctx: Context) Result { var text: []const u8 = undefined; if (!(ctx.args.buf.len > 0 and try ctx.args.match(.{tp.extract(&text)}))) { - if (self.clipboard) |text_| text = text_ else return; + if (tui.get_clipboard()) |text_| text = text_ else return; } self.logger.print("paste: {d} bytes", .{text.len}); diff --git a/src/tui/mode/helix.zig b/src/tui/mode/helix.zig index 84eaffe..80a998b 100644 --- a/src/tui/mode/helix.zig +++ b/src/tui/mode/helix.zig @@ -267,7 +267,7 @@ const cmds_ = struct { var text: []const u8 = undefined; if (!(ctx.args.buf.len > 0 and try ctx.args.match(.{tp.extract(&text)}))) { - if (ed.clipboard) |text_| text = text_ else return; + if (tui.get_clipboard()) |text_| text = text_ else return; } ed.logger.print("paste: {d} bytes", .{text.len}); diff --git a/src/tui/tui.zig b/src/tui/tui.zig index 810a723..7f0c172 100644 --- a/src/tui/tui.zig +++ b/src/tui/tui.zig @@ -64,6 +64,7 @@ fontfaces_: std.ArrayListUnmanaged([]const u8) = .{}, enable_mouse_idle_timer: bool = false, query_cache_: *syntax.QueryCache, frames_rendered_: usize = 0, +clipboard: ?[]const u8 = null, const keepalive = std.time.us_per_day * 365; // one year const idle_frames = 0; @@ -252,6 +253,7 @@ fn deinit(self: *Self) void { self.logger.deinit(); self.query_cache_.deinit(); root.free_config(self.allocator, self.config_bufs); + if (self.clipboard) |text| self.allocator.free(text); self.allocator.destroy(self); } @@ -1645,3 +1647,15 @@ fn widget_type_config_variable(widget_type: WidgetType) *ConfigWidgetStyle { .home => &config_.home_style, }; } + +pub fn get_clipboard() ?[]const u8 { + const self = current(); + return self.clipboard; +} + +pub fn set_clipboard(text: []const u8) void { + const self = current(); + if (self.clipboard) |old| + self.allocator.free(old); + self.clipboard = text; +} From dad954da1aefd618c83d39a19962d17001dc2f28 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Fri, 22 Aug 2025 22:24:32 +0200 Subject: [PATCH 2/5] fix: don't falsely claim to support LSP pull diagnostics closes #285 --- src/Project.zig | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Project.zig b/src/Project.zig index ad71f8c..f78db3a 100644 --- a/src/Project.zig +++ b/src/Project.zig @@ -1872,10 +1872,6 @@ fn send_lsp_init_request(self: *Self, lsp: *const LSP, project_path: []const u8, }, }, }, - .diagnostic = .{ - .dynamicRegistration = true, - .relatedDocumentSupport = false, - }, }, .window = .{ .showMessage = .{ From cc3ac7e58d058706e636de5986fd1ff9112fd154 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Sat, 23 Aug 2025 17:28:32 +0200 Subject: [PATCH 3/5] refactor: move default language_server and formatter config to file_type_lsp.zig From flow-syntax commit 410d19e633f237cd1602175450bd7d3bb03a1898. --- src/file_type_config.zig | 45 +++++++- src/file_type_lsp.zig | 223 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 266 insertions(+), 2 deletions(-) create mode 100644 src/file_type_lsp.zig diff --git a/src/file_type_config.zig b/src/file_type_config.zig index 944ea16..a8b8a74 100644 --- a/src/file_type_config.zig +++ b/src/file_type_config.zig @@ -23,6 +23,7 @@ pub const default = struct { pub const folder_icon = ""; fn from_file_type(file_type: syntax.FileType) @This() { + const lsp_defaults: LspDefaults = static_file_type_lsp_defaults.get(file_type.name) orelse .{}; return .{ .name = file_type.name, .color = file_type.color, @@ -33,8 +34,8 @@ fn from_file_type(file_type: syntax.FileType) @This() { .first_line_matches_content = if (file_type.first_line_matches) |flm| flm.content else null, .parser = file_type.name, .comment = file_type.comment, - .formatter = file_type.formatter, - .language_server = file_type.language_server, + .formatter = lsp_defaults.formatter, + .language_server = lsp_defaults.language_server, }; } @@ -199,6 +200,46 @@ pub fn create_syntax_guess_file_type( return create_syntax(file_type, allocator, query_cache); } +const static_file_type_lsp_defaults_list = load_file_type_lsp_defaults(@import("file_type_lsp.zig")); +const static_file_type_lsp_defaults = std.StaticStringMap(LspDefaults).initComptime(static_file_type_lsp_defaults_list); + +const LspDefaults = struct { + formatter: ?[]const []const u8 = null, + language_server: ?[]const []const u8 = null, +}; +const ListEntry = struct { []const u8, LspDefaults }; + +fn load_file_type_lsp_defaults(comptime Namespace: type) []const ListEntry { + comptime switch (@typeInfo(Namespace)) { + .@"struct" => |info| { + var count = 0; + for (info.decls) |_| count += 1; + var construct_types: [count]ListEntry = undefined; + var i = 0; + for (info.decls) |decl| { + const lang = decl.name; + const args = @field(Namespace, lang); + construct_types[i] = .{ lang, .{ + .formatter = if (@hasField(@TypeOf(args), "formatter")) vec(args.formatter) else null, + .language_server = if (@hasField(@TypeOf(args), "language_server")) vec(args.language_server) else null, + } }; + i += 1; + } + const types = construct_types; + return &types; + }, + else => @compileError("expected tuple or struct type"), + }; +} + +fn vec(comptime args: anytype) []const []const u8 { + var cmd: []const []const u8 = &[_][]const u8{}; + inline for (args) |arg| { + cmd = cmd ++ [_][]const u8{arg}; + } + return cmd; +} + const syntax = @import("syntax"); const std = @import("std"); const root = @import("root"); diff --git a/src/file_type_lsp.zig b/src/file_type_lsp.zig new file mode 100644 index 0000000..facfe14 --- /dev/null +++ b/src/file_type_lsp.zig @@ -0,0 +1,223 @@ +pub const agda = .{}; + +pub const astro = .{ + .language_server = .{ "astro-ls", "--stdio" }, +}; + +pub const bash = .{ + .language_server = .{ "bash-language-server", "start" }, + .formatter = .{ "shfmt", "--indent", "4" }, +}; + +pub const c = .{ + .language_server = .{"clangd"}, + .formatter = .{"clang-format"}, +}; + +pub const @"c-sharp" = .{ + .language_server = .{ "OmniSharp", "-lsp" }, + .formatter = .{ "csharpier", "format" }, +}; + +pub const conf = .{}; + +pub const cmake = .{ + .language_server = .{"cmake-language-server"}, + .formatter = .{"cmake-format"}, +}; + +pub const cpp = .{ + .language_server = .{"clangd"}, + .formatter = .{"clang-format"}, +}; + +pub const css = .{ + .language_server = .{ "vscode-css-language-server", "--stdio" }, +}; + +pub const diff = .{}; + +pub const dockerfile = .{}; + +pub const dtd = .{}; + +pub const elixir = .{ + .language_server = .{"elixir-ls"}, + .formatter = .{ "mix", "format", "-" }, +}; + +pub const fish = .{}; + +pub const @"git-rebase" = .{}; + +pub const gitcommit = .{}; + +pub const gleam = .{ + .language_server = .{ "gleam", "lsp" }, + .formatter = .{ "gleam", "format", "--stdin" }, +}; + +pub const go = .{ + .language_server = .{"gopls"}, + .formatter = .{"gofmt"}, +}; + +pub const hare = .{}; + +pub const haskell = .{ + .language_server = .{ "haskell-language-server-wrapper", "lsp" }, +}; + +pub const html = .{ + .language_server = .{ "superhtml", "lsp" }, // https://github.com/kristoff-it/super-html.git + .formatter = .{ "superhtml", "fmt", "--stdin" }, +}; + +pub const superhtml = .{ + .language_server = .{ "superhtml", "lsp" }, + .formatter = .{ "superhtml", "fmt", "--stdin-super" }, +}; + +pub const hurl = .{}; + +pub const java = .{}; + +pub const javascript = .{ + .language_server = .{ "typescript-language-server", "--stdio" }, + .formatter = .{ "prettier", "--parser", "typescript" }, +}; + +pub const json = .{ + .language_server = .{ "vscode-json-language-server", "--stdio" }, + .formatter = .{ "prettier", "--parser", "json" }, +}; + +pub const julia = .{ + .language_server = .{ "julia", "-e", "using LanguageServer; runserver()" }, + .formatter = .{ "julia", "-e", "using JuliaFormatter; print(format_text(read(stdin, String)))" }, +}; + +pub const kdl = .{}; + +pub const lua = .{ + .language_server = .{"lua-lsp"}, +}; + +pub const mail = .{}; + +pub const make = .{}; + +pub const markdown = .{ + .language_server = .{ "marksman", "server" }, + .formatter = .{ "prettier", "--parser", "markdown" }, +}; + +pub const @"markdown-inline" = .{}; + +pub const nasm = .{}; + +pub const nim = .{ + .language_server = .{"nimlangserver"}, +}; + +pub const nimble = .{}; + +pub const ninja = .{}; + +pub const nix = .{ + .language_server = .{"nixd"}, + .formatter = .{"alejandra"}, +}; + +pub const nu = .{ + .language_server = .{ "nu", "--lsp" }, +}; + +pub const ocaml = .{ + .language_server = .{ "ocamllsp", "--fallback-read-dot-merlin" }, + .formatter = .{ "ocamlformat", "--profile=ocamlformat", "-" }, +}; + +pub const odin = .{ + .language_server = .{"ols"}, + .formatter = .{ "odinfmt", "-stdin" }, +}; + +pub const openscad = .{ + .language_server = .{"openscad-lsp"}, +}; + +pub const org = .{}; + +pub const php = .{ + .language_server = .{ "intelephense", "--stdio" }, +}; + +pub const powershell = .{}; + +pub const proto = .{}; + +pub const purescript = .{}; + +pub const python = .{ + .language_server = .{"pylsp"}, +}; + +pub const regex = .{}; + +pub const rpmspec = .{}; + +pub const ruby = .{ + .language_server = .{"ruby-lsp"}, +}; + +pub const rust = .{ + .language_server = .{"rust-analyzer"}, + .formatter = .{"rustfmt"}, +}; + +pub const scheme = .{}; + +pub const sql = .{}; + +pub const @"ssh-config" = .{}; + +pub const swift = .{ + .language_server = .{"sourcekit-lsp"}, + .formatter = .{"swift-format"}, +}; + +pub const verilog = .{ + .language_server = .{"verible-verilog-ls"}, + .formatter = .{ "verible-verilog-format", "-" }, +}; + +pub const toml = .{}; + +pub const typescript = .{ + .language_server = .{ "typescript-language-server", "--stdio" }, + .formatter = .{ "prettier", "--parser", "typescript" }, +}; + +pub const typst = .{ + .language_server = .{"tinymist"}, +}; + +pub const uxntal = .{}; + +pub const vim = .{}; + +pub const xml = .{ + .formatter = .{ "xmllint", "--format", "-" }, +}; + +pub const yaml = .{}; + +pub const zig = .{ + .language_server = .{"zls"}, + .formatter = .{ "zig", "fmt", "--stdin" }, +}; + +pub const ziggy = .{}; + +pub const @"ziggy-schema" = .{}; From 5dfa156fd65d59da1163335d18b45ff4de166c6b Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Sat, 23 Aug 2025 17:33:14 +0200 Subject: [PATCH 4/5] build: update flow-syntax to drop lsp configuration fields --- build.zig.zon | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index 75d94c0..64a5fe7 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -6,8 +6,8 @@ .dependencies = .{ .syntax = .{ - .url = "git+https://github.com/neurocyte/flow-syntax?ref=zig-0.14#410d19e633f237cd1602175450bd7d3bb03a1898", - .hash = "flow_syntax-0.1.0-X8jOoT4OAQDibKKzYlJls3u5KczVh__cWYN7vTqCE1o3", + .url = "git+https://github.com/neurocyte/flow-syntax?ref=zig-0.14#bfeb77d62a5e58ec5d2451bdd543619ae26923a2", + .hash = "flow_syntax-0.1.0-X8jOoSkCAQClgFS-j4X7jL-PMJ6LVKdAoNMaHgqJweFL", }, .flags = .{ .url = "https://github.com/n0s4/flags/archive/372501d1576b5723829bcba98e41361132c7b618.tar.gz", From 7a705a4eb40a2878fe0c9b0b9038f74171cc8f0e Mon Sep 17 00:00:00 2001 From: Daggerfall-is-the-best-TES-game <33900090+Daggerfall-is-the-best-TES-game@users.noreply.github.com> Date: Fri, 22 Aug 2025 20:50:37 -0400 Subject: [PATCH 5/5] change python default lsp to ruff ruff is much faster than pylsp so it would be a good fit for flow closes neurocyte/flow-syntax#13 --- src/file_type_lsp.zig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/file_type_lsp.zig b/src/file_type_lsp.zig index facfe14..a558cc7 100644 --- a/src/file_type_lsp.zig +++ b/src/file_type_lsp.zig @@ -160,7 +160,8 @@ pub const proto = .{}; pub const purescript = .{}; pub const python = .{ - .language_server = .{"pylsp"}, + .language_server = .{ "ruff", "server" }, + .formatter = .{ "ruff", "format", "-" }, }; pub const regex = .{};