actually fully implement the highlighter within a plugin + a rust one too

plugins
Patrick Cleavelin 2024-01-05 14:16:42 -06:00
parent 9e8e8f4207
commit 8891213a0a
7 changed files with 704 additions and 470 deletions

View File

@ -4,7 +4,7 @@ editor: src/*.odin rg odin_highlighter
odin build src/ -out:bin/editor -lld
odin_highlighter:
odin build plugins/odin_highlighter/src/ -build-mode:dll -no-entry-point -out:bin/odin_highlighter
odin build plugins/highlighter/src/ -build-mode:dll -no-entry-point -out:bin/highlighter
rg:
cargo b --manifest-path=lib-rg/Cargo.toml

View File

@ -0,0 +1,418 @@
// The default syntax highlighter plugin for Odin
package highlighter;
import "core:runtime"
import "core:fmt"
import p "../../../src/plugin"
Plugin :: p.Plugin;
Iterator :: p.Iterator;
BufferIter :: p.BufferIter;
BufferIndex :: p.BufferIndex;
@export
OnInitialize :: proc "c" (plugin: Plugin) {
context = runtime.default_context();
fmt.println("Hello from the Odin Highlighter Plugin!");
plugin.register_highlighter(plugin.state, ".odin", color_buffer_odin);
plugin.register_highlighter(plugin.state, ".rs", color_buffer_rust);
}
@export
OnExit :: proc "c" () {
context = runtime.default_context();
fmt.println("Goodbye from the Odin Highlighter Plugin!");
}
@export
OnDraw :: proc "c" (plugin: Plugin) {
context = runtime.default_context();
}
iterate_buffer :: proc(state: rawptr, iter_funcs: Iterator, it: ^BufferIter) -> (character: u8, idx: BufferIndex, cond: bool) {
result := iter_funcs.iterate_buffer(state, it);
return result.char, it.cursor.index, result.should_stop;
}
iterate_buffer_reverse :: proc(state: rawptr, iter_funcs: Iterator, it: ^BufferIter) -> (character: u8, idx: BufferIndex, cond: bool) {
result := iter_funcs.iterate_buffer_reverse(state, it);
return result.char, it.cursor.index, result.should_stop;
}
iterate_buffer_until :: proc(plugin: Plugin, it: ^BufferIter, until_proc: rawptr) {
plugin.iter.iterate_buffer_until(plugin.state, it, until_proc);
}
iterate_buffer_peek :: proc(plugin: Plugin, it: ^BufferIter) -> (character: u8, idx: BufferIndex, cond: bool) {
result := plugin.iter.iterate_buffer_peek(plugin.state, it);
return result.char, it.cursor.index, result.should_stop;
}
is_odin_keyword :: proc(plugin: Plugin, start: BufferIter, end: BufferIter) -> (matches: bool) {
keywords := []string {
"using",
"transmute",
"cast",
"distinct",
"opaque",
"where",
"struct",
"enum",
"union",
"bit_field",
"bit_set",
"if",
"when",
"else",
"do",
"for",
"switch",
"case",
"continue",
"break",
"size_of",
"offset_of",
"type_info_of",
"typeid_of",
"type_of",
"align_of",
"or_return",
"or_else",
"inline",
"no_inline",
"string",
"cstring",
"bool",
"b8",
"b16",
"b32",
"b64",
"rune",
"any",
"rawptr",
"f16",
"f32",
"f64",
"f16le",
"f16be",
"f32le",
"f32be",
"f64le",
"f64be",
"u8",
"u16",
"u32",
"u64",
"u128",
"u16le",
"u32le",
"u64le",
"u128le",
"u16be",
"u32be",
"u64be",
"u128be",
"uint",
"uintptr",
"i8",
"i16",
"i32",
"i64",
"i128",
"i16le",
"i32le",
"i64le",
"i128le",
"i16be",
"i32be",
"i64be",
"i128be",
"int",
"complex",
"complex32",
"complex64",
"complex128",
"quaternion",
"quaternion64",
"quaternion128",
"quaternion256",
"matrix",
"typeid",
"true",
"false",
"nil",
"dynamic",
"map",
"proc",
"in",
"notin",
"not_in",
"import",
"export",
"foreign",
"const",
"package",
"return",
"defer",
};
for keyword in keywords {
it := start;
keyword_index := 0;
for character in iterate_buffer(plugin.state, plugin.iter, &it) {
if character != keyword[keyword_index] {
break;
}
keyword_index += 1;
if keyword_index >= len(keyword)-1 && it == end {
if plugin.iter.get_char_at_iter(plugin.state, &it) == keyword[keyword_index] {
matches = true;
}
break;
} else if keyword_index >= len(keyword)-1 {
break;
} else if it == end {
break;
}
}
if matches {
break;
}
}
return;
}
is_rust_keyword :: proc(plugin: Plugin, start: BufferIter, end: BufferIter) -> (matches: bool) {
keywords := []string {
"as",
"break",
"const",
"continue",
"crate",
"else",
"enum",
"extern",
"false",
"fn",
"for",
"if",
"impl",
"in",
"let",
"loop",
"match",
"mod",
"move",
"mut",
"pub",
"ref",
"return",
"self",
"Self",
"static",
"struct",
"super",
"trait",
"true",
"type",
"unsafe",
"use",
"where",
"while",
"u8",
"i8",
"u16",
"i16",
"u32",
"i32",
"u64",
"i64",
"bool",
"usize",
"isize",
"str",
"String",
"Option",
"Result",
};
for keyword in keywords {
it := start;
keyword_index := 0;
for character in iterate_buffer(plugin.state, plugin.iter, &it) {
if character != keyword[keyword_index] {
break;
}
keyword_index += 1;
if keyword_index >= len(keyword)-1 && it == end {
if plugin.iter.get_char_at_iter(plugin.state, &it) == keyword[keyword_index] {
matches = true;
}
break;
} else if keyword_index >= len(keyword)-1 {
break;
} else if it == end {
break;
}
}
if matches {
break;
}
}
return;
}
color_buffer_odin :: proc "c" (plugin: Plugin, buffer: rawptr) {
context = runtime.default_context();
start_it := plugin.iter.get_buffer_iterator(plugin.state, buffer);
it := plugin.iter.get_buffer_iterator(plugin.state, buffer);
buffer := plugin.buffer.get_buffer_info(plugin.state);
for character in iterate_buffer(plugin.state, plugin.iter, &it) {
if it.cursor.line > buffer.glyph_buffer_height && (it.cursor.line - buffer.top_line) > buffer.glyph_buffer_height {
break;
}
if character == '/' {
start_it = it;
// need to go back one character because `it` is on the next character
iterate_buffer_reverse(plugin.state, plugin.iter, &start_it);
character, _, succ := iterate_buffer(plugin.state, plugin.iter, &it);
if !succ { break; }
if character == '/' {
iterate_buffer_until(plugin, &it, plugin.iter.until_line_break);
plugin.buffer.color_char_at(plugin.state, it.buffer, start_it.cursor, it.cursor, 9);
} else if character == '*' {
// TODO: block comments
}
} else if character == '\'' {
start_it = it;
// need to go back one character because `it` is on the next character
iterate_buffer_reverse(plugin.state, plugin.iter, &start_it);
// jump into the quoted text
iterate_buffer_until(plugin, &it, plugin.iter.until_single_quote);
plugin.buffer.color_char_at(plugin.state, it.buffer, start_it.cursor, it.cursor, 12);
iterate_buffer(plugin.state, plugin.iter, &it);
} else if character == '"' {
start_it = it;
// need to go back one character because `it` is on the next character
iterate_buffer_reverse(plugin.state, plugin.iter, &start_it);
// jump into the quoted text
iterate_buffer_until(plugin, &it, plugin.iter.until_double_quote);
plugin.buffer.color_char_at(plugin.state, it.buffer, start_it.cursor, it.cursor, 12);
iterate_buffer(plugin.state, plugin.iter, &it);
} else if (character >= 'a' && character <= 'z') || (character >= 'A' && character <= 'Z') || character == '_' {
start_it = it;
// need to go back one character because `it` is on the next character
iterate_buffer_reverse(plugin.state, plugin.iter, &start_it);
it = start_it;
iterate_buffer_until(plugin, &it, plugin.iter.until_end_of_word);
if is_odin_keyword(plugin, start_it, it) {
plugin.buffer.color_char_at(plugin.state, it.buffer, start_it.cursor, it.cursor, 13);
} else if character, _, cond := iterate_buffer_peek(plugin, &it); cond {
if character == '(' {
plugin.buffer.color_char_at(plugin.state, it.buffer, start_it.cursor, it.cursor, 11);
}
} else {
break;
}
iterate_buffer(plugin.state, plugin.iter, &it);
}
}
}
color_buffer_rust :: proc "c" (plugin: Plugin, buffer: rawptr) {
context = runtime.default_context();
start_it := plugin.iter.get_buffer_iterator(plugin.state, buffer);
it := plugin.iter.get_buffer_iterator(plugin.state, buffer);
buffer := plugin.buffer.get_buffer_info(plugin.state);
for character in iterate_buffer(plugin.state, plugin.iter, &it) {
if it.cursor.line > buffer.glyph_buffer_height && (it.cursor.line - buffer.top_line) > buffer.glyph_buffer_height {
break;
}
if character == '/' {
start_it = it;
// need to go back one character because `it` is on the next character
iterate_buffer_reverse(plugin.state, plugin.iter, &start_it);
character, _, succ := iterate_buffer(plugin.state, plugin.iter, &it);
if !succ { break; }
if character == '/' {
iterate_buffer_until(plugin, &it, plugin.iter.until_line_break);
plugin.buffer.color_char_at(plugin.state, it.buffer, start_it.cursor, it.cursor, 9);
} else if character == '*' {
// TODO: block comments
}
} else if character == '\'' && false {
start_it = it;
// need to go back one character because `it` is on the next character
iterate_buffer_reverse(plugin.state, plugin.iter, &start_it);
// jump into the quoted text
iterate_buffer_until(plugin, &it, plugin.iter.until_single_quote);
plugin.buffer.color_char_at(plugin.state, it.buffer, start_it.cursor, it.cursor, 12);
iterate_buffer(plugin.state, plugin.iter, &it);
} else if character == '"' {
start_it = it;
// need to go back one character because `it` is on the next character
iterate_buffer_reverse(plugin.state, plugin.iter, &start_it);
// jump into the quoted text
iterate_buffer_until(plugin, &it, plugin.iter.until_double_quote);
plugin.buffer.color_char_at(plugin.state, it.buffer, start_it.cursor, it.cursor, 12);
iterate_buffer(plugin.state, plugin.iter, &it);
} else if (character >= 'a' && character <= 'z') || (character >= 'A' && character <= 'Z') || character == '_' {
start_it = it;
// need to go back one character because `it` is on the next character
iterate_buffer_reverse(plugin.state, plugin.iter, &start_it);
it = start_it;
iterate_buffer_until(plugin, &it, plugin.iter.until_end_of_word);
if is_rust_keyword(plugin, start_it, it) {
plugin.buffer.color_char_at(plugin.state, it.buffer, start_it.cursor, it.cursor, 13);
} else if character, _, cond := iterate_buffer_peek(plugin, &it); cond {
if character == '(' || character == '<' {
plugin.buffer.color_char_at(plugin.state, it.buffer, start_it.cursor, it.cursor, 11);
}
} else {
break;
}
iterate_buffer(plugin.state, plugin.iter, &it);
}
}
}

View File

@ -1,260 +0,0 @@
// The default syntax highlighter plugin for Odin
package odin_highlighter;
import "core:runtime"
import "core:fmt"
import p "../../../src/plugin"
Plugin :: p.Plugin;
Iterator :: p.Iterator;
BufferIter :: p.BufferIter;
BufferIndex :: p.BufferIndex;
@export
OnInitialize :: proc "c" (plugin: Plugin) {
context = runtime.default_context();
fmt.println("Hello from the Odin Highlighter Plugin!");
it := plugin.iter.get_current_buffer_iterator(plugin.state);
fmt.println("Look I have an iterator!", it);
}
@export
OnExit :: proc "c" () {
context = runtime.default_context();
fmt.println("Goodbye from the Odin Highlighter Plugin!");
}
@export
OnDraw :: proc "c" (plugin: Plugin) {
context = runtime.default_context();
color_buffer(plugin);
}
iterate_buffer :: proc(state: rawptr, iter_funcs: Iterator, it: ^BufferIter) -> (character: u8, idx: BufferIndex, cond: bool) {
result := iter_funcs.iterate_buffer(state, it);
return result.char, it.cursor.index, result.should_stop;
}
iterate_buffer_reverse :: proc(state: rawptr, iter_funcs: Iterator, it: ^BufferIter) -> (character: u8, idx: BufferIndex, cond: bool) {
result := iter_funcs.iterate_buffer_reverse(state, it);
return result.char, it.cursor.index, result.should_stop;
}
iterate_buffer_until :: proc(plugin: Plugin, it: ^BufferIter, until_proc: rawptr) {
plugin.iter.iterate_buffer_until(plugin.state, it, until_proc);
}
is_keyword :: proc(plugin: Plugin, start: BufferIter, end: BufferIter) -> (matches: bool) {
keywords := []string {
"using",
"transmute",
"cast",
"distinct",
"opaque",
"where",
"struct",
"enum",
"union",
"bit_field",
"bit_set",
"if",
"when",
"else",
"do",
"for",
"switch",
"case",
"continue",
"break",
"size_of",
"offset_of",
"type_info_of",
"typeid_of",
"type_of",
"align_of",
"or_return",
"or_else",
"inline",
"no_inline",
"string",
"cstring",
"bool",
"b8",
"b16",
"b32",
"b64",
"rune",
"any",
"rawptr",
"f16",
"f32",
"f64",
"f16le",
"f16be",
"f32le",
"f32be",
"f64le",
"f64be",
"u8",
"u16",
"u32",
"u64",
"u128",
"u16le",
"u32le",
"u64le",
"u128le",
"u16be",
"u32be",
"u64be",
"u128be",
"uint",
"uintptr",
"i8",
"i16",
"i32",
"i64",
"i128",
"i16le",
"i32le",
"i64le",
"i128le",
"i16be",
"i32be",
"i64be",
"i128be",
"int",
"complex",
"complex32",
"complex64",
"complex128",
"quaternion",
"quaternion64",
"quaternion128",
"quaternion256",
"matrix",
"typeid",
"true",
"false",
"nil",
"dynamic",
"map",
"proc",
"in",
"notin",
"not_in",
"import",
"export",
"foreign",
"const",
"package",
"return",
"defer",
};
for keyword in keywords {
it := start;
keyword_index := 0;
for character in iterate_buffer(plugin.state, plugin.iter, &it) {
if character != keyword[keyword_index] {
break;
}
keyword_index += 1;
if keyword_index >= len(keyword)-1 && it == end {
if plugin.iter.get_char_at_iter(plugin.state, &it) == keyword[keyword_index] {
matches = true;
}
break;
} else if keyword_index >= len(keyword)-1 {
break;
} else if it == end {
break;
}
}
if matches {
break;
}
}
return;
}
color_buffer :: proc(plugin: Plugin) {
start_it := plugin.iter.get_current_buffer_iterator(plugin.state);
it := plugin.iter.get_current_buffer_iterator(plugin.state);
buffer := plugin.buffer.get_buffer_info(plugin.state);
for character in iterate_buffer(plugin.state, plugin.iter, &it) {
if it.cursor.line > buffer.glyph_buffer_height && (it.cursor.line - buffer.top_line) > buffer.glyph_buffer_height {
break;
}
if character == '/' {
start_it = it;
// need to go back one character because `it` is on the next character
iterate_buffer_reverse(plugin.state, plugin.iter, &start_it);
character, _, succ := iterate_buffer(plugin.state, plugin.iter, &it);
if !succ { break; }
if character == '/' {
iterate_buffer_until(plugin, &it, plugin.iter.until_line_break);
plugin.buffer.color_char_at(plugin.state, start_it.cursor, it.cursor, 9);
} else if character == '*' {
// TODO: block comments
}
} else if character == '\'' {
start_it = it;
// need to go back one character because `it` is on the next character
iterate_buffer_reverse(plugin.state, plugin.iter, &start_it);
// jump into the quoted text
iterate_buffer_until(plugin, &it, plugin.iter.until_single_quote);
plugin.buffer.color_char_at(plugin.state, start_it.cursor, it.cursor, 12);
iterate_buffer(plugin.state, plugin.iter, &it);
} else if character == '"' {
start_it = it;
// need to go back one character because `it` is on the next character
iterate_buffer_reverse(plugin.state, plugin.iter, &start_it);
// jump into the quoted text
iterate_buffer_until(plugin, &it, plugin.iter.until_double_quote);
plugin.buffer.color_char_at(plugin.state, start_it.cursor, it.cursor, 12);
iterate_buffer(plugin.state, plugin.iter, &it);
} else if (character >= 'a' && character <= 'z') || (character >= 'A' && character <= 'Z') || character == '_' {
start_it = it;
// need to go back one character because `it` is on the next character
iterate_buffer_reverse(plugin.state, plugin.iter, &start_it);
it = start_it;
iterate_buffer_until(plugin, &it, plugin.iter.until_end_of_word);
if is_keyword(plugin, start_it, it) {
plugin.buffer.color_char_at(plugin.state, start_it.cursor, it.cursor, 13);
}
//else {
// break;
//}
// else if character, _, cond := iterate_peek(&it, iterate_file_buffer); cond {
// if character == '(' {
// color_character(buffer, start_it.cursor, it.cursor, .Green);
// }
// }
iterate_buffer(plugin.state, plugin.iter, &it);
}
}
}

View File

@ -66,6 +66,8 @@ State :: struct {
current_input_map: ^InputMap,
plugins: [dynamic]plugin.Interface,
plugin_vtable: plugin.Plugin,
highlighters: map[string]plugin.OnColorBufferProc
}
EditorAction :: proc(state: ^State);

View File

@ -48,6 +48,8 @@ FileBuffer :: struct {
directory: string,
file_path: string,
extension: string,
top_line: int,
cursor: Cursor,
@ -584,6 +586,8 @@ new_file_buffer :: proc(allocator: mem.Allocator, file_path: string, base_dir: s
dir = filepath.dir(fi.fullpath);
}
extension := filepath.ext(fi.fullpath);
if original_content, success := os.read_entire_file_from_handle(fd); success {
width := 256;
height := 256;
@ -592,6 +596,7 @@ new_file_buffer :: proc(allocator: mem.Allocator, file_path: string, base_dir: s
allocator = allocator,
directory = dir,
file_path = fi.fullpath,
extension = extension,
original_content = slice.clone_to_dynamic(original_content),
added_content = make([dynamic]u8, 0, 1024*1024),
@ -916,13 +921,9 @@ update_glyph_buffer :: proc(buffer: ^FileBuffer) {
draw_file_buffer :: proc(state: ^State, buffer: ^FileBuffer, x: int, y: int, font: raylib.Font, show_line_numbers: bool = true) {
update_glyph_buffer(buffer);
for plugin in state.plugins {
if plugin.on_initialize != nil {
plugin.on_draw(plugin.plugin);
}
if highlighter, exists := state.highlighters[buffer.extension]; exists {
highlighter(state.plugin_vtable, buffer);
}
//color_buffer(buffer);
//update_glyph_buffer(buffer);
padding := 0;
if show_line_numbers {

View File

@ -187,204 +187,7 @@ register_default_input_actions :: proc(input_map: ^core.InputMap) {
}
load_plugins :: proc(state: ^State) -> core.Error {
if loaded_plugin, succ := plugin.try_load_plugin("bin/odin_highlighter.dylib"); succ {
loaded_plugin.plugin = plugin.Plugin {
state = cast(rawptr)state,
iter = plugin.Iterator {
get_current_buffer_iterator = proc "c" (state: rawptr) -> plugin.BufferIter {
state := cast(^State)state;
context = state.ctx;
it := core.new_file_buffer_iter(&state.buffers[state.current_buffer]);
// TODO: make this into a function
return plugin.BufferIter {
cursor = plugin.Cursor {
col = it.cursor.col,
line = it.cursor.line,
index = plugin.BufferIndex {
slice_index = it.cursor.index.slice_index,
content_index = it.cursor.index.content_index,
}
},
buffer = cast(rawptr)it.buffer,
hit_end = it.hit_end,
}
},
get_char_at_iter = proc "c" (state: rawptr, it: ^plugin.BufferIter) -> u8 {
state := cast(^State)state;
context = state.ctx;
internal_it := core.FileBufferIter {
cursor = core.Cursor {
col = it.cursor.col,
line = it.cursor.line,
index = core.FileBufferIndex {
slice_index = it.cursor.index.slice_index,
content_index = it.cursor.index.content_index,
}
},
buffer = cast(^core.FileBuffer)it.buffer,
hit_end = it.hit_end,
}
return core.get_character_at_iter(internal_it);
},
iterate_buffer = proc "c" (state: rawptr, it: ^plugin.BufferIter) -> plugin.IterateResult {
state := cast(^State)state;
context = state.ctx;
// TODO: make this into a function
internal_it := core.FileBufferIter {
cursor = core.Cursor {
col = it.cursor.col,
line = it.cursor.line,
index = core.FileBufferIndex {
slice_index = it.cursor.index.slice_index,
content_index = it.cursor.index.content_index,
}
},
buffer = cast(^core.FileBuffer)it.buffer,
hit_end = it.hit_end,
}
char, _, cond := core.iterate_file_buffer(&internal_it);
it^ = plugin.BufferIter {
cursor = plugin.Cursor {
col = internal_it.cursor.col,
line = internal_it.cursor.line,
index = plugin.BufferIndex {
slice_index = internal_it.cursor.index.slice_index,
content_index = internal_it.cursor.index.content_index,
}
},
buffer = cast(rawptr)internal_it.buffer,
hit_end = internal_it.hit_end,
};
return plugin.IterateResult {
char = char,
should_stop = cond,
};
},
iterate_buffer_reverse = proc "c" (state: rawptr, it: ^plugin.BufferIter) -> plugin.IterateResult {
state := cast(^State)state;
context = state.ctx;
// TODO: make this into a function
internal_it := core.FileBufferIter {
cursor = core.Cursor {
col = it.cursor.col,
line = it.cursor.line,
index = core.FileBufferIndex {
slice_index = it.cursor.index.slice_index,
content_index = it.cursor.index.content_index,
}
},
buffer = cast(^core.FileBuffer)it.buffer,
hit_end = it.hit_end,
}
char, _, cond := core.iterate_file_buffer_reverse(&internal_it);
it^ = plugin.BufferIter {
cursor = plugin.Cursor {
col = internal_it.cursor.col,
line = internal_it.cursor.line,
index = plugin.BufferIndex {
slice_index = internal_it.cursor.index.slice_index,
content_index = internal_it.cursor.index.content_index,
}
},
buffer = cast(rawptr)internal_it.buffer,
hit_end = internal_it.hit_end,
};
return plugin.IterateResult {
char = char,
should_stop = cond,
};
},
iterate_buffer_until = proc "c" (state: rawptr, it: ^plugin.BufferIter, until_proc: rawptr) {
state := cast(^State)state;
context = state.ctx;
// TODO: make this into a function
internal_it := core.FileBufferIter {
cursor = core.Cursor {
col = it.cursor.col,
line = it.cursor.line,
index = core.FileBufferIndex {
slice_index = it.cursor.index.slice_index,
content_index = it.cursor.index.content_index,
}
},
buffer = cast(^core.FileBuffer)it.buffer,
hit_end = it.hit_end,
}
core.iterate_file_buffer_until(&internal_it, transmute(core.UntilProc)until_proc);
it^ = plugin.BufferIter {
cursor = plugin.Cursor {
col = internal_it.cursor.col,
line = internal_it.cursor.line,
index = plugin.BufferIndex {
slice_index = internal_it.cursor.index.slice_index,
content_index = internal_it.cursor.index.content_index,
}
},
buffer = cast(rawptr)internal_it.buffer,
hit_end = internal_it.hit_end,
};
},
until_line_break = transmute(rawptr)core.until_line_break,
until_single_quote = transmute(rawptr)core.until_single_quote,
until_double_quote = transmute(rawptr)core.until_double_quote,
until_end_of_word = transmute(rawptr)core.until_end_of_word,
},
buffer = plugin.Buffer {
get_buffer_info = proc "c" (state: rawptr) -> plugin.BufferInfo {
state := cast(^State)state;
context = state.ctx;
buffer := &state.buffers[state.current_buffer];
return plugin.BufferInfo {
glyph_buffer_width = buffer.glyph_buffer_width,
glyph_buffer_height = buffer.glyph_buffer_height,
top_line = buffer.top_line,
};
},
color_char_at = proc "c" (state: rawptr, start_cursor: plugin.Cursor, end_cursor: plugin.Cursor, palette_index: i32) {
state := cast(^State)state;
context = state.ctx;
buffer := &state.buffers[state.current_buffer];
start_cursor := core.Cursor {
col = start_cursor.col,
line = start_cursor.line,
index = core.FileBufferIndex {
slice_index = start_cursor.index.slice_index,
content_index = start_cursor.index.content_index,
}
};
end_cursor := core.Cursor {
col = end_cursor.col,
line = end_cursor.line,
index = core.FileBufferIndex {
slice_index = end_cursor.index.slice_index,
content_index = end_cursor.index.content_index,
}
};
core.color_character(buffer, start_cursor, end_cursor, cast(theme.PaletteColor)palette_index);
}
}
};
if loaded_plugin, succ := plugin.try_load_plugin("bin/highlighter.dylib"); succ {
append(&state.plugins, loaded_plugin);
fmt.println("Loaded Odin Highlighter plugin");
return core.no_error();
@ -395,12 +198,280 @@ load_plugins :: proc(state: ^State) -> core.Error {
main :: proc() {
state := State {
ctx = context,
source_font_width = 8,
source_font_height = 16,
input_map = core.new_input_map(),
window = nil,
directory = os.get_current_directory(),
plugins = make([dynamic]plugin.Interface),
highlighters = make(map[string]plugin.OnColorBufferProc),
};
state.plugin_vtable = plugin.Plugin {
state = cast(rawptr)&state,
register_highlighter = proc "c" (state: rawptr, extension: cstring, on_color_buffer: plugin.OnColorBufferProc) {
state := cast(^State)state;
context = state.ctx;
extension := strings.clone(string(extension));
if _, exists := state.highlighters[extension]; exists {
fmt.eprintln("Highlighter already registered for", extension, "files");
} else {
state.highlighters[extension] = on_color_buffer;
}
},
iter = plugin.Iterator {
get_current_buffer_iterator = proc "c" (state: rawptr) -> plugin.BufferIter {
state := cast(^State)state;
context = state.ctx;
it := core.new_file_buffer_iter(&state.buffers[state.current_buffer]);
// TODO: make this into a function
return plugin.BufferIter {
cursor = plugin.Cursor {
col = it.cursor.col,
line = it.cursor.line,
index = plugin.BufferIndex {
slice_index = it.cursor.index.slice_index,
content_index = it.cursor.index.content_index,
}
},
buffer = cast(rawptr)it.buffer,
hit_end = it.hit_end,
}
},
get_buffer_iterator = proc "c" (state: rawptr, buffer: rawptr) -> plugin.BufferIter {
state := cast(^State)state;
buffer := cast(^core.FileBuffer)buffer;
context = state.ctx;
it := core.new_file_buffer_iter(buffer);
// TODO: make this into a function
return plugin.BufferIter {
cursor = plugin.Cursor {
col = it.cursor.col,
line = it.cursor.line,
index = plugin.BufferIndex {
slice_index = it.cursor.index.slice_index,
content_index = it.cursor.index.content_index,
}
},
buffer = cast(rawptr)it.buffer,
hit_end = it.hit_end,
}
},
get_char_at_iter = proc "c" (state: rawptr, it: ^plugin.BufferIter) -> u8 {
state := cast(^State)state;
context = state.ctx;
internal_it := core.FileBufferIter {
cursor = core.Cursor {
col = it.cursor.col,
line = it.cursor.line,
index = core.FileBufferIndex {
slice_index = it.cursor.index.slice_index,
content_index = it.cursor.index.content_index,
}
},
buffer = cast(^core.FileBuffer)it.buffer,
hit_end = it.hit_end,
}
return core.get_character_at_iter(internal_it);
},
iterate_buffer = proc "c" (state: rawptr, it: ^plugin.BufferIter) -> plugin.IterateResult {
state := cast(^State)state;
context = state.ctx;
// TODO: make this into a function
internal_it := core.FileBufferIter {
cursor = core.Cursor {
col = it.cursor.col,
line = it.cursor.line,
index = core.FileBufferIndex {
slice_index = it.cursor.index.slice_index,
content_index = it.cursor.index.content_index,
}
},
buffer = cast(^core.FileBuffer)it.buffer,
hit_end = it.hit_end,
}
char, _, cond := core.iterate_file_buffer(&internal_it);
it^ = plugin.BufferIter {
cursor = plugin.Cursor {
col = internal_it.cursor.col,
line = internal_it.cursor.line,
index = plugin.BufferIndex {
slice_index = internal_it.cursor.index.slice_index,
content_index = internal_it.cursor.index.content_index,
}
},
buffer = cast(rawptr)internal_it.buffer,
hit_end = internal_it.hit_end,
};
return plugin.IterateResult {
char = char,
should_stop = cond,
};
},
iterate_buffer_reverse = proc "c" (state: rawptr, it: ^plugin.BufferIter) -> plugin.IterateResult {
state := cast(^State)state;
context = state.ctx;
// TODO: make this into a function
internal_it := core.FileBufferIter {
cursor = core.Cursor {
col = it.cursor.col,
line = it.cursor.line,
index = core.FileBufferIndex {
slice_index = it.cursor.index.slice_index,
content_index = it.cursor.index.content_index,
}
},
buffer = cast(^core.FileBuffer)it.buffer,
hit_end = it.hit_end,
}
char, _, cond := core.iterate_file_buffer_reverse(&internal_it);
it^ = plugin.BufferIter {
cursor = plugin.Cursor {
col = internal_it.cursor.col,
line = internal_it.cursor.line,
index = plugin.BufferIndex {
slice_index = internal_it.cursor.index.slice_index,
content_index = internal_it.cursor.index.content_index,
}
},
buffer = cast(rawptr)internal_it.buffer,
hit_end = internal_it.hit_end,
};
return plugin.IterateResult {
char = char,
should_stop = cond,
};
},
iterate_buffer_until = proc "c" (state: rawptr, it: ^plugin.BufferIter, until_proc: rawptr) {
state := cast(^State)state;
context = state.ctx;
// TODO: make this into a function
internal_it := core.FileBufferIter {
cursor = core.Cursor {
col = it.cursor.col,
line = it.cursor.line,
index = core.FileBufferIndex {
slice_index = it.cursor.index.slice_index,
content_index = it.cursor.index.content_index,
}
},
buffer = cast(^core.FileBuffer)it.buffer,
hit_end = it.hit_end,
}
core.iterate_file_buffer_until(&internal_it, transmute(core.UntilProc)until_proc);
it^ = plugin.BufferIter {
cursor = plugin.Cursor {
col = internal_it.cursor.col,
line = internal_it.cursor.line,
index = plugin.BufferIndex {
slice_index = internal_it.cursor.index.slice_index,
content_index = internal_it.cursor.index.content_index,
}
},
buffer = cast(rawptr)internal_it.buffer,
hit_end = internal_it.hit_end,
};
},
iterate_buffer_peek = proc "c" (state: rawptr, it: ^plugin.BufferIter) -> plugin.IterateResult {
state := cast(^State)state;
context = state.ctx;
// TODO: make this into a function
internal_it := core.FileBufferIter {
cursor = core.Cursor {
col = it.cursor.col,
line = it.cursor.line,
index = core.FileBufferIndex {
slice_index = it.cursor.index.slice_index,
content_index = it.cursor.index.content_index,
}
},
buffer = cast(^core.FileBuffer)it.buffer,
hit_end = it.hit_end,
}
char, _, cond := core.iterate_peek(&internal_it, core.iterate_file_buffer);
it^ = plugin.BufferIter {
cursor = plugin.Cursor {
col = internal_it.cursor.col,
line = internal_it.cursor.line,
index = plugin.BufferIndex {
slice_index = internal_it.cursor.index.slice_index,
content_index = internal_it.cursor.index.content_index,
}
},
buffer = cast(rawptr)internal_it.buffer,
hit_end = internal_it.hit_end,
};
return plugin.IterateResult {
char = char,
should_stop = cond,
};
},
until_line_break = transmute(rawptr)core.until_line_break,
until_single_quote = transmute(rawptr)core.until_single_quote,
until_double_quote = transmute(rawptr)core.until_double_quote,
until_end_of_word = transmute(rawptr)core.until_end_of_word,
},
buffer = plugin.Buffer {
get_buffer_info = proc "c" (state: rawptr) -> plugin.BufferInfo {
state := cast(^State)state;
context = state.ctx;
buffer := &state.buffers[state.current_buffer];
return plugin.BufferInfo {
glyph_buffer_width = buffer.glyph_buffer_width,
glyph_buffer_height = buffer.glyph_buffer_height,
top_line = buffer.top_line,
};
},
color_char_at = proc "c" (state: rawptr, buffer: rawptr, start_cursor: plugin.Cursor, end_cursor: plugin.Cursor, palette_index: i32) {
state := cast(^State)state;
buffer := cast(^core.FileBuffer)buffer;
context = state.ctx;
start_cursor := core.Cursor {
col = start_cursor.col,
line = start_cursor.line,
index = core.FileBufferIndex {
slice_index = start_cursor.index.slice_index,
content_index = start_cursor.index.content_index,
}
};
end_cursor := core.Cursor {
col = end_cursor.col,
line = end_cursor.line,
index = core.FileBufferIndex {
slice_index = end_cursor.index.slice_index,
content_index = end_cursor.index.content_index,
}
};
core.color_character(buffer, start_cursor, end_cursor, cast(theme.PaletteColor)palette_index);
}
}
};
state.current_input_map = &state.input_map;
register_default_input_actions(&state.input_map);
@ -431,7 +502,7 @@ main :: proc() {
for plugin in state.plugins {
if plugin.on_initialize != nil {
plugin.on_initialize(plugin.plugin);
plugin.on_initialize(state.plugin_vtable);
}
}

View File

@ -7,12 +7,11 @@ import "core:fmt"
OnInitializeProc :: proc "c" (plugin: Plugin);
OnExitProc :: proc "c" (/* probably needs some state eventually */);
OnDrawProc :: proc "c" (plugin: Plugin);
OnColorBufferProc :: proc "c" (plugin: Plugin, buffer: rawptr);
Interface :: struct {
on_initialize: OnInitializeProc,
on_exit: OnExitProc,
on_draw: OnDrawProc,
plugin: Plugin,
}
BufferIndex :: struct {
@ -45,18 +44,19 @@ BufferInfo :: struct {
Buffer :: struct {
get_buffer_info: proc "c" (state: rawptr) -> BufferInfo,
color_char_at: proc "c" (buffer: rawptr, start_cursor: Cursor, end_cursor: Cursor, palette_index: i32),
color_char_at: proc "c" (state: rawptr, buffer: rawptr, start_cursor: Cursor, end_cursor: Cursor, palette_index: i32),
}
Iterator :: struct {
get_current_buffer_iterator: proc "c" (state: rawptr) -> BufferIter,
get_buffer_iterator: proc "c" (state: rawptr, buffer: rawptr) -> BufferIter,
get_char_at_iter: proc "c" (state: rawptr, it: ^BufferIter) -> u8,
iterate_buffer: proc "c" (state: rawptr, it: ^BufferIter) -> IterateResult,
iterate_buffer_reverse: proc "c" (state: rawptr, it: ^BufferIter) -> IterateResult,
iterate_buffer_until: proc "c" (state: rawptr, it: ^BufferIter, until_proc: rawptr),
iterate_buffer_until_reverse: proc "c" (state: rawptr, it: ^BufferIter, until_proc: rawptr),
iterate_buffer_peek: proc "c" (state: rawptr, it: ^BufferIter, iter_proc: rawptr) -> IterateResult,
iterate_buffer_peek: proc "c" (state: rawptr, it: ^BufferIter) -> IterateResult,
until_line_break: rawptr,
until_single_quote: rawptr,
@ -68,6 +68,8 @@ Plugin :: struct {
state: rawptr,
iter: Iterator,
buffer: Buffer,
register_highlighter: proc "c" (state: rawptr, extension: cstring, on_color_buffer: OnColorBufferProc),
}
load_proc_address :: proc(lib_path: string, library: dynlib.Library, symbol: string, $ProcType: typeid) -> ProcType