First attempt at adding syntax highlighting
This commit is contained in:
parent
9dee6e739c
commit
72f0ea221c
7 changed files with 261 additions and 66 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1 +1,2 @@
|
||||||
zig-cache/
|
zig-cache/
|
||||||
|
tmp/
|
||||||
|
|
6
.gitmodules
vendored
6
.gitmodules
vendored
|
@ -1,3 +1,9 @@
|
||||||
[submodule "vendor/zig-sdl"]
|
[submodule "vendor/zig-sdl"]
|
||||||
path = vendor/zig-sdl
|
path = vendor/zig-sdl
|
||||||
url = https://git.sijman.nl/zig/zig-sdl.git
|
url = https://git.sijman.nl/zig/zig-sdl.git
|
||||||
|
[submodule "vendor/tree-sitter-c"]
|
||||||
|
path = vendor/tree-sitter-c
|
||||||
|
url = https://github.com/tree-sitter/tree-sitter-c.git
|
||||||
|
[submodule "vendor/tree-sitter"]
|
||||||
|
path = vendor/tree-sitter
|
||||||
|
url = https://github.com/tree-sitter/tree-sitter.git
|
||||||
|
|
|
@ -11,10 +11,16 @@ pub fn build(b: *Builder) void {
|
||||||
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
|
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
|
||||||
const mode = b.standardReleaseOptions();
|
const mode = b.standardReleaseOptions();
|
||||||
|
|
||||||
|
const cflags = &[_][]const u8{};
|
||||||
|
|
||||||
const exe = b.addExecutable("zite", "src/main.zig");
|
const exe = b.addExecutable("zite", "src/main.zig");
|
||||||
exe.linkSystemLibrary("c");
|
exe.linkSystemLibrary("c");
|
||||||
exe.linkSystemLibrary("SDL2");
|
exe.linkSystemLibrary("SDL2");
|
||||||
exe.linkSystemLibrary("SDL2_ttf");
|
exe.linkSystemLibrary("SDL2_ttf");
|
||||||
|
exe.addIncludeDir("vendor/tree-sitter/lib/include");
|
||||||
|
exe.addIncludeDir("vendor/tree-sitter/lib/src");
|
||||||
|
exe.addObjectFile("vendor/tree-sitter/libtree-sitter.a");
|
||||||
|
exe.addCSourceFile("vendor/tree-sitter-c/src/parser.c", cflags);
|
||||||
|
|
||||||
exe.addPackagePath("zig-sdl", "vendor/zig-sdl/src/main.zig");
|
exe.addPackagePath("zig-sdl", "vendor/zig-sdl/src/main.zig");
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,20 @@ pub const ColorTheme = struct {
|
||||||
treeFolderFg: sdl.Color,
|
treeFolderFg: sdl.Color,
|
||||||
treeEntryHighlightBg: sdl.Color,
|
treeEntryHighlightBg: sdl.Color,
|
||||||
|
|
||||||
|
commentFg: sdl.Color,
|
||||||
|
constantFg: sdl.Color,
|
||||||
|
delimiterFg: sdl.Color,
|
||||||
|
functionFg: sdl.Color,
|
||||||
|
functionSpecialFg: sdl.Color,
|
||||||
|
keywordFg: sdl.Color,
|
||||||
|
labelFg: sdl.Color,
|
||||||
|
numberFg: sdl.Color,
|
||||||
|
operatorFg: sdl.Color,
|
||||||
|
propertyFg: sdl.Color,
|
||||||
|
stringFg: sdl.Color,
|
||||||
|
typeFg: sdl.Color,
|
||||||
|
variableFg: sdl.Color,
|
||||||
|
|
||||||
fn init() ColorTheme {
|
fn init() ColorTheme {
|
||||||
var fg = sdl.Color{ .r = 56, .g = 58, .b = 68 };
|
var fg = sdl.Color{ .r = 56, .g = 58, .b = 68 };
|
||||||
return ColorTheme{
|
return ColorTheme{
|
||||||
|
@ -18,6 +32,20 @@ pub const ColorTheme = struct {
|
||||||
.treeFileFg = fg,
|
.treeFileFg = fg,
|
||||||
.treeFolderFg = sdl.Color{ .r = 64, .g = 120, .b = 242 },
|
.treeFolderFg = sdl.Color{ .r = 64, .g = 120, .b = 242 },
|
||||||
.treeEntryHighlightBg = sdl.Color{ .r = 229, .g = 229, .b = 229 },
|
.treeEntryHighlightBg = sdl.Color{ .r = 229, .g = 229, .b = 229 },
|
||||||
|
|
||||||
|
.commentFg = sdl.Color{ .r = 0x50, .g = 0xa1, .b = 0x4f },
|
||||||
|
.constantFg = fg,
|
||||||
|
.delimiterFg = fg,
|
||||||
|
.functionFg = fg,
|
||||||
|
.functionSpecialFg = sdl.Color{ .r = 0xa6, .g = 0x26, .b = 0xa4 },
|
||||||
|
.keywordFg = sdl.Color{ .r = 0xa6, .g = 0x26, .b = 0xa4 },
|
||||||
|
.labelFg = fg,
|
||||||
|
.numberFg = sdl.Color{ .r = 0x98, .g = 0x68, .b = 0x01 },
|
||||||
|
.operatorFg = fg,
|
||||||
|
.propertyFg = fg,
|
||||||
|
.stringFg = sdl.Color{ .r = 0x50, .g = 0xa1, .b = 0x4f },
|
||||||
|
.typeFg = fg,
|
||||||
|
.variableFg = fg,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
284
src/docview.zig
284
src/docview.zig
|
@ -1,100 +1,252 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const zig_sdl = @import("zig-sdl");
|
const sdl = @import("zig-sdl").sdl;
|
||||||
const sdl = zig_sdl.sdl;
|
const ts = @cImport(@cInclude("tree_sitter/api.h"));
|
||||||
|
|
||||||
const Ui = @import("ui.zig").Ui;
|
const Ui = @import("ui.zig").Ui;
|
||||||
const TreeView = @import("treeview.zig").TreeView;
|
const TreeView = @import("treeview.zig").TreeView;
|
||||||
const colorTheme = @import("color_theme.zig").colorTheme;
|
const colorTheme = @import("color_theme.zig").colorTheme;
|
||||||
|
|
||||||
|
extern fn tree_sitter_c() ?*ts.TSLanguage;
|
||||||
|
|
||||||
|
pub const SegmentKind = enum {
|
||||||
|
Comment,
|
||||||
|
Constant,
|
||||||
|
Delimiter,
|
||||||
|
Function,
|
||||||
|
FunctionSpecial,
|
||||||
|
Keyword,
|
||||||
|
Label,
|
||||||
|
Number,
|
||||||
|
Operator,
|
||||||
|
Property,
|
||||||
|
String,
|
||||||
|
Type,
|
||||||
|
Variable,
|
||||||
|
|
||||||
|
fn init(string: []const u8) ?SegmentKind {
|
||||||
|
return if (std.mem.eql(u8, string, "comment"))
|
||||||
|
SegmentKind.Comment
|
||||||
|
else if (std.mem.eql(u8, string, "constant"))
|
||||||
|
SegmentKind.Constant
|
||||||
|
else if (std.mem.eql(u8, string, "delimiter"))
|
||||||
|
SegmentKind.Delimiter
|
||||||
|
else if (std.mem.eql(u8, string, "function"))
|
||||||
|
SegmentKind.Function
|
||||||
|
else if (std.mem.eql(u8, string, "function.special"))
|
||||||
|
SegmentKind.FunctionSpecial
|
||||||
|
else if (std.mem.eql(u8, string, "keyword"))
|
||||||
|
SegmentKind.Keyword
|
||||||
|
else if (std.mem.eql(u8, string, "label"))
|
||||||
|
SegmentKind.Label
|
||||||
|
else if (std.mem.eql(u8, string, "number"))
|
||||||
|
SegmentKind.Number
|
||||||
|
else if (std.mem.eql(u8, string, "operator"))
|
||||||
|
SegmentKind.Operator
|
||||||
|
else if (std.mem.eql(u8, string, "property"))
|
||||||
|
SegmentKind.Property
|
||||||
|
else if (std.mem.eql(u8, string, "string"))
|
||||||
|
SegmentKind.String
|
||||||
|
else if (std.mem.eql(u8, string, "type"))
|
||||||
|
SegmentKind.Type
|
||||||
|
else if (std.mem.eql(u8, string, "variable"))
|
||||||
|
SegmentKind.Variable
|
||||||
|
else
|
||||||
|
null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn color(self: SegmentKind) sdl.Color {
|
||||||
|
return switch (self) {
|
||||||
|
.Comment => colorTheme.commentFg,
|
||||||
|
.Constant => colorTheme.constantFg,
|
||||||
|
.Delimiter => colorTheme.delimiterFg,
|
||||||
|
.Function => colorTheme.functionFg,
|
||||||
|
.FunctionSpecial => colorTheme.functionSpecialFg,
|
||||||
|
.Keyword => colorTheme.keywordFg,
|
||||||
|
.Label => colorTheme.labelFg,
|
||||||
|
.Number => colorTheme.numberFg,
|
||||||
|
.Operator => colorTheme.operatorFg,
|
||||||
|
.Property => colorTheme.propertyFg,
|
||||||
|
.String => colorTheme.stringFg,
|
||||||
|
.Type => colorTheme.typeFg,
|
||||||
|
.Variable => colorTheme.variableFg,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Segment = struct {
|
||||||
|
const height: i32 = 20;
|
||||||
|
|
||||||
|
string: []const u8,
|
||||||
|
kind: ?SegmentKind = null,
|
||||||
|
|
||||||
|
texture: ?sdl.Texture = null,
|
||||||
|
|
||||||
|
x: i32 = 0,
|
||||||
|
y: i32 = 0,
|
||||||
|
textWidth: i32 = 0,
|
||||||
|
textHeight: i32 = 0,
|
||||||
|
|
||||||
|
fn prerender(self: *Segment, ui: *Ui) !void {
|
||||||
|
if (self.texture) |texture| {
|
||||||
|
texture.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
var color = colorTheme.fg;
|
||||||
|
var line = try std.mem.dupeZ(std.heap.c_allocator, u8, self.string);
|
||||||
|
var surface = try ui.codeFont.renderBlended(
|
||||||
|
line,
|
||||||
|
if (self.kind) |kind| kind.color() else colorTheme.fg,
|
||||||
|
);
|
||||||
|
defer surface.deinit();
|
||||||
|
|
||||||
|
self.textWidth = surface.width();
|
||||||
|
self.textHeight = surface.height();
|
||||||
|
// std.debug.warn("surface size: '{}' {}x{}\n", .{ self.string, self.textWidth, self.textHeight });
|
||||||
|
self.texture = try ui.renderer.createTextureFromSurface(surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render(self: *Segment, ui: *Ui) !void {
|
||||||
|
if (self.string.len == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.texture == null) {
|
||||||
|
try self.prerender(ui);
|
||||||
|
}
|
||||||
|
|
||||||
|
var dst = sdl.Rect{
|
||||||
|
.x = ui.scale(i32, DocView.x + self.x + 10),
|
||||||
|
.y = ui.scale(i32, self.y),
|
||||||
|
.w = self.textWidth,
|
||||||
|
.h = self.textHeight,
|
||||||
|
};
|
||||||
|
if (self.texture) |texture| {
|
||||||
|
try ui.renderer.copy(texture, null, dst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
pub const DocView = struct {
|
pub const DocView = struct {
|
||||||
pub const Line = struct {
|
|
||||||
const height: i32 = 20;
|
|
||||||
|
|
||||||
string: []const u8,
|
|
||||||
|
|
||||||
texture: ?sdl.Texture = null,
|
|
||||||
|
|
||||||
y: i32 = 0,
|
|
||||||
textWidth: i32 = 0,
|
|
||||||
textHeight: i32 = 0,
|
|
||||||
|
|
||||||
fn prerender(self: *Line, ui: *Ui) !void {
|
|
||||||
if (self.texture) |texture| {
|
|
||||||
texture.deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
var color = colorTheme.fg;
|
|
||||||
var line = try std.mem.dupeZ(std.heap.c_allocator, u8, self.string);
|
|
||||||
var surface = try ui.codeFont.renderBlended(line, color);
|
|
||||||
defer surface.deinit();
|
|
||||||
|
|
||||||
self.textWidth = surface.width();
|
|
||||||
self.textHeight = surface.height();
|
|
||||||
std.debug.warn("surface size: {}x{}\n", .{ self.textWidth, self.textHeight });
|
|
||||||
self.texture = try ui.renderer.createTextureFromSurface(surface);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render(self: *Line, ui: *Ui) !void {
|
|
||||||
if (self.string.len == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self.texture == null) {
|
|
||||||
try self.prerender(ui);
|
|
||||||
}
|
|
||||||
|
|
||||||
var dst = sdl.Rect{
|
|
||||||
.x = ui.scale(i32, DocView.x + 10),
|
|
||||||
.y = ui.scale(i32, self.y),
|
|
||||||
.w = self.textWidth,
|
|
||||||
.h = self.textHeight,
|
|
||||||
};
|
|
||||||
if (self.texture) |texture| {
|
|
||||||
try ui.renderer.copy(texture, null, dst);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const x = TreeView.width + 10;
|
pub const x = TreeView.width + 10;
|
||||||
|
|
||||||
path: ?[]const u8 = null,
|
path: ?[]const u8 = null,
|
||||||
|
|
||||||
// TODO Do allocator stuff
|
// TODO Do allocator stuff
|
||||||
count: u64 = 0,
|
count: u64 = 0,
|
||||||
lines: [4096]Line = undefined,
|
segments: [4096]Segment = undefined,
|
||||||
|
|
||||||
pub fn init() DocView {
|
pub fn init() DocView {
|
||||||
return DocView{};
|
return DocView{};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(self: *DocView, ui: *Ui) !void {
|
pub fn render(self: *DocView, ui: *Ui) !void {
|
||||||
for (self.lines[0..self.count]) |*line| {
|
for (self.segments[0..self.count]) |*segment| {
|
||||||
try line.render(ui);
|
try segment.render(ui);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open(self: *DocView, path: []const u8) !void {
|
pub fn open(self: *DocView, path: []const u8) !void {
|
||||||
self.path = path;
|
self.path = path;
|
||||||
|
|
||||||
|
var parser = ts.ts_parser_new() orelse unreachable;
|
||||||
|
defer ts.ts_parser_delete(parser);
|
||||||
|
|
||||||
|
var language = tree_sitter_c();
|
||||||
|
_ = ts.ts_parser_set_language(parser, language);
|
||||||
|
|
||||||
const file = std.fs.cwd().openFile(self.path.?, .{}) catch |err| {
|
const file = std.fs.cwd().openFile(self.path.?, .{}) catch |err| {
|
||||||
std.debug.warn("unable to open file: {}\n", .{err});
|
std.debug.warn("unable to open file: {}\n", .{err});
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
defer file.close();
|
|
||||||
|
|
||||||
var line_buf: [1024 * 1024]u8 = undefined;
|
var buffer: [1024 * 1024]u8 = undefined;
|
||||||
var len = file.readAll(&line_buf) catch unreachable;
|
var len = file.readAll(&buffer) catch unreachable;
|
||||||
|
buffer[len] = 0;
|
||||||
|
file.close();
|
||||||
|
|
||||||
var iterator = std.mem.split(line_buf[0..len], "\n");
|
std.debug.warn("{s}\n", .{buffer});
|
||||||
var index: u32 = 0;
|
var source_code =
|
||||||
while (iterator.next()) |line| {
|
\\#include <assert.h>
|
||||||
std.debug.warn("line {}: '{}'\n", .{ index, line });
|
\\int main(int argc, char** argv) {
|
||||||
self.lines[index] = Line{
|
\\ int a = 3 + argc;
|
||||||
.string = line,
|
\\ printf("%d\n", a);
|
||||||
.y = @bitCast(i32, index) * Line.height + 10,
|
\\}
|
||||||
};
|
;
|
||||||
index += 1;
|
|
||||||
|
var tree = ts.ts_parser_parse_string(
|
||||||
|
parser,
|
||||||
|
null,
|
||||||
|
&buffer,
|
||||||
|
@intCast(u32, len),
|
||||||
|
) orelse unreachable;
|
||||||
|
defer ts.ts_tree_delete(tree);
|
||||||
|
|
||||||
|
var rootNode = ts.ts_tree_root_node(tree);
|
||||||
|
|
||||||
|
var highlightsQuery: [4096]u8 = undefined;
|
||||||
|
var highlightsFile = std.fs.cwd().openFile(
|
||||||
|
"vendor/tree-sitter-c/queries/highlights.scm",
|
||||||
|
.{},
|
||||||
|
) catch unreachable;
|
||||||
|
var highlightsQueryLen = highlightsFile.readAll(&highlightsQuery) catch unreachable;
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
var errorOffset: u32 = undefined;
|
||||||
|
var errorType: ts.TSQueryError = undefined;
|
||||||
|
var query = ts.ts_query_new(
|
||||||
|
language,
|
||||||
|
&highlightsQuery,
|
||||||
|
@intCast(u32, highlightsQueryLen),
|
||||||
|
&errorOffset,
|
||||||
|
&errorType,
|
||||||
|
) orelse {
|
||||||
|
std.debug.warn(
|
||||||
|
"error parsing query at {}: {}\n",
|
||||||
|
.{ errorOffset, @tagName(errorType) },
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
defer ts.ts_query_delete(query);
|
||||||
|
|
||||||
|
//var captures = [_][*c]const u8{null} ** ts.ts_query_capture_count(query);
|
||||||
|
var captureCount = ts.ts_query_capture_count(query);
|
||||||
|
var captures = std.ArrayList([]const u8).init(std.heap.c_allocator);
|
||||||
|
defer captures.deinit();
|
||||||
|
|
||||||
|
var cursor = ts.ts_query_cursor_new();
|
||||||
|
defer ts.ts_query_cursor_delete(cursor);
|
||||||
|
ts.ts_query_cursor_exec(cursor, query, rootNode);
|
||||||
|
|
||||||
|
var match: ts.TSQueryMatch = undefined;
|
||||||
|
var segmentIndex: usize = 0;
|
||||||
|
while (ts.ts_query_cursor_next_match(cursor, &match)) : (segmentIndex += 1) {
|
||||||
|
std.debug.warn("[{:>3}] index={}: ", .{ match.id, match.pattern_index });
|
||||||
|
|
||||||
|
var i: usize = 0;
|
||||||
|
while (i < match.capture_count) : (i += 1) {
|
||||||
|
var capture = &match.captures[i];
|
||||||
|
var captureNameLength: u32 = 0;
|
||||||
|
var captureName = ts.ts_query_capture_name_for_id(
|
||||||
|
query,
|
||||||
|
@intCast(u32, capture.index),
|
||||||
|
&captureNameLength,
|
||||||
|
);
|
||||||
|
var node = capture.node;
|
||||||
|
|
||||||
|
var startByte = ts.ts_node_start_byte(node);
|
||||||
|
var endByte = ts.ts_node_end_byte(node);
|
||||||
|
var startPoint = ts.ts_node_start_point(node);
|
||||||
|
//std.debug.warn("[{d:>2}-{d:<2}] {s:<40} {s:<40} ", .{ start, end, string, parent });
|
||||||
|
|
||||||
|
self.segments[segmentIndex] = Segment{
|
||||||
|
.string = buffer[startByte..endByte],
|
||||||
|
.x = @intCast(i32, startPoint.column) * 8,
|
||||||
|
.y = @intCast(i32, startPoint.row) * Segment.height + 10,
|
||||||
|
.kind = SegmentKind.init(captureName[0..captureNameLength]),
|
||||||
|
};
|
||||||
|
std.debug.warn("{}\n", .{self.segments[segmentIndex]});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.count = index;
|
self.count = segmentIndex;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
1
vendor/tree-sitter
vendored
Submodule
1
vendor/tree-sitter
vendored
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit ec870e9e66c34354133ad865dd12fbaceb021083
|
1
vendor/tree-sitter-c
vendored
Submodule
1
vendor/tree-sitter-c
vendored
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 99151b1e9293c9e025498fee7e6691e1a52e1d03
|
Loading…
Reference in a new issue