From 8891213a0a39f1fd0bd9fc66bf9e0fd3d69c0925 Mon Sep 17 00:00:00 2001 From: Patrick Cleavelin Date: Fri, 5 Jan 2024 14:16:42 -0600 Subject: [PATCH] actually fully implement the highlighter within a plugin + a rust one too --- Makefile | 2 +- plugins/highlighter/src/plugin.odin | 418 ++++++++++++++++++++ plugins/odin_highlighter/src/plugin.odin | 260 ------------- src/core/core.odin | 2 + src/core/file_buffer.odin | 13 +- src/main.odin | 469 +++++++++++++---------- src/plugin/plugin.odin | 10 +- 7 files changed, 704 insertions(+), 470 deletions(-) create mode 100644 plugins/highlighter/src/plugin.odin delete mode 100644 plugins/odin_highlighter/src/plugin.odin diff --git a/Makefile b/Makefile index 4547d4f..58238f1 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/plugins/highlighter/src/plugin.odin b/plugins/highlighter/src/plugin.odin new file mode 100644 index 0000000..1a87ee2 --- /dev/null +++ b/plugins/highlighter/src/plugin.odin @@ -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); + } + } +} diff --git a/plugins/odin_highlighter/src/plugin.odin b/plugins/odin_highlighter/src/plugin.odin deleted file mode 100644 index e7fad83..0000000 --- a/plugins/odin_highlighter/src/plugin.odin +++ /dev/null @@ -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); - } - } -} diff --git a/src/core/core.odin b/src/core/core.odin index c1c41ee..2de55f1 100644 --- a/src/core/core.odin +++ b/src/core/core.odin @@ -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); diff --git a/src/core/file_buffer.odin b/src/core/file_buffer.odin index 1645aac..f181612 100644 --- a/src/core/file_buffer.odin +++ b/src/core/file_buffer.odin @@ -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 { diff --git a/src/main.odin b/src/main.odin index 6291397..f139a82 100644 --- a/src/main.odin +++ b/src/main.odin @@ -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); } } diff --git a/src/plugin/plugin.odin b/src/plugin/plugin.odin index 58998c8..811feeb 100644 --- a/src/plugin/plugin.odin +++ b/src/plugin/plugin.odin @@ -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