refactor: add support for nested snippet placeholders
This commit is contained in:
parent
4a44838b88
commit
b6161043ac
1 changed files with 17 additions and 14 deletions
|
|
@ -20,7 +20,8 @@ pub fn parse(allocator: std.mem.Allocator, snippet: []const u8) Error!Snippet {
|
||||||
var tabstops: std.ArrayList(struct { id: usize, range: Range }) = .empty;
|
var tabstops: std.ArrayList(struct { id: usize, range: Range }) = .empty;
|
||||||
defer tabstops.deinit(allocator);
|
defer tabstops.deinit(allocator);
|
||||||
var id: ?usize = null;
|
var id: ?usize = null;
|
||||||
var content_begin: ?Position = null;
|
var content_begin: std.ArrayList(Position) = .empty;
|
||||||
|
defer content_begin.deinit(allocator);
|
||||||
var max_id: usize = 0;
|
var max_id: usize = 0;
|
||||||
var text: std.Io.Writer.Allocating = .init(allocator);
|
var text: std.Io.Writer.Allocating = .init(allocator);
|
||||||
defer text.deinit();
|
defer text.deinit();
|
||||||
|
|
@ -31,25 +32,29 @@ pub fn parse(allocator: std.mem.Allocator, snippet: []const u8) Error!Snippet {
|
||||||
tabstop,
|
tabstop,
|
||||||
placeholder,
|
placeholder,
|
||||||
content,
|
content,
|
||||||
content_escape,
|
|
||||||
} = .initial;
|
} = .initial;
|
||||||
|
|
||||||
|
var state_stack: std.ArrayList(@TypeOf(state)) = .empty;
|
||||||
|
defer state_stack.deinit(allocator);
|
||||||
|
|
||||||
var iter = snippet;
|
var iter = snippet;
|
||||||
while (iter.len > 0) : (iter = iter[1..]) {
|
while (iter.len > 0) : (iter = iter[1..]) {
|
||||||
const c = iter[0];
|
const c = iter[0];
|
||||||
fsm: switch (state) {
|
fsm: switch (state) {
|
||||||
.initial => switch (c) {
|
.initial => switch (c) {
|
||||||
'\\' => {
|
'\\' => {
|
||||||
|
(try state_stack.addOne(allocator)).* = state;
|
||||||
state = .escape;
|
state = .escape;
|
||||||
},
|
},
|
||||||
'$' => {
|
'$' => {
|
||||||
|
(try state_stack.addOne(allocator)).* = state;
|
||||||
state = .tabstop;
|
state = .tabstop;
|
||||||
},
|
},
|
||||||
else => try text.writer.writeByte(c),
|
else => try text.writer.writeByte(c),
|
||||||
},
|
},
|
||||||
.escape => {
|
.escape => {
|
||||||
try text.writer.writeByte(c);
|
try text.writer.writeByte(c);
|
||||||
state = .initial;
|
state = state_stack.pop() orelse return error.InvalidState;
|
||||||
},
|
},
|
||||||
.tabstop => switch (c) {
|
.tabstop => switch (c) {
|
||||||
'{' => {
|
'{' => {
|
||||||
|
|
@ -69,7 +74,7 @@ pub fn parse(allocator: std.mem.Allocator, snippet: []const u8) Error!Snippet {
|
||||||
};
|
};
|
||||||
max_id = @max(id orelse unreachable, max_id);
|
max_id = @max(id orelse unreachable, max_id);
|
||||||
id = null;
|
id = null;
|
||||||
state = .initial;
|
state = state_stack.pop() orelse return error.InvalidState;
|
||||||
continue :fsm .initial;
|
continue :fsm .initial;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -82,7 +87,7 @@ pub fn parse(allocator: std.mem.Allocator, snippet: []const u8) Error!Snippet {
|
||||||
const pos = snippet.len - iter.len;
|
const pos = snippet.len - iter.len;
|
||||||
if (id == null)
|
if (id == null)
|
||||||
return invalid(snippet, pos, error.InvalidIdValue);
|
return invalid(snippet, pos, error.InvalidIdValue);
|
||||||
content_begin = .{text.written().len};
|
(try content_begin.addOne(allocator)).* = .{text.written().len};
|
||||||
state = .content;
|
state = .content;
|
||||||
},
|
},
|
||||||
else => {
|
else => {
|
||||||
|
|
@ -92,32 +97,29 @@ pub fn parse(allocator: std.mem.Allocator, snippet: []const u8) Error!Snippet {
|
||||||
},
|
},
|
||||||
.content => switch (c) {
|
.content => switch (c) {
|
||||||
'\\' => {
|
'\\' => {
|
||||||
state = .content_escape;
|
(try state_stack.addOne(allocator)).* = state;
|
||||||
|
state = .escape;
|
||||||
},
|
},
|
||||||
'}' => {
|
'}' => {
|
||||||
const pos = snippet.len - iter.len;
|
const pos = snippet.len - iter.len;
|
||||||
if (id == null)
|
if (id == null)
|
||||||
return invalid(snippet, pos, error.InvalidIdValue);
|
return invalid(snippet, pos, error.InvalidIdValue);
|
||||||
if (content_begin == null)
|
if (content_begin.items.len == 0)
|
||||||
return invalid(snippet, pos, error.InvalidPlaceholderValue);
|
return invalid(snippet, pos, error.InvalidPlaceholderValue);
|
||||||
|
const begin_pos = content_begin.pop() orelse return invalid(snippet, pos, error.InvalidPlaceholderValue);
|
||||||
(try tabstops.addOne(allocator)).* = .{
|
(try tabstops.addOne(allocator)).* = .{
|
||||||
.id = id orelse unreachable,
|
.id = id orelse unreachable,
|
||||||
.range = .{
|
.range = .{
|
||||||
.begin = content_begin orelse unreachable,
|
.begin = begin_pos,
|
||||||
.end = .{text.written().len},
|
.end = .{text.written().len},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
max_id = @max(id orelse unreachable, max_id);
|
max_id = @max(id orelse unreachable, max_id);
|
||||||
id = null;
|
id = null;
|
||||||
content_begin = null;
|
state = state_stack.pop() orelse return error.InvalidState;
|
||||||
state = .initial;
|
|
||||||
},
|
},
|
||||||
else => try text.writer.writeByte(c),
|
else => try text.writer.writeByte(c),
|
||||||
},
|
},
|
||||||
.content_escape => {
|
|
||||||
try text.writer.writeByte(c);
|
|
||||||
state = .content;
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -164,6 +166,7 @@ pub const Error = error{
|
||||||
InvalidIdValue,
|
InvalidIdValue,
|
||||||
InvalidPlaceholderValue,
|
InvalidPlaceholderValue,
|
||||||
UnexpectedEndOfDocument,
|
UnexpectedEndOfDocument,
|
||||||
|
InvalidState,
|
||||||
};
|
};
|
||||||
|
|
||||||
const log = std.log.scoped(.snippet);
|
const log = std.log.scoped(.snippet);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue