First attempt at adding syntax highlighting

This commit is contained in:
Sijmen 2020-05-28 00:54:11 +02:00
parent 9dee6e739c
commit 72f0ea221c
Signed by: vijfhoek
GPG key ID: DAF7821E067D9C48
7 changed files with 261 additions and 66 deletions

1
.gitignore vendored
View file

@ -1 +1,2 @@
zig-cache/
tmp/

6
.gitmodules vendored
View file

@ -1,3 +1,9 @@
[submodule "vendor/zig-sdl"]
path = vendor/zig-sdl
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

View file

@ -11,10 +11,16 @@ pub fn build(b: *Builder) void {
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
const mode = b.standardReleaseOptions();
const cflags = &[_][]const u8{};
const exe = b.addExecutable("zite", "src/main.zig");
exe.linkSystemLibrary("c");
exe.linkSystemLibrary("SDL2");
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");

View file

@ -9,6 +9,20 @@ pub const ColorTheme = struct {
treeFolderFg: 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 {
var fg = sdl.Color{ .r = 56, .g = 58, .b = 68 };
return ColorTheme{
@ -18,6 +32,20 @@ pub const ColorTheme = struct {
.treeFileFg = fg,
.treeFolderFg = sdl.Color{ .r = 64, .g = 120, .b = 242 },
.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,
};
}
};

View file

@ -1,40 +1,111 @@
const std = @import("std");
const zig_sdl = @import("zig-sdl");
const sdl = zig_sdl.sdl;
const sdl = @import("zig-sdl").sdl;
const ts = @cImport(@cInclude("tree_sitter/api.h"));
const Ui = @import("ui.zig").Ui;
const TreeView = @import("treeview.zig").TreeView;
const colorTheme = @import("color_theme.zig").colorTheme;
pub const DocView = struct {
pub const Line = struct {
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: *Line, ui: *Ui) !void {
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, color);
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.textWidth, self.textHeight });
// std.debug.warn("surface size: '{}' {}x{}\n", .{ self.string, self.textWidth, self.textHeight });
self.texture = try ui.renderer.createTextureFromSurface(surface);
}
pub fn render(self: *Line, ui: *Ui) !void {
pub fn render(self: *Segment, ui: *Ui) !void {
if (self.string.len == 0) {
return;
}
@ -44,7 +115,7 @@ pub const DocView = struct {
}
var dst = sdl.Rect{
.x = ui.scale(i32, DocView.x + 10),
.x = ui.scale(i32, DocView.x + self.x + 10),
.y = ui.scale(i32, self.y),
.w = self.textWidth,
.h = self.textHeight,
@ -55,46 +126,127 @@ pub const DocView = struct {
}
};
pub const DocView = struct {
pub const x = TreeView.width + 10;
path: ?[]const u8 = null,
// TODO Do allocator stuff
count: u64 = 0,
lines: [4096]Line = undefined,
segments: [4096]Segment = undefined,
pub fn init() DocView {
return DocView{};
}
pub fn render(self: *DocView, ui: *Ui) !void {
for (self.lines[0..self.count]) |*line| {
try line.render(ui);
for (self.segments[0..self.count]) |*segment| {
try segment.render(ui);
}
}
pub fn open(self: *DocView, path: []const u8) !void {
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| {
std.debug.warn("unable to open file: {}\n", .{err});
return;
};
defer file.close();
var line_buf: [1024 * 1024]u8 = undefined;
var len = file.readAll(&line_buf) catch unreachable;
var buffer: [1024 * 1024]u8 = undefined;
var len = file.readAll(&buffer) catch unreachable;
buffer[len] = 0;
file.close();
var iterator = std.mem.split(line_buf[0..len], "\n");
var index: u32 = 0;
while (iterator.next()) |line| {
std.debug.warn("line {}: '{}'\n", .{ index, line });
self.lines[index] = Line{
.string = line,
.y = @bitCast(i32, index) * Line.height + 10,
std.debug.warn("{s}\n", .{buffer});
var source_code =
\\#include <assert.h>
\\int main(int argc, char** argv) {
\\ int a = 3 + argc;
\\ printf("%d\n", a);
\\}
;
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;
};
index += 1;
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

@ -0,0 +1 @@
Subproject commit ec870e9e66c34354133ad865dd12fbaceb021083

1
vendor/tree-sitter-c vendored Submodule

@ -0,0 +1 @@
Subproject commit 99151b1e9293c9e025498fee7e6691e1a52e1d03