Compare commits
15 commits
7b7c84628c
...
4a44838b88
| Author | SHA1 | Date | |
|---|---|---|---|
| 4a44838b88 | |||
| b472300b3d | |||
| 94109da73e | |||
| a897e6bf87 | |||
| 50db9082d8 | |||
| 025ef9c768 | |||
| c462e3abda | |||
| 098d925358 | |||
| 8152de3df9 | |||
| 387a3416c3 | |||
| c71ddb2900 | |||
| 62bc86d2db | |||
| b22337a2b3 | |||
| 19751e7fd4 | |||
| 288e23e8b0 |
4 changed files with 141 additions and 22 deletions
|
|
@ -1362,6 +1362,7 @@ fn send_completion_item(to: tp.pid_ref, file_path: []const u8, row: usize, col:
|
|||
var documentation: []const u8 = "";
|
||||
var documentation_kind: []const u8 = "";
|
||||
var sortText: []const u8 = "";
|
||||
var insertText: []const u8 = "";
|
||||
var insertTextFormat: usize = 0;
|
||||
var textEdit: TextEdit = .{};
|
||||
var additionalTextEdits: [32]TextEdit = undefined;
|
||||
|
|
@ -1402,6 +1403,8 @@ fn send_completion_item(to: tp.pid_ref, file_path: []const u8, row: usize, col:
|
|||
try cbor.skipValue(&iter);
|
||||
}
|
||||
}
|
||||
} else if (std.mem.eql(u8, field_name, "insertText")) {
|
||||
if (!(try cbor.matchValue(&iter, cbor.extract(&insertText)))) return invalid_field("insertText");
|
||||
} else if (std.mem.eql(u8, field_name, "sortText")) {
|
||||
if (!(try cbor.matchValue(&iter, cbor.extract(&sortText)))) return invalid_field("sortText");
|
||||
} else if (std.mem.eql(u8, field_name, "insertTextFormat")) {
|
||||
|
|
@ -1437,6 +1440,7 @@ fn send_completion_item(to: tp.pid_ref, file_path: []const u8, row: usize, col:
|
|||
documentation,
|
||||
documentation_kind,
|
||||
sortText,
|
||||
insertText,
|
||||
insertTextFormat,
|
||||
textEdit.newText,
|
||||
insert.start.line,
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ pub fn parse(allocator: std.mem.Allocator, snippet: []const u8) Error!Snippet {
|
|||
max_id = @max(id orelse unreachable, max_id);
|
||||
id = null;
|
||||
state = .initial;
|
||||
break :fsm;
|
||||
continue :fsm .initial;
|
||||
},
|
||||
},
|
||||
.placeholder => switch (c) {
|
||||
|
|
@ -136,8 +136,16 @@ pub fn parse(allocator: std.mem.Allocator, snippet: []const u8) Error!Snippet {
|
|||
for (tabstops.items) |item| if (item.id == n) {
|
||||
(try tabstop.addOne(allocator)).* = item.range;
|
||||
};
|
||||
(try result.addOne(allocator)).* = try tabstop.toOwnedSlice(allocator);
|
||||
if (tabstop.items.len > 0)
|
||||
(try result.addOne(allocator)).* = try tabstop.toOwnedSlice(allocator);
|
||||
}
|
||||
var tabstop: std.ArrayList(Range) = .empty;
|
||||
errdefer tabstop.deinit(allocator);
|
||||
for (tabstops.items) |item| if (item.id == 0) {
|
||||
(try tabstop.addOne(allocator)).* = item.range;
|
||||
};
|
||||
if (tabstop.items.len > 0)
|
||||
(try result.addOne(allocator)).* = try tabstop.toOwnedSlice(allocator);
|
||||
return .{
|
||||
.text = try text.toOwnedSlice(),
|
||||
.tabstops = try result.toOwnedSlice(allocator),
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ const Cell = @import("renderer").Cell;
|
|||
const input = @import("input");
|
||||
const command = @import("command");
|
||||
const EventHandler = @import("EventHandler");
|
||||
const snippet = @import("snippet");
|
||||
|
||||
const scrollbar_v = @import("scrollbar_v.zig");
|
||||
const editor_gutter = @import("editor_gutter.zig");
|
||||
|
|
@ -308,6 +309,7 @@ pub const Editor = struct {
|
|||
|
||||
cursels: CurSel.List = .empty,
|
||||
cursels_saved: CurSel.List = .empty,
|
||||
cursels_tabstops: std.ArrayList([]CurSel) = .empty,
|
||||
selection_mode: SelectMode = .char,
|
||||
selection_drag_initial: ?Selection = null,
|
||||
target_column: ?Cursor = null,
|
||||
|
|
@ -432,6 +434,34 @@ pub const Editor = struct {
|
|||
return count;
|
||||
}
|
||||
|
||||
fn cancel_all_tabstops(self: *Self) void {
|
||||
for (self.cursels_tabstops.items) |ts_list| self.allocator.free(ts_list);
|
||||
self.cursels_tabstops.clearRetainingCapacity();
|
||||
}
|
||||
|
||||
fn pop_tabstop(self: *Self) bool {
|
||||
if (self.cursels_tabstops.items.len == 0) return false;
|
||||
|
||||
const tabstops = self.cursels_tabstops.toOwnedSlice(self.allocator) catch return false;
|
||||
defer {
|
||||
self.allocator.free(tabstops[0]);
|
||||
self.allocator.free(tabstops);
|
||||
}
|
||||
|
||||
self.cancel_all_matches();
|
||||
self.cancel_all_selections();
|
||||
self.cursels.clearRetainingCapacity();
|
||||
|
||||
for (tabstops[0]) |cursel| {
|
||||
(self.cursels.addOne(self.allocator) catch return false).* = cursel;
|
||||
if (builtin.mode == .Debug)
|
||||
self.logger.print("pop tabstop 1 of {}", .{tabstops.len});
|
||||
}
|
||||
for (tabstops[1..]) |tabstop| (self.cursels_tabstops.addOne(self.allocator) catch return false).* = tabstop;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn write_state(self: *const Self, writer: *std.Io.Writer) !void {
|
||||
try cbor.writeArrayHeader(writer, 10);
|
||||
try cbor.writeValue(writer, self.file_path orelse "");
|
||||
|
|
@ -544,6 +574,7 @@ pub const Editor = struct {
|
|||
self.diagnostics.deinit(self.allocator);
|
||||
self.completions.deinit(self.allocator);
|
||||
if (self.syntax) |syn| syn.destroy(tui.query_cache());
|
||||
self.cancel_all_tabstops();
|
||||
self.cursels.deinit(self.allocator);
|
||||
self.matches.deinit(self.allocator);
|
||||
self.handlers.deinit();
|
||||
|
|
@ -2167,6 +2198,8 @@ pub const Editor = struct {
|
|||
cursel.nudge_insert(nudge);
|
||||
for (self.matches.items) |*match_| if (match_.*) |*match|
|
||||
match.nudge_insert(nudge);
|
||||
for (self.cursels_tabstops.items) |tabstop| for (tabstop) |*cursel|
|
||||
cursel.nudge_insert(nudge);
|
||||
}
|
||||
|
||||
fn nudge_delete(self: *Self, nudge: Selection, exclude: *const CurSel, _: usize) void {
|
||||
|
|
@ -2179,6 +2212,11 @@ pub const Editor = struct {
|
|||
if (!match.nudge_delete(nudge)) {
|
||||
self.matches.items[i] = null;
|
||||
};
|
||||
for (self.cursels_tabstops.items) |tabstop| for (tabstop) |*cursel|
|
||||
if (!cursel.nudge_delete(nudge)) {
|
||||
self.cancel_all_tabstops();
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
pub fn delete_selection(self: *Self, root: Buffer.Root, cursel: *CurSel, allocator: Allocator) error{Stop}!Buffer.Root {
|
||||
|
|
@ -3934,11 +3972,12 @@ pub const Editor = struct {
|
|||
}
|
||||
|
||||
pub fn indent(self: *Self, ctx: Context) Result {
|
||||
if (self.pop_tabstop()) return;
|
||||
const b = try self.buf_for_update();
|
||||
const root = try self.with_cursels_mut_repeat(b.root, indent_cursel, b.allocator, ctx);
|
||||
try self.update_buf(root);
|
||||
}
|
||||
pub const indent_meta: Meta = .{ .description = "Indent current line", .arguments = &.{.integer} };
|
||||
pub const indent_meta: Meta = .{ .description = "Indent current line (or pop tabstop)", .arguments = &.{.integer} };
|
||||
|
||||
fn unindent_cursor(self: *Self, root: Buffer.Root, cursor: *Cursor, cursor_protect: ?*Cursor, allocator: Allocator) error{Stop}!Buffer.Root {
|
||||
var newroot = root;
|
||||
|
|
@ -4166,6 +4205,7 @@ pub const Editor = struct {
|
|||
pub const move_buffer_end_meta: Meta = .{ .description = "Move cursor to end of file" };
|
||||
|
||||
pub fn cancel(self: *Self, _: Context) Result {
|
||||
self.cancel_all_tabstops();
|
||||
self.cancel_all_selections();
|
||||
self.cancel_all_matches();
|
||||
@import("keybind").clear_integer_argument();
|
||||
|
|
@ -4618,10 +4658,52 @@ pub const Editor = struct {
|
|||
}
|
||||
pub const select_prev_sibling_meta: Meta = .{ .description = "Move selection to previous AST sibling node" };
|
||||
|
||||
pub fn insert_chars(self: *Self, ctx: Context) Result {
|
||||
var chars: []const u8 = undefined;
|
||||
if (!try ctx.args.match(.{tp.extract(&chars)}))
|
||||
return error.InvalidInsertCharsArgument;
|
||||
pub fn insert_snippet(self: *Self, snippet_text: []const u8) Result {
|
||||
self.logger.print("snippet: {s}", .{snippet_text});
|
||||
const value = try snippet.parse(self.allocator, snippet_text);
|
||||
defer value.deinit(self.allocator);
|
||||
|
||||
const root_ = try self.buf_root();
|
||||
const primary = self.get_primary();
|
||||
const cursor = if (primary.selection) |sel| sel.begin else primary.cursor;
|
||||
const eol_mode = try self.buf_eol_mode();
|
||||
var cursor_pos: usize = 0;
|
||||
_ = try root_.get_range(.{
|
||||
.begin = .{ .row = 0, .col = 0 },
|
||||
.end = cursor,
|
||||
}, null, &cursor_pos, null, self.metrics);
|
||||
|
||||
try self.insert_cursels(value.text);
|
||||
const root = try self.buf_root();
|
||||
|
||||
if (self.count_cursels() > 1)
|
||||
return;
|
||||
|
||||
self.cancel_all_tabstops();
|
||||
for (value.tabstops) |ts| {
|
||||
var cursels: std.ArrayList(CurSel) = .empty;
|
||||
for (ts) |placeholder| {
|
||||
const ts_begin_pos = cursor_pos + placeholder.begin.@"0";
|
||||
const ts_begin = root.byte_offset_to_line_and_col(ts_begin_pos, self.metrics, eol_mode);
|
||||
const ts_end = if (placeholder.end) |end| blk: {
|
||||
const ts_end_pos = cursor_pos + end.@"0";
|
||||
break :blk root.byte_offset_to_line_and_col(ts_end_pos, self.metrics, eol_mode);
|
||||
} else null;
|
||||
const p = (try cursels.addOne(self.allocator));
|
||||
p.* = if (ts_end) |ts_end_| .{
|
||||
.cursor = ts_end_,
|
||||
.selection = .{ .begin = ts_begin, .end = ts_end_ },
|
||||
} else .{
|
||||
.cursor = ts_begin,
|
||||
};
|
||||
if (p.selection) |sel| self.add_match_from_selection(sel);
|
||||
}
|
||||
(try self.cursels_tabstops.addOne(self.allocator)).* = try cursels.toOwnedSlice(self.allocator);
|
||||
}
|
||||
_ = self.pop_tabstop();
|
||||
}
|
||||
|
||||
pub fn insert_cursels(self: *Self, chars: []const u8) Result {
|
||||
const b = try self.buf_for_update();
|
||||
var root = b.root;
|
||||
for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel| {
|
||||
|
|
@ -4630,6 +4712,13 @@ pub const Editor = struct {
|
|||
try self.update_buf(root);
|
||||
self.clamp();
|
||||
}
|
||||
|
||||
pub fn insert_chars(self: *Self, ctx: Context) Result {
|
||||
var chars: []const u8 = undefined;
|
||||
if (!try ctx.args.match(.{tp.extract(&chars)}))
|
||||
return error.InvalidInsertCharsArgument;
|
||||
return self.insert_cursels(chars);
|
||||
}
|
||||
pub const insert_chars_meta: Meta = .{ .arguments = &.{.string} };
|
||||
|
||||
pub fn insert_line(self: *Self, _: Context) Result {
|
||||
|
|
@ -5452,6 +5541,13 @@ pub const Editor = struct {
|
|||
(self.matches.addOne(self.allocator) catch return).* = match;
|
||||
}
|
||||
|
||||
fn add_match_from_selection(self: *Self, sel: Selection) void {
|
||||
var match: Match = Match.from_selection(sel);
|
||||
if (match.end.eql(self.get_primary().cursor))
|
||||
match.has_selection = true;
|
||||
(self.matches.addOne(self.allocator) catch return).* = match;
|
||||
}
|
||||
|
||||
fn find_selection_match(self: *const Self, sel: Selection) ?*Match {
|
||||
for (self.matches.items) |*match_| if (match_.*) |*match| {
|
||||
if (match.to_selection().eql(sel))
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ const tp = @import("thespian");
|
|||
const root = @import("soft_root").root;
|
||||
const command = @import("command");
|
||||
const Buffer = @import("Buffer");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
const tui = @import("../../tui.zig");
|
||||
pub const Type = @import("palette.zig").Create(@This());
|
||||
|
|
@ -46,9 +47,9 @@ pub fn load_entries(palette: *Type) !usize {
|
|||
var max_label_len: usize = 0;
|
||||
for (palette.entries.items) |*item| {
|
||||
const values = get_values(item.cbor);
|
||||
if (get_replace_selection(values.replace)) |replace| {
|
||||
if (palette.value.replace == null) palette.value.replace = replace;
|
||||
}
|
||||
if (palette.value.replace == null) if (get_replace_selection(values.replace)) |replace| {
|
||||
palette.value.replace = replace;
|
||||
};
|
||||
item.label = values.label;
|
||||
item.sort_text = values.sort_text;
|
||||
|
||||
|
|
@ -131,6 +132,7 @@ const Values = struct {
|
|||
label_description: []const u8,
|
||||
detail: []const u8,
|
||||
documentation: []const u8,
|
||||
insertText: []const u8,
|
||||
insertTextFormat: usize,
|
||||
textEdit_newText: []const u8,
|
||||
};
|
||||
|
|
@ -143,6 +145,7 @@ fn get_values(item_cbor: []const u8) Values {
|
|||
var documentation: []const u8 = "";
|
||||
var sort_text: []const u8 = "";
|
||||
var kind: u8 = 0;
|
||||
var insertText: []const u8 = "";
|
||||
var insertTextFormat: usize = 0;
|
||||
var textEdit_newText: []const u8 = "";
|
||||
var replace: Buffer.Selection = .{};
|
||||
|
|
@ -160,6 +163,7 @@ fn get_values(item_cbor: []const u8) Values {
|
|||
cbor.extract(&documentation), // documentation
|
||||
cbor.any, // documentation_kind
|
||||
cbor.extract(&sort_text), // sortText
|
||||
cbor.extract(&insertText), // insertText
|
||||
cbor.extract(&insertTextFormat), // insertTextFormat
|
||||
cbor.extract(&textEdit_newText), // textEdit_newText
|
||||
cbor.any, // insert.begin.row
|
||||
|
|
@ -183,6 +187,7 @@ fn get_values(item_cbor: []const u8) Values {
|
|||
.detail = detail,
|
||||
.documentation = documentation,
|
||||
.insertTextFormat = insertTextFormat,
|
||||
.insertText = insertText,
|
||||
.textEdit_newText = textEdit_newText,
|
||||
};
|
||||
}
|
||||
|
|
@ -192,25 +197,26 @@ const Range = struct { start: Position, end: Position };
|
|||
const Position = struct { line: usize, character: usize };
|
||||
|
||||
fn get_replace_selection(replace: Buffer.Selection) ?Buffer.Selection {
|
||||
return if (tui.get_active_editor()) |edt|
|
||||
replace.from_pos(edt.buf_root() catch return null, edt.metrics)
|
||||
else if (replace.empty())
|
||||
return if (replace.empty())
|
||||
null
|
||||
else if (tui.get_active_editor()) |edt|
|
||||
replace.from_pos(edt.buf_root() catch return null, edt.metrics)
|
||||
else
|
||||
replace;
|
||||
}
|
||||
|
||||
fn select(menu: **Type.MenuType, button: *Type.ButtonType, _: Type.Pos) void {
|
||||
const values = get_values(button.opts.label);
|
||||
const editor = tui.get_active_editor() orelse return;
|
||||
const text = if (values.insertText.len > 0)
|
||||
values.insertText
|
||||
else if (values.textEdit_newText.len > 0)
|
||||
values.textEdit_newText
|
||||
else
|
||||
values.label;
|
||||
switch (values.insertTextFormat) {
|
||||
2 => {
|
||||
const snippet = @import("snippet").parse(menu.*.opts.ctx.allocator, values.textEdit_newText) catch return;
|
||||
defer snippet.deinit(menu.*.opts.ctx.allocator);
|
||||
tp.self_pid().send(.{ "cmd", "insert_chars", .{snippet.text} }) catch |e| menu.*.opts.ctx.logger.err(module_name, e);
|
||||
},
|
||||
else => {
|
||||
tp.self_pid().send(.{ "cmd", "insert_chars", .{values.label} }) catch |e| menu.*.opts.ctx.logger.err(module_name, e);
|
||||
},
|
||||
2 => editor.insert_snippet(text) catch |e| menu.*.opts.ctx.logger.err(module_name, e),
|
||||
else => editor.insert_cursels(text) catch |e| menu.*.opts.ctx.logger.err(module_name, e),
|
||||
}
|
||||
const mv = tui.mainview() orelse return;
|
||||
mv.cancel_info_content() catch {};
|
||||
|
|
@ -227,7 +233,12 @@ pub fn updated(palette: *Type, button_: ?*Type.ButtonType) !void {
|
|||
try mv.set_info_content(values.label, .replace);
|
||||
try mv.set_info_content(" ", .append); // blank line
|
||||
try mv.set_info_content(values.detail, .append);
|
||||
// try mv.set_info_content(values.textEdit_newText, .append);
|
||||
if (builtin.mode == .Debug) {
|
||||
try mv.set_info_content("newText:", .append); // blank line
|
||||
try mv.set_info_content(values.textEdit_newText, .append);
|
||||
try mv.set_info_content("insertText:", .append); // blank line
|
||||
try mv.set_info_content(values.insertText, .append);
|
||||
}
|
||||
try mv.set_info_content(" ", .append); // blank line
|
||||
try mv.set_info_content(values.documentation, .append);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue