remove plugin support

memory-refactor
Patrick Cleaveliln 2025-07-05 20:42:24 +00:00
parent f03cbcd06d
commit d5db929b3f
25 changed files with 453 additions and 6988 deletions

View File

@ -0,0 +1,15 @@
{
"image": "mcr.microsoft.com/devcontainers/cpp:ubuntu",
"customizations": {
"vscode": {
"extensions": [
"danielgavin.ols"
]
}
},
"features": {
"./odin-feature": {
"version": "latest"
}
}
}

View File

@ -0,0 +1,11 @@
{
"name": "Odin Compiler",
"id": "odin",
"version": "1.0.0",
"installsAfter": [
"ghcr.io/devcontainers/features/common-utils"
],
"containerEnv": {
"ODIN_ROOT": "/usr/local/share/odin"
}
}

View File

@ -0,0 +1,19 @@
#!/bin/sh
set -e
git clone --branch "dev-2024-12" "https://github.com/odin-lang/Odin.git"
cd Odin
mkdir -p /usr/local/share/odin
cp -r ./base /usr/local/share/odin/
cp -r ./core /usr/local/share/odin/
cp -r ./vendor /usr/local/share/odin/
make release
cp ./odin /usr/local/bin/
echo "Installing SDL2 & SDL2_ttf"
apt update
apt --yes --force-yes install libsdl2-dev libsdl2-ttf-dev

View File

@ -1,13 +1,5 @@
all: editor all: editor
editor: src/*.odin # grep odin_highlighter editor: src/**/*.odin
# odin build src/ -out:bin/editor.o -build-mode:obj -debug -lld mkdir -p bin
# dsymutil bin/editor.o -o bin/editor.dSYM odin build src/ -out:bin/editor -debug
../Odin/odin build src/ -out:bin/editor -debug -linker:lld -extra-linker-flags:"-L./"
odin_highlighter: plugins/highlighter/src/*.odin
../Odin/odin build plugins/highlighter/src/ -build-mode:dll -no-entry-point -out:bin/highlighter
grep: plugins/grep/src/*.rs
nightly-cargo b --manifest-path=plugins/grep/Cargo.toml
cp plugins/grep/target/debug/libgrep_plugin.dylib bin/

Binary file not shown.

View File

@ -1,7 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "plugin_rs_bindings"
version = "0.1.0"

View File

@ -1,8 +0,0 @@
[package]
name = "plugin_rs_bindings"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View File

@ -1,781 +0,0 @@
use std::{
borrow::Cow,
ffi::{c_char, c_void, CStr, CString},
fmt::write,
path::Path,
};
#[macro_export]
macro_rules! Closure {
(($($arg: ident: $type: ty),+) => $body: expr) => {
{
extern "C" fn f($($arg: $type),+) {
$body
}
f
}
};
(($($arg: ident: $type: ty),+) -> $return_type: ty => $body: expr) => {
{
extern "C" fn f($($arg: $type),+) -> $return_type {
$body
}
f
}
};
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct InputMap {
internal: *const std::ffi::c_void,
}
#[repr(C)]
#[derive(Debug)]
pub struct BufferIndex {
pub slice_index: isize,
pub content_index: isize,
}
#[repr(C)]
#[derive(Debug)]
pub struct Cursor {
pub col: isize,
pub line: isize,
pub index: BufferIndex,
}
#[repr(C)]
#[derive(Debug)]
struct InternalBufferIter {
cursor: Cursor,
buffer: *const c_void,
hit_end: bool,
}
#[repr(C)]
pub struct IterateResult {
pub char: u8,
pub should_continue: bool,
}
#[repr(C)]
#[derive(Debug)]
pub struct BufferInput {
bytes: *const u8,
length: isize,
}
impl BufferInput {
pub fn try_as_str(&self) -> Option<Cow<'_, str>> {
if self.bytes.is_null() {
None
} else {
let slice = unsafe { std::slice::from_raw_parts(self.bytes, self.length as usize) };
Some(String::from_utf8_lossy(slice))
}
}
}
#[repr(C)]
#[derive(Debug)]
pub struct BufferInfo {
pub buffer: Buffer,
pub file_path: *const i8,
pub input: BufferInput,
pub cursor: Cursor,
pub glyph_buffer_width: isize,
pub glyph_buffer_height: isize,
pub top_line: isize,
}
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Buffer {
internal: *const c_void,
}
impl Buffer {
pub fn null() -> Buffer {
Buffer {
internal: std::ptr::null(),
}
}
}
#[repr(C)]
pub struct BufferVTable {
pub get_num_buffers: extern "C" fn() -> isize,
get_buffer_info: extern "C" fn(buffer: Buffer) -> BufferInfo,
pub get_buffer_info_from_index: extern "C" fn(buffer_index: isize) -> BufferInfo,
pub color_char_at: extern "C" fn(
buffer: *const c_void,
start_cursor: Cursor,
end_cursor: Cursor,
palette_index: i32,
),
pub set_current_buffer: extern "C" fn(buffer_index: isize),
open_buffer: extern "C" fn(path: *const u8, line: isize, col: isize),
open_virtual_buffer: extern "C" fn() -> *const c_void,
free_virtual_buffer: extern "C" fn(buffer: Buffer),
}
impl BufferVTable {
pub fn get_buffer_info(&self, buffer: Buffer) -> Option<BufferInfo> {
if buffer.internal.is_null() {
None
} else {
Some((self.get_buffer_info)(buffer))
}
}
pub fn open_buffer(&self, path: impl AsRef<Path>, line: i32, col: i32) {
let Ok(c_str) = CString::new(path.as_ref().as_os_str().as_encoded_bytes()) else {
eprintln!("grep plugin failed to open buffer");
return;
};
(self.open_buffer)(c_str.as_ptr() as *const u8, line as isize, col as isize);
}
pub fn open_virtual_buffer(&self) -> Buffer {
Buffer {
internal: (self.open_virtual_buffer)(),
}
}
pub fn free_virtual_buffer(&self, buffer: Buffer) {
(self.free_virtual_buffer)(buffer);
}
}
#[repr(C)]
pub struct IteratorVTable {
get_current_buffer_iterator: extern "C" fn() -> InternalBufferIter,
get_buffer_iterator: extern "C" fn(buffer: *const c_void) -> InternalBufferIter,
get_char_at_iter: extern "C" fn(it: *const InternalBufferIter) -> u8,
get_buffer_list_iter: extern "C" fn(prev_buffer: *const isize) -> isize,
iterate_buffer: extern "C" fn(it: *mut InternalBufferIter) -> IterateResult,
iterate_buffer_reverse: extern "C" fn(it: *mut InternalBufferIter) -> IterateResult,
iterate_buffer_until: extern "C" fn(it: *mut InternalBufferIter, until_proc: *const c_void),
iterate_buffer_until_reverse:
extern "C" fn(it: *mut InternalBufferIter, until_proc: *const c_void),
iterate_buffer_peek: extern "C" fn(it: *mut InternalBufferIter) -> IterateResult,
pub until_line_break: *const c_void,
pub until_single_quote: *const c_void,
pub until_double_quote: *const c_void,
pub until_end_of_word: *const c_void,
}
#[repr(C)]
pub struct UiInteraction {
pub hovering: bool,
pub clicked: bool,
}
#[repr(C)]
struct InternalUiSemanticSize {
kind: isize,
value: isize,
}
#[repr(isize)]
pub enum UiAxis {
Horizontal = 0,
Vertical,
}
pub enum UiSemanticSize {
FitText,
Exact(isize),
ChildrenSum,
Fill,
PercentOfParent(isize),
}
impl From<UiSemanticSize> for InternalUiSemanticSize {
fn from(value: UiSemanticSize) -> Self {
let (kind, value) = match value {
UiSemanticSize::FitText => (0, 0),
UiSemanticSize::Exact(value) => (1, value),
UiSemanticSize::ChildrenSum => (2, 0),
UiSemanticSize::Fill => (3, 0),
UiSemanticSize::PercentOfParent(value) => (4, value),
};
Self { kind, value }
}
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct UiContext(*const c_void);
#[repr(C)]
#[derive(Clone, Copy)]
pub struct UiBox(*const c_void);
type UiPushParentProc = extern "C" fn(ui_context: UiContext, ui_box: UiBox);
type UiPopParentProc = extern "C" fn(ui_context: UiContext);
type UiFloatingProc =
extern "C" fn(ui_context: UiContext, label: *const i8, pos: [isize; 2]) -> UiBox;
type UiRectProc = extern "C" fn(
ui_context: UiContext,
label: *const i8,
border: bool,
border: bool,
axis: UiAxis,
size: [InternalUiSemanticSize; 2],
) -> UiBox;
type UiSimpleProc = extern "C" fn(ui_context: UiContext, label: *const i8) -> UiInteraction;
type UiBufferProc = extern "C" fn(ui_context: UiContext, buffer: Buffer, show_line_numbers: bool);
#[repr(C)]
pub struct UiVTable {
ui_context: UiContext,
push_parent: UiPushParentProc,
pop_parent: UiPopParentProc,
spacer: UiSimpleProc,
floating: UiFloatingProc,
rect: UiRectProc,
button: UiSimpleProc,
label: UiSimpleProc,
buffer: UiBufferProc,
buffer_from_index: UiBufferProc,
}
impl UiVTable {
pub fn push_parent(&self, ui_box: UiBox) {
(self.push_parent)(self.ui_context, ui_box);
}
pub fn pop_parent(&self) {
(self.pop_parent)(self.ui_context);
}
pub fn spacer(&self, label: &CStr) -> UiInteraction {
(self.spacer)(self.ui_context, label.as_ptr())
}
pub fn push_rect(
&self,
label: &CStr,
show_background: bool,
show_border: bool,
axis: UiAxis,
horizontal_size: UiSemanticSize,
vertical_size: UiSemanticSize,
inner: impl FnOnce(&UiVTable),
) {
let rect = (self.rect)(
self.ui_context,
label.as_ptr(),
show_background,
show_border,
axis,
[horizontal_size.into(), vertical_size.into()],
);
self.push_parent(rect);
inner(self);
self.pop_parent();
}
pub fn push_floating(&self, label: &CStr, x: isize, y: isize, inner: impl FnOnce(&UiVTable)) {
let floating = (self.floating)(self.ui_context, label.as_ptr(), [x, y]);
self.push_parent(floating);
inner(self);
self.pop_parent();
}
pub fn label(&self, label: &CStr) -> UiInteraction {
(self.label)(self.ui_context, label.as_ptr())
}
pub fn button(&self, label: &CStr) -> UiInteraction {
(self.button)(self.ui_context, label.as_ptr())
}
pub fn buffer(&self, buffer: Buffer, show_line_numbers: bool) {
(self.buffer)(self.ui_context, buffer, show_line_numbers)
}
}
type OnColorBufferProc = extern "C" fn(plugin: Plugin, buffer: *const c_void);
type OnHookProc = extern "C" fn(plugin: Plugin, buffer: Buffer);
type InputGroupProc = extern "C" fn(plugin: Plugin, input_map: InputMap);
type InputActionProc = extern "C" fn(plugin: Plugin);
type WindowDrawProc = extern "C" fn(plugin: Plugin, window: *const c_void);
type WindowFreeProc = extern "C" fn(plugin: Plugin, window: *const c_void);
type WindowGetBufferProc = extern "C" fn(plugin: Plugin, window: *const c_void) -> Buffer;
#[repr(C)]
pub struct Plugin {
state: *const c_void,
pub iter_table: IteratorVTable,
pub buffer_table: BufferVTable,
pub ui_table: UiVTable,
pub register_hook: extern "C" fn(hook: Hook, on_hook: OnHookProc),
pub register_highlighter:
extern "C" fn(extension: *const c_char, on_color_buffer: OnColorBufferProc),
pub register_input_group:
extern "C" fn(input_map: InputMap, key: Key, register_group: InputGroupProc),
pub register_input: extern "C" fn(
input_map: InputMap,
key: Key,
input_action: InputActionProc,
description: *const u8,
),
pub create_window: extern "C" fn(
user_data: *const c_void,
register_group: InputGroupProc,
draw_proc: WindowDrawProc,
free_window_proc: WindowFreeProc,
get_buffer_proc: *const (),
) -> *const c_void,
get_window: extern "C" fn() -> *const c_void,
pub request_window_close: extern "C" fn(),
pub get_screen_width: extern "C" fn() -> isize,
pub get_screen_height: extern "C" fn() -> isize,
pub get_font_width: extern "C" fn() -> isize,
pub get_font_height: extern "C" fn() -> isize,
get_current_directory: extern "C" fn() -> *const c_char,
pub enter_insert_mode: extern "C" fn(),
pub draw_rect: extern "C" fn(x: i32, y: i32, width: i32, height: i32, color: PaletteColor),
pub draw_text: extern "C" fn(text: *const c_char, x: f32, y: f32, color: PaletteColor),
pub draw_buffer_from_index: extern "C" fn(
buffer_index: isize,
x: isize,
y: isize,
glyph_buffer_width: isize,
glyph_buffer_height: isize,
show_line_numbers: bool,
),
pub draw_buffer: extern "C" fn(
buffer: Buffer,
x: isize,
y: isize,
glyph_buffer_width: isize,
glyph_buffer_height: isize,
show_line_numbers: bool,
),
}
pub struct BufferIter {
iter: InternalBufferIter,
iter_table: IteratorVTable,
}
impl BufferIter {
pub fn new(plugin: Plugin, buffer: Buffer) -> Self {
let buffer_info = (plugin.buffer_table.get_buffer_info)(buffer);
Self {
iter: InternalBufferIter {
cursor: buffer_info.cursor,
buffer: buffer.internal,
hit_end: false,
},
iter_table: plugin.iter_table,
}
}
}
impl Iterator for BufferIter {
type Item = char;
fn next(&mut self) -> Option<Self::Item> {
let iter_ptr = (&mut self.iter) as *mut InternalBufferIter;
let result = (self.iter_table.iterate_buffer)(iter_ptr);
if result.should_continue {
Some(result.char as char)
} else {
None
}
}
}
pub struct BufferListIter {
index: isize,
next_fn: extern "C" fn(prev_buffer: *const isize) -> isize,
}
impl From<&Plugin> for BufferListIter {
fn from(value: &Plugin) -> Self {
BufferListIter {
index: 0,
next_fn: value.iter_table.get_buffer_list_iter,
}
}
}
impl Iterator for BufferListIter {
type Item = isize;
fn next(&mut self) -> Option<Self::Item> {
if self.index == -1 {
return None;
}
Some((self.next_fn)(&mut self.index))
}
}
impl Plugin {
pub fn get_current_directory(&self) -> Cow<str> {
unsafe {
let c_str = CStr::from_ptr((self.get_current_directory)());
c_str.to_string_lossy()
}
}
/// # Safety
/// If `W` is not the same type as given in `self.create_window`, it will result in undefined
/// behavior. `W` can also be a different type if another plugin has created a window.
pub unsafe fn get_window<'a, W>(&self) -> Option<&'a mut W> {
let window_ptr = (self.get_window)() as *mut W;
if window_ptr.is_null() {
None
} else {
let window = Box::from_raw(window_ptr);
Some(Box::leak(window))
}
}
pub fn create_window<W>(
&self,
window: W,
register_group: InputGroupProc,
draw_proc: WindowDrawProc,
free_window_proc: WindowFreeProc,
get_buffer_proc: Option<WindowGetBufferProc>,
) {
let boxed = Box::new(window);
(self.create_window)(
Box::into_raw(boxed) as *const std::ffi::c_void,
register_group,
draw_proc,
free_window_proc,
if let Some(proc) = get_buffer_proc {
proc as *const ()
} else {
std::ptr::null()
},
);
}
pub fn register_hook(&self, hook: Hook, on_hook: OnHookProc) {
(self.register_hook)(hook, on_hook)
}
pub fn register_input_group(
&self,
input_map: Option<InputMap>,
key: Key,
register_group: InputGroupProc,
) {
let input_map = match input_map {
Some(input_map) => input_map,
None => InputMap {
internal: std::ptr::null(),
},
};
(self.register_input_group)(input_map, key, register_group);
}
}
#[repr(i32)]
pub enum Hook {
BufferInput,
}
#[repr(i32)]
pub enum Key {
UNKNOWN = 0,
Enter = 13,
ESCAPE = 27,
BACKSPACE = 8,
TAB = 9,
Space = 32,
EXCLAIM = 33,
QUOTEDBL = 34,
HASH = 35,
PERCENT = 37,
DOLLAR = 36,
AMPERSAND = 38,
QUOTE = 39,
LEFTPAREN = 40,
RIGHTPAREN = 41,
ASTERISK = 42,
PLUS = 43,
COMMA = 44,
MINUS = 45,
PERIOD = 46,
SLASH = 47,
NUM0 = 48,
NUM1 = 49,
NUM2 = 50,
NUM3 = 51,
NUM4 = 52,
NUM5 = 53,
NUM6 = 54,
NUM7 = 55,
NUM8 = 56,
NUM9 = 57,
COLON = 58,
SEMICOLON = 59,
LESS = 60,
EQUAL = 61,
GREATER = 62,
QUESTION = 63,
AT = 64,
LEFTBRACKET = 91,
BACKSLASH = 92,
RIGHTBRACKET = 93,
CARET = 94,
UNDERSCORE = 95,
BACKQUOTE = 96,
A = 97,
B = 98,
C = 99,
D = 100,
E = 101,
F = 102,
G = 103,
H = 104,
I = 105,
J = 106,
K = 107,
L = 108,
M = 109,
N = 110,
O = 111,
P = 112,
Q = 113,
R = 114,
S = 115,
T = 116,
U = 117,
V = 118,
W = 119,
X = 120,
Y = 121,
Z = 122,
CAPSLOCK = 1073741881,
F1 = 1073741882,
F2 = 1073741883,
F3 = 1073741884,
F4 = 1073741885,
F5 = 1073741886,
F6 = 1073741887,
F7 = 1073741888,
F8 = 1073741889,
F9 = 1073741890,
F10 = 1073741891,
F11 = 1073741892,
F12 = 1073741893,
PRINTSCREEN = 1073741894,
SCROLLLOCK = 1073741895,
PAUSE = 1073741896,
INSERT = 1073741897,
HOME = 1073741898,
PAGEUP = 1073741899,
DELETE = 127,
END = 1073741901,
PAGEDOWN = 1073741902,
RIGHT = 1073741903,
LEFT = 1073741904,
DOWN = 1073741905,
UP = 1073741906,
NUMLOCKCLEAR = 1073741907,
KpDivide = 1073741908,
KpMultiply = 1073741909,
KpMinus = 1073741910,
KpPlus = 1073741911,
KpEnter = 1073741912,
Kp1 = 1073741913,
Kp2 = 1073741914,
Kp3 = 1073741915,
Kp4 = 1073741916,
Kp5 = 1073741917,
Kp6 = 1073741918,
Kp7 = 1073741919,
Kp8 = 1073741920,
Kp9 = 1073741921,
Kp0 = 1073741922,
KpPeriod = 1073741923,
APPLICATION = 1073741925,
POWER = 1073741926,
KpEquals = 1073741927,
F13 = 1073741928,
F14 = 1073741929,
F15 = 1073741930,
F16 = 1073741931,
F17 = 1073741932,
F18 = 1073741933,
F19 = 1073741934,
F20 = 1073741935,
F21 = 1073741936,
F22 = 1073741937,
F23 = 1073741938,
F24 = 1073741939,
EXECUTE = 1073741940,
HELP = 1073741941,
MENU = 1073741942,
SELECT = 1073741943,
STOP = 1073741944,
AGAIN = 1073741945,
UNDO = 1073741946,
CUT = 1073741947,
COPY = 1073741948,
PASTE = 1073741949,
FIND = 1073741950,
MUTE = 1073741951,
VOLUMEUP = 1073741952,
VOLUMEDOWN = 1073741953,
KpComma = 1073741957,
KpEqualsas400 = 1073741958,
ALTERASE = 1073741977,
SYSREQ = 1073741978,
CANCEL = 1073741979,
CLEAR = 1073741980,
PRIOR = 1073741981,
RETURN2 = 1073741982,
SEPARATOR = 1073741983,
OUT = 1073741984,
OPER = 1073741985,
CLEARAGAIN = 1073741986,
CRSEL = 1073741987,
EXSEL = 1073741988,
Kp00 = 1073742000,
Kp000 = 1073742001,
THOUSANDSSEPARATOR = 1073742002,
DECIMALSEPARATOR = 1073742003,
CURRENCYUNIT = 1073742004,
CURRENCYSUBUNIT = 1073742005,
KpLeftparen = 1073742006,
KpRightparen = 1073742007,
KpLeftbrace = 1073742008,
KpRightbrace = 1073742009,
KpTab = 1073742010,
KpBackspace = 1073742011,
KpA = 1073742012,
KpB = 1073742013,
KpC = 1073742014,
KpD = 1073742015,
KpE = 1073742016,
KpF = 1073742017,
KpXor = 1073742018,
KpPower = 1073742019,
KpPercent = 1073742020,
KpLess = 1073742021,
KpGreater = 1073742022,
KpAmpersand = 1073742023,
KpDblampersand = 1073742024,
KpVerticalbar = 1073742025,
KpDblverticalbar = 1073742026,
KpColon = 1073742027,
KpHash = 1073742028,
KpSpace = 1073742029,
KpAt = 1073742030,
KpExclam = 1073742031,
KpMemstore = 1073742032,
KpMemrecall = 1073742033,
KpMemclear = 1073742034,
KpMemadd = 1073742035,
KpMemsubtract = 1073742036,
KpMemmultiply = 1073742037,
KpMemdivide = 1073742038,
KpPlusminus = 1073742039,
KpClear = 1073742040,
KpClearentry = 1073742041,
KpBinary = 1073742042,
KpOctal = 1073742043,
KpDecimal = 1073742044,
KpHexadecimal = 1073742045,
LCTRL = 1073742048,
LSHIFT = 1073742049,
LALT = 1073742050,
LGUI = 1073742051,
RCTRL = 1073742052,
RSHIFT = 1073742053,
RALT = 1073742054,
RGUI = 1073742055,
MODE = 1073742081,
AUDIONEXT = 1073742082,
AUDIOPREV = 1073742083,
AUDIOSTOP = 1073742084,
AUDIOPLAY = 1073742085,
AUDIOMUTE = 1073742086,
MEDIASELECT = 1073742087,
WWW = 1073742088,
MAIL = 1073742089,
CALCULATOR = 1073742090,
COMPUTER = 1073742091,
AcSearch = 1073742092,
AcHome = 1073742093,
AcBack = 1073742094,
AcForward = 1073742095,
AcStop = 1073742096,
AcRefresh = 1073742097,
AcBookmarks = 1073742098,
BRIGHTNESSDOWN = 1073742099,
BRIGHTNESSUP = 1073742100,
DISPLAYSWITCH = 1073742101,
KBDILLUMTOGGLE = 1073742102,
KBDILLUMDOWN = 1073742103,
KBDILLUMUP = 1073742104,
EJECT = 1073742105,
SLEEP = 1073742106,
APP1 = 1073742107,
APP2 = 1073742108,
AUDIOREWIND = 1073742109,
AUDIOFASTFORWARD = 1073742110,
}
#[repr(i32)]
pub enum PaletteColor {
Background,
Foreground,
Background1,
Background2,
Background3,
Background4,
Foreground1,
Foreground2,
Foreground3,
Foreground4,
Red,
Green,
Yellow,
Blue,
Purple,
Aqua,
Gray,
BrightRed,
BrightGreen,
BrightYellow,
BrightBlue,
BrightPurple,
BrightAqua,
BrightGray,
}

View File

@ -1,96 +0,0 @@
local M = {}
M.version = "0.1"
M.name = "Default_View"
M.namespace = "nl_spacegirl_plugin_Default"
M.BufferListPanel = {
num_clicks = 0
}
M.SomeOtherPanel = {
num_clicks_2 = 0
}
function M.BufferListPanel.new()
local p = {}
setmetatable(p, {__index = M.BufferListPanel})
return p
end
function M.BufferListPanel:render(ctx)
-- if UI.button(ctx, "Number of Clicks "..self.num_clicks).clicked then
-- self.num_clicks = self.num_clicks + 1
-- end
end
function M.SomeOtherPanel.new()
local p = {}
setmetatable(p, {__index = M.SomeOtherPanel})
return p
end
function M.SomeOtherPanel:render(ctx)
UI_New.open_element(ctx, "Number of Clicks", {
dir = UI_New.LeftToRight,
kind = {UI_New.Exact(128), UI_New.Exact(32)},
})
UI_New.close_element(ctx)
UI_New.open_element(ctx, "Whatever man", {
dir = UI_New.LeftToRight,
kind = {UI_New.Fit, UI_New.Exact(32)},
})
UI_New.close_element(ctx)
end
function M.open_file_search_window()
local input = {
{Editor.Key.Enter, "Open File", function() Editor.log("this should open a file") end}
}
Editor.spawn_floating_window(input, function(ctx)
UI.push_parent(ctx, UI.push_rect(ctx, "window", true, true, UI.Vertical, UI.Fill, UI.ChildrenSum))
UI.label(ctx, "eventually this will be a window where you can search through a bunch of files 1")
UI.label(ctx, "eventually this will be a window where you can search through a bunch of files 2")
UI.label(ctx, "eventually this will be a window where you can search through a bunch of files 3")
UI.label(ctx, "eventually this will be a window where you can search through a bunch of files 4")
UI.label(ctx, "eventually this will be a window where you can search through a bunch of files 5")
UI.label(ctx, "eventually this will be a window where you can search through a bunch of files 6")
UI.label(ctx, "eventually this will be a window where you can search through a bunch of files 7")
UI.pop_parent(ctx)
end)
end
function M.OnLoad()
Editor.log("default view loaded")
Editor.log(nl_spacegirl_plugin_Default_Legacy_View['namespace'])
local a = M.BufferListPanel.new()
local b = M.BufferListPanel.new()
print(M.BufferListPanel)
print(a)
print(b)
Editor.register_key_group({
{Editor.Key.Space, "", {
{Editor.Key.F, "Open File", M.open_file_search_window},
{Editor.Key.J, "New Panel", function()
Editor.run_command("nl.spacegirl.editor.core", "Open New Panel", "BufferListPanel")
end},
{Editor.Key.K, "Some Other Panel", function()
Editor.run_command("nl.spacegirl.editor.core", "Open New Panel", "SomeOtherPanel")
end}
}},
})
Editor.register_panel("BufferList", "BufferListPanel")
Editor.register_panel("aksjdhflkasjdf", "SomeOtherPanel")
end
function M.view_render(cx)
UI.label(cx, "Look its a me, a plugin")
end
return M

334
plugins/grep/Cargo.lock generated
View File

@ -1,334 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
dependencies = [
"memchr",
]
[[package]]
name = "bstr"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc"
dependencies = [
"memchr",
"regex-automata",
"serde",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "encoding_rs"
version = "0.8.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1"
dependencies = [
"cfg-if",
]
[[package]]
name = "encoding_rs_io"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cc3c5651fb62ab8aa3103998dade57efdd028544bd300516baa31840c252a83"
dependencies = [
"encoding_rs",
]
[[package]]
name = "globset"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1"
dependencies = [
"aho-corasick",
"bstr",
"log",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "grep"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e2b024ec1e686cb64d78beb852030b0e632af93817f1ed25be0173af0e94939"
dependencies = [
"grep-cli",
"grep-matcher",
"grep-printer",
"grep-regex",
"grep-searcher",
]
[[package]]
name = "grep-cli"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea40788c059ab8b622c4d074732750bfb3bd2912e2dd58eabc11798a4d5ad725"
dependencies = [
"bstr",
"globset",
"libc",
"log",
"termcolor",
"winapi-util",
]
[[package]]
name = "grep-matcher"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47a3141a10a43acfedc7c98a60a834d7ba00dfe7bec9071cbfc19b55b292ac02"
dependencies = [
"memchr",
]
[[package]]
name = "grep-printer"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "743c12a03c8aee38b6e5bd0168d8ebb09345751323df4a01c56e792b1f38ceb2"
dependencies = [
"bstr",
"grep-matcher",
"grep-searcher",
"log",
"serde",
"serde_json",
"termcolor",
]
[[package]]
name = "grep-regex"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f748bb135ca835da5cbc67ca0e6955f968db9c5df74ca4f56b18e1ddbc68230d"
dependencies = [
"bstr",
"grep-matcher",
"log",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "grep-searcher"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba536ae4f69bec62d8839584dd3153d3028ef31bb229f04e09fb5a9e5a193c54"
dependencies = [
"bstr",
"encoding_rs",
"encoding_rs_io",
"grep-matcher",
"log",
"memchr",
"memmap2",
]
[[package]]
name = "grep_plugin"
version = "0.1.0"
dependencies = [
"grep",
"plugin_rs_bindings",
"termcolor",
"walkdir",
]
[[package]]
name = "itoa"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
[[package]]
name = "libc"
version = "0.2.151"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
[[package]]
name = "log"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "memchr"
version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
[[package]]
name = "memmap2"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45fd3a57831bf88bc63f8cebc0cf956116276e97fef3966103e96416209f7c92"
dependencies = [
"libc",
]
[[package]]
name = "plugin_rs_bindings"
version = "0.1.0"
[[package]]
name = "proc-macro2"
version = "1.0.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2de98502f212cfcea8d0bb305bd0f49d7ebdd75b64ba0a68f937d888f4e0d6db"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex-automata"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "ryu"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "serde"
version = "1.0.194"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b114498256798c94a0689e1a15fec6005dee8ac1f41de56404b67afc2a4b773"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.194"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3385e45322e8f9931410f01b3031ec534c3947d0e94c18049af4d9f9907d4e0"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.110"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fbd975230bada99c8bb618e0c365c2eefa219158d5c6c29610fd09ff1833257"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "syn"
version = "2.0.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89456b690ff72fddcecf231caedbe615c59480c93358a93dfae7fc29e3ebbf0e"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "termcolor"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449"
dependencies = [
"winapi-util",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "walkdir"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
dependencies = [
"same-file",
"winapi-util",
]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

View File

@ -1,14 +0,0 @@
[package]
name = "grep_plugin"
version = "0.1.0"
edition = "2021"
publish = false
[lib]
crate-type = ["cdylib"]
[dependencies]
grep = "0.3.1"
termcolor = "1.4.0"
walkdir = "2.4.0"
plugin_rs_bindings = { path = "../../plugin-rs-bindings" }

View File

@ -1,3 +0,0 @@
[toolchain]
channel = "nightly-2024-01-24"

View File

@ -1,449 +0,0 @@
use std::{
error::Error,
ffi::{CString, OsString},
path::Path,
str::FromStr,
sync::mpsc::{Receiver, Sender},
thread,
};
use grep::{
regex::RegexMatcherBuilder,
searcher::{BinaryDetection, SearcherBuilder, Sink, SinkError},
};
use plugin_rs_bindings::{Buffer, Closure, Hook, InputMap, Key, Plugin, UiAxis, UiSemanticSize};
use std::sync::mpsc::channel;
use walkdir::WalkDir;
#[derive(Debug)]
pub enum SimpleSinkError {
StandardError,
NoLine,
BadString,
}
impl SinkError for SimpleSinkError {
fn error_message<T: std::fmt::Display>(message: T) -> Self {
eprintln!("{message}");
Self::StandardError
}
}
#[derive(Debug)]
struct Match {
text: Vec<u8>,
path: String,
line_number: Option<u64>,
column: u64,
}
impl Match {
fn from_sink_match_with_path(
value: &grep::searcher::SinkMatch<'_>,
path: Option<String>,
) -> Result<Self, SimpleSinkError> {
let line = value
.lines()
.next()
.ok_or(SimpleSinkError::NoLine)?
.to_vec();
let column = value.bytes_range_in_buffer().len() as u64;
Ok(Self {
text: line,
path: path.unwrap_or_default(),
line_number: value.line_number(),
column,
})
}
}
#[derive(Default, Debug)]
struct SimpleSink {
current_path: Option<String>,
matches: Vec<Match>,
}
impl Sink for SimpleSink {
type Error = SimpleSinkError;
fn matched(
&mut self,
_searcher: &grep::searcher::Searcher,
mat: &grep::searcher::SinkMatch<'_>,
) -> Result<bool, Self::Error> {
self.matches.push(Match::from_sink_match_with_path(
mat,
self.current_path.clone(),
)?);
Ok(true)
}
}
fn search(pattern: &str, paths: &[OsString]) -> Result<SimpleSink, Box<dyn Error>> {
let matcher = RegexMatcherBuilder::new()
.case_smart(true)
.fixed_strings(true)
.build(pattern)?;
let mut searcher = SearcherBuilder::new()
.binary_detection(BinaryDetection::quit(b'\x00'))
.line_number(true)
.build();
let mut sink = SimpleSink::default();
for path in paths {
for result in WalkDir::new(path).into_iter().filter_entry(|dent| {
if dent.file_type().is_dir()
&& (dent.path().ends_with("target") || dent.path().ends_with(".git"))
{
return false;
}
true
}) {
let dent = match result {
Ok(dent) => dent,
Err(err) => {
eprintln!("{}", err);
continue;
}
};
if !dent.file_type().is_file() {
continue;
}
sink.current_path = Some(dent.path().to_string_lossy().into());
let result = searcher.search_path(&matcher, dent.path(), &mut sink);
if let Err(err) = result {
eprintln!("{}: {:?}", dent.path().display(), err);
}
}
}
Ok(sink)
}
enum Message {
Search((String, Vec<OsString>)),
Quit,
}
struct GrepWindow {
sink: Option<SimpleSink>,
selected_match: usize,
top_index: usize,
input_buffer: Option<Buffer>,
tx: Sender<Message>,
rx: Receiver<SimpleSink>,
}
#[no_mangle]
pub extern "C" fn OnInitialize(plugin: Plugin) {
println!("Grep Plugin Initialized");
plugin.register_hook(Hook::BufferInput, on_buffer_input);
plugin.register_input_group(
None,
Key::Space,
Closure!((plugin: Plugin, input_map: InputMap) => {
(plugin.register_input)(
input_map,
Key::R,
Closure!((plugin: Plugin) => {
let (window_tx, thread_rx) = channel();
let (thread_tx, window_rx) = channel();
create_search_thread(thread_tx, thread_rx);
let window = GrepWindow {
selected_match: 0,
top_index: 0,
input_buffer: Some(plugin.buffer_table.open_virtual_buffer()),
sink: None,
tx: window_tx,
rx: window_rx,
};
plugin.create_window(window, Closure!((plugin: Plugin, input_map: InputMap) => {
(plugin.enter_insert_mode)();
(plugin.register_input)(input_map, Key::I, Closure!((plugin: Plugin) => {
(plugin.enter_insert_mode)()
}), "\0".as_ptr());
(plugin.register_input)(input_map, Key::Enter, Closure!((plugin: Plugin) => {
if let Some(window) = unsafe { plugin.get_window::<GrepWindow>() } {
match &window.sink {
Some(sink) => if window.selected_match < sink.matches.len() {
let mat = unsafe { &sink.matches.get_unchecked(window.selected_match) };
plugin.buffer_table.open_buffer(&mat.path, (mat.line_number.unwrap_or(1)-1) as i32, 0);
(plugin.request_window_close)();
},
None => {},
}
}
}), "move selection up\0".as_ptr());
(plugin.register_input)(input_map, Key::K, Closure!((plugin: Plugin) => {
if let Some(window) = unsafe { plugin.get_window::<GrepWindow>() } {
if window.selected_match > 0 {
window.selected_match -= 1;
if window.selected_match < window.top_index {
window.top_index = window.selected_match;
}
} else {
window.selected_match = match &window.sink {
Some(sink) => sink.matches.len()-1,
None => 0,
};
window.top_index = window.selected_match;
}
}
}), "move selection up\0".as_ptr());
(plugin.register_input)(input_map, Key::J, Closure!((plugin: Plugin) => {
if let Some(window) = unsafe { plugin.get_window::<GrepWindow>() } {
let screen_height = (plugin.get_screen_height)() as i32;
let font_height = (plugin.get_font_height)() as i32;
let height = screen_height - screen_height / 4;
let max_mats_to_draw = (height - font_height * 2) / (font_height) - 1;
let match_count = match &window.sink {
Some(sink) => sink.matches.len(),
None => 0,
};
if match_count > 0 {
let index_threshold = std::cmp::max(max_mats_to_draw-4, 0) as usize;
if window.selected_match < match_count-1 {
window.selected_match += 1;
if window.selected_match - window.top_index > index_threshold {
window.top_index += 1;
}
} else {
window.selected_match = 0;
window.top_index = 0;
}
}
}
}), "move selection down\0".as_ptr());
}), draw_window, free_window, Some(Closure!((_plugin: Plugin, window: *const std::ffi::c_void) -> Buffer => {
let window = Box::leak(unsafe { Box::<GrepWindow>::from_raw(window as *mut GrepWindow) });
if let Some(buffer) = window.input_buffer {
return buffer;
} else {
return Buffer::null();
}
})));
}),
"Open Grep Window\0".as_ptr(),
);
}),
);
}
fn create_search_thread(tx: Sender<SimpleSink>, rx: Receiver<Message>) {
thread::spawn(move || {
while let Ok(message) = rx.recv() {
match message {
Message::Search((pattern, paths)) => {
if let Ok(sink) = search(&pattern, &paths) {
if let Err(err) = tx.send(sink) {
eprintln!("error getting grep results: {err:?}");
return;
}
}
}
Message::Quit => return,
}
}
});
}
#[no_mangle]
pub extern "C" fn OnExit(_plugin: Plugin) {
println!("Grep Plugin Exiting");
}
extern "C" fn draw_window(plugin: Plugin, window: *const std::ffi::c_void) {
let window = Box::leak(unsafe { Box::<GrepWindow>::from_raw(window as *mut GrepWindow) });
let screen_height = (plugin.get_screen_height)() as i32;
let font_height = (plugin.get_font_height)() as i32;
let height = screen_height - screen_height / 4;
let dir = plugin.get_current_directory();
let directory = Path::new(dir.as_ref());
plugin
.ui_table
.push_floating(c"grep canvas", 0, 0, |ui_table| {
// TODO: make some primitive that centers a Box
ui_table.spacer(c"left spacer");
ui_table.push_rect(
c"centered container",
false,
false,
UiAxis::Vertical,
UiSemanticSize::PercentOfParent(75),
UiSemanticSize::Fill,
|ui_table| {
ui_table.spacer(c"top spacer");
ui_table.push_rect(
c"grep window",
true,
true,
UiAxis::Vertical,
UiSemanticSize::Fill,
UiSemanticSize::PercentOfParent(75),
|ui_table| {
if let Ok(sink) = window.rx.try_recv() {
window.sink = Some(sink);
}
ui_table.push_rect(
c"results list",
false,
false,
UiAxis::Vertical,
UiSemanticSize::Fill,
UiSemanticSize::Fill,
|ui_table| match &window.sink {
Some(sink) if !sink.matches.is_empty() => {
let num_mats_to_draw = std::cmp::min(
(sink.matches.len() - window.top_index) as i32,
(height - font_height) / (font_height),
);
for (i, mat) in
sink.matches[window.top_index..].iter().enumerate()
{
let index = i + window.top_index;
if i as i32 >= num_mats_to_draw {
break;
}
let path = Path::new(&mat.path);
let relative_file_path = path
.strip_prefix(directory)
.unwrap_or(path)
.to_str()
.unwrap_or("");
let matched_text = String::from_utf8_lossy(&mat.text);
let text = match mat.line_number {
Some(line_number) => format!(
"{}:{}:{}: {}",
relative_file_path,
line_number,
mat.column,
matched_text
),
None => format!(
"{}:{}: {}",
relative_file_path, mat.column, matched_text
),
};
if index == window.selected_match {
// TODO: don't use button here, but apply a style
// to `label`
ui_table.button(
&CString::new(text).expect("valid text"),
);
} else {
ui_table.label(
&CString::new(text).expect("valid text"),
);
}
}
}
Some(_) | None => {
ui_table.spacer(c"top spacer");
ui_table.push_rect(
c"centered text container",
false,
false,
UiAxis::Horizontal,
UiSemanticSize::Fill,
UiSemanticSize::Fill,
|ui_table| {
ui_table.spacer(c"left spacer");
ui_table.label(c"no results");
ui_table.spacer(c"right spacer");
},
);
ui_table.spacer(c"bottom spacer");
}
},
);
ui_table.push_rect(
c"grep window",
true,
false,
UiAxis::Vertical,
UiSemanticSize::Fill,
UiSemanticSize::Exact(font_height as isize),
|ui_table| {
if let Some(buffer) = window.input_buffer {
ui_table.buffer(buffer, false);
}
},
);
},
);
ui_table.spacer(c"bottom spacer");
},
);
ui_table.spacer(c"right spacer");
});
}
extern "C" fn on_buffer_input(plugin: Plugin, buffer: Buffer) {
// NOTE(pcleavelin): this is super jank, because another plugin could have a window open when
// this gets called, however its fine here because we aren't manipulating any data, and a check
// is made between the buffer pointers which will only be correct if its our window.
if let Some(window) = unsafe { plugin.get_window::<GrepWindow>() } {
if window.input_buffer == Some(buffer) {
window.selected_match = 0;
window.top_index = 0;
if let Some(buffer_info) = plugin.buffer_table.get_buffer_info(buffer) {
if let Some(input) = buffer_info.input.try_as_str() {
let directory = OsString::from_str(plugin.get_current_directory().as_ref());
match directory {
Ok(dir) => {
if let Err(err) = window
.tx
.send(Message::Search((input.to_string(), vec![dir])))
{
eprintln!("failed to grep: {err:?}");
}
}
Err(_) => {
eprintln!("failed to parse directory");
}
};
}
}
}
}
}
extern "C" fn free_window(plugin: Plugin, window: *const std::ffi::c_void) {
let mut window = unsafe { Box::<GrepWindow>::from_raw(window as *mut GrepWindow) };
let _ = window.tx.send(Message::Quit);
if let Some(buffer) = window.input_buffer {
plugin.buffer_table.free_virtual_buffer(buffer);
window.input_buffer = None;
}
}

View File

@ -1,423 +0,0 @@
// The default syntax highlighter plugin for Odin & Rust
package highlighter;
import "base: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("builtin highlighter plugin initialized!");
plugin.register_highlighter(".odin", color_buffer_odin);
plugin.register_highlighter(".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(iter_funcs: Iterator, it: ^BufferIter) -> (character: u8, idx: BufferIndex, cond: bool) {
result := iter_funcs.iterate_buffer(it);
return result.char, it.cursor.index, result.should_continue;
}
iterate_buffer_reverse :: proc(iter_funcs: Iterator, it: ^BufferIter) -> (character: u8, idx: BufferIndex, cond: bool) {
result := iter_funcs.iterate_buffer_reverse(it);
return result.char, it.cursor.index, result.should_continue;
}
iterate_buffer_until :: proc(plugin: Plugin, it: ^BufferIter, until_proc: rawptr) {
plugin.iter.iterate_buffer_until(it, until_proc);
}
iterate_buffer_peek :: proc(plugin: Plugin, it: ^BufferIter) -> (character: u8, idx: BufferIndex, cond: bool) {
result := plugin.iter.iterate_buffer_peek(it);
return result.char, it.cursor.index, result.should_continue;
}
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.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(&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.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(&it) == keyword[keyword_index] {
matches = true;
}
break;
} else if keyword_index >= len(keyword)-1 {
break;
} else if it == end {
break;
}
}
if matches {
break;
}
}
return;
}
// TODO: split logic into single line coloring, and multi-line coloring.
// single line coloring can be done directly on the glyph buffer
// (with some edge cases, literally, the edge of the screen)
color_buffer_odin :: proc "c" (plugin: Plugin, buffer: rawptr) {
context = runtime.default_context();
buffer := plugin.buffer.get_buffer_info(buffer);
start_it := plugin.iter.get_buffer_iterator(buffer.buffer);
it := plugin.iter.get_buffer_iterator(buffer.buffer);
for character in iterate_buffer(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.iter, &start_it);
character, _, succ := iterate_buffer(plugin.iter, &it);
if !succ { break; }
if character == '/' {
iterate_buffer_until(plugin, &it, plugin.iter.until_line_break);
plugin.buffer.color_char_at(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.iter, &start_it);
// jump into the quoted text
iterate_buffer_until(plugin, &it, plugin.iter.until_single_quote);
plugin.buffer.color_char_at(it.buffer, start_it.cursor, it.cursor, 12);
iterate_buffer(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.iter, &start_it);
// jump into the quoted text
iterate_buffer_until(plugin, &it, plugin.iter.until_double_quote);
plugin.buffer.color_char_at(it.buffer, start_it.cursor, it.cursor, 12);
iterate_buffer(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.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(it.buffer, start_it.cursor, it.cursor, 13);
iterate_buffer(plugin.iter, &it);
} else if character, _, cond := iterate_buffer_peek(plugin, &it); cond {
if character == '(' {
plugin.buffer.color_char_at(it.buffer, start_it.cursor, it.cursor, 11);
iterate_buffer(plugin.iter, &it);
}
} else {
break;
}
}
}
}
color_buffer_rust :: proc "c" (plugin: Plugin, buffer: rawptr) {
context = runtime.default_context();
buffer := plugin.buffer.get_buffer_info(buffer);
start_it := plugin.iter.get_buffer_iterator(buffer.buffer);
it := plugin.iter.get_buffer_iterator(buffer.buffer);
for character in iterate_buffer(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.iter, &start_it);
character, _, succ := iterate_buffer(plugin.iter, &it);
if !succ { break; }
if character == '/' {
iterate_buffer_until(plugin, &it, plugin.iter.until_line_break);
plugin.buffer.color_char_at(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.iter, &start_it);
// jump into the quoted text
iterate_buffer_until(plugin, &it, plugin.iter.until_single_quote);
plugin.buffer.color_char_at(it.buffer, start_it.cursor, it.cursor, 12);
iterate_buffer(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.iter, &start_it);
// jump into the quoted text
iterate_buffer_until(plugin, &it, plugin.iter.until_double_quote);
plugin.buffer.color_char_at(it.buffer, start_it.cursor, it.cursor, 12);
iterate_buffer(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.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(it.buffer, start_it.cursor, it.cursor, 13);
iterate_buffer(plugin.iter, &it);
} else if character, _, cond := iterate_buffer_peek(plugin, &it); cond {
if character == '(' || character == '<' || character == '!' {
plugin.buffer.color_char_at(it.buffer, start_it.cursor, it.cursor, 11);
iterate_buffer(plugin.iter, &it);
}
} else {
break;
}
}
}
}

View File

@ -1,526 +0,0 @@
local M = {}
M.version = "0.1"
M.name = "Legacy_View"
M.namespace = "nl_spacegirl_plugin_Default"
local BufferSearchOpen = false
local BufferSearchOpenElapsed = 0
local CommandSearchOpen = false
local CommandSearchOpenElapsed = 0
local CommandList = {}
local LogWindowOpen = false
local LogWindowOpenElapsed = 0
local CurrentPreviewBufferIndex = Editor.get_current_buffer_index()
local BufferSearchIndex = 0
local SideBarSmoothedWidth = 128
local SideBarWidth = 128
local SideBarClosed = false
local ActiveCodeView = nil
local CodeViews = {}
local MovingTab = nil
local MovingTabDest = nil
local MovingTabInBetween = false
local LastMouseX = 0
local LastMouseY = 0
function buffer_list_iter(start)
local idx = start
return function ()
buffer_info = Editor.buffer_info_from_index(idx)
idx = idx + 1
return buffer_info, idx-1
end
end
function centered(ctx, label, axis, width, height, body)
UI.push_parent(ctx, UI.push_rect(ctx, label, false, false, UI.Horizontal, UI.Fill, UI.Fill))
UI.spacer(ctx, "left spacer")
UI.push_parent(ctx, UI.push_rect(ctx, "halfway centered", false, false, UI.Vertical, width, UI.Fill))
UI.spacer(ctx, "top spacer")
UI.push_parent(ctx, UI.push_rect(ctx, "centered container", false, false, axis, UI.Fill, height))
body()
UI.pop_parent(ctx)
UI.spacer(ctx, "bottom spacer")
UI.pop_parent(ctx)
UI.spacer(ctx, "right spacer")
UI.pop_parent(ctx)
end
function list_iter(start, list)
local idx = start
return function()
local value = list[idx]
idx = idx + 1
return value, idx-1
end
end
function list(ctx, label, selection_index, list, render_func)
list_with_iter(ctx, label, selection_index, list_iter(selection_index, list), render_func)
end
function list_with_iter(ctx, label, selection_index, list_iter, render_func)
local num_items = 10
UI.push_parent(ctx, UI.push_rect(ctx, label, true, true, UI.Vertical, UI.Fill, UI.Fill))
for data, i in list_iter do
render_func(ctx, data, i == selection_index)
end
UI.pop_parent(ctx)
end
function lerp(from, to, rate)
return (1 - rate) * from + rate*to
end
function remove_buffer_from_code_view(code_view_index, file_path)
if code_view_index ~= nil and CodeViews[code_view_index] ~= nil then
CodeViews[code_view_index].tabs[file_path] = nil
k,v = pairs(CodeViews[code_view_index].tabs)(CodeViews[code_view_index].tabs)
CodeViews[code_view_index].current_tab = k
end
end
function add_buffer_to_code_view(code_view_index, file_path, buffer_index)
if code_view_index == nil then
code_view_index = 1
ActiveCodeView = 1
end
-- A new code view is being created
if CodeViews[code_view_index] == nil then
CodeViews[code_view_index] = {}
CodeViews[code_view_index].tabs = {}
CodeViews[code_view_index].width = UI.Fill
end
ActiveCodeView = code_view_index
CodeViews[code_view_index].tabs[file_path] = {}
CodeViews[code_view_index].tabs[file_path].buffer_index = buffer_index
CodeViews[code_view_index].current_tab = file_path
end
function ui_sidemenu(ctx)
if SideBarClosed then
SideBarSmoothedWidth = lerp(SideBarSmoothedWidth, 0, 0.3)
else
SideBarSmoothedWidth = lerp(SideBarSmoothedWidth, SideBarWidth, 0.3)
end
side_menu, _ = UI.push_box(ctx, "side menu", {"Scrollable"}, UI.Vertical, UI.Exact(SideBarSmoothedWidth), UI.Fill)
UI.push_parent(ctx, side_menu)
UI.push_rect(ctx, "padded top open files", false, false, UI.Horizontal, UI.Fill, UI.Exact(8))
UI.push_parent(ctx, UI.push_rect(ctx, "padded open files", false, false, UI.Horizontal, UI.Fill, UI.ChildrenSum))
UI.push_rect(ctx, "padded top open files", false, false, UI.Horizontal, UI.Exact(8), UI.Fill)
UI.label(ctx, "Open Files")
UI.pop_parent(ctx)
UI.push_rect(ctx, "padded bottom open files", false, false, UI.Horizontal, UI.Fill, UI.Exact(8))
for buffer_info, i in buffer_list_iter(0) do
button_container = UI.push_rect(ctx, "button container"..i, false, false, UI.Horizontal, UI.Fill, UI.ChildrenSum)
UI.push_parent(ctx, button_container)
flags = {"Clickable", "Hoverable", "DrawText"}
if i == current_buffer_index then
table.insert(flags, 1, "DrawBackground")
end
if UI.advanced_button(ctx, " x ", flags, UI.FitText, UI.FitText).clicked then
Editor.log("hahah, you can't close buffers yet silly")
if ActiveCodeView ~= nil then
Editor.set_current_buffer_from_index(i)
add_buffer_to_code_view(ActiveCodeView+1, buffer_info.file_path, i)
end
end
tab_button_interaction = UI.advanced_button(ctx, " "..buffer_info.file_path.." ", flags, UI.Fill, UI.FitText)
if tab_button_interaction.clicked then
Editor.set_current_buffer_from_index(i)
add_buffer_to_code_view(ActiveCodeView, buffer_info.file_path, i)
end
if tab_button_interaction.hovering then
CurrentPreviewBufferIndex = i
end
UI.pop_parent(ctx)
end
UI.spacer(ctx, "below buffers spacer")
UI.pop_parent(ctx)
end
function ui_code_view(ctx, code_view_index)
local code_view = CodeViews[code_view_index]
local is_tab_dest = MovingTab ~= nil and ActiveCodeView ~= code_view_index
code_view_rect, code_view_interaction = UI.push_rect(ctx, code_view_index.." code view", ActiveCodeView ~= code_view_index, true, UI.Vertical, code_view.width, UI.Fill)
UI.push_parent(ctx, code_view_rect)
tab_dest_flags = {}
if is_tab_dest then tab_dest_flags = {"Hoverable"} end
tab_dest_region, tab_dest_interaction = UI.push_box(ctx, "code view tab dest", tab_dest_flags, UI.Vertical, UI.Fill, UI.Fill)
UI.push_parent(ctx, tab_dest_region)
if is_tab_dest then
if tab_dest_interaction.hovering then
MovingTabDest = code_view_index
elseif MovingTabDest == code_view_index then
MovingTabDest = nil
end
end
UI.push_parent(ctx, UI.push_box(ctx, "tabs", {}, UI.Horizontal, UI.Fill, UI.ChildrenSum))
for k,v in pairs(code_view.tabs) do
show_border = k ~= code_view.current_tab
background = show_border
flags = {"Clickable", "DrawText"}
if show_border then
table.insert(flags, 1, "DrawBorder")
table.insert(flags, 1, "Hoverable")
end
UI.push_parent(ctx, UI.push_rect(ctx, k.." tab container", background, false, UI.Horizontal, UI.ChildrenSum, UI.ChildrenSum))
tab_button = UI.advanced_button(ctx, " "..k.." ", flags, UI.FitText, UI.Exact(32))
if tab_button.clicked or tab_button.dragging then
ActiveCodeView = code_view_index
code_view.current_tab = k
Editor.set_current_buffer_from_index(v["buffer_index"])
end
if tab_button.dragging then
if MovingTab == nil then
MovingTab = {}
MovingTab["code_view_index"] = code_view_index
MovingTab["tab"] = k
end
UI.push_parent(ctx, UI.push_floating(ctx, "dragging tab", x-(96/2), y-(32/2)))
UI.advanced_button(ctx, " "..k.." ", {"DrawText", "DrawBorder", "DrawBackground"}, UI.FitText, UI.Exact(32))
UI.pop_parent(ctx)
elseif MovingTab ~= nil and MovingTab["code_view_index"] == code_view_index and MovingTab["tab"] == k then
if MovingTabDest ~= nil then
if MovingTabInBetween then
remove_buffer_from_code_view(code_view_index, k)
table.insert(CodeViews, MovingTabDest+1, nil)
add_buffer_to_code_view(MovingTabDest+1, k, v["buffer_index"])
else
add_buffer_to_code_view(MovingTabDest, k, v["buffer_index"])
remove_buffer_from_code_view(code_view_index, k)
MovingTabDest = nil
end
end
MovingTab = nil
end
UI.pop_parent(ctx)
end
UI.pop_parent(ctx)
current_tab = code_view.current_tab
if code_view.tabs[current_tab] ~= nil then
buffer_index = code_view.tabs[current_tab].buffer_index
UI.buffer(ctx, buffer_index)
end
UI.pop_parent(ctx)
UI.pop_parent(ctx)
return code_view_interaction
end
function M.render_ui_window(ctx)
current_buffer_index = Editor.get_current_buffer_index()
x,y = UI.get_mouse_pos(ctx)
delta_x = LastMouseX - x
delta_y = LastMouseY - y
numFrames = 7
CurrentPreviewBufferIndex = current_buffer_index
if not SideBarClosed or SideBarSmoothedWidth > 2 then
ui_sidemenu(ctx)
end
side_bar_interaction = UI.advanced_button(ctx, "side menu grab handle", {"DrawBorder", "Hoverable", "Clickable"}, UI.Exact(16), UI.Fill)
if side_bar_interaction.clicked then
if SideBarClosed then
SideBarClosed = false
if SideBarWidth < 32 then
SideBarWidth = 128
end
else
SideBarClosed = true
end
end
if side_bar_interaction.dragging then
SideBarWidth = x-8
if SideBarWidth < 32 then
SideBarClosed = true
SideBarWidth = 0
elseif SideBarWidth > 128 then
SideBarClosed = false
end
if not SideBarClosed then
SideBarWidth = math.max(SideBarWidth, 128)
end
end
for k,v in ipairs(CodeViews) do
code_view_interaction = ui_code_view(ctx, k)
if next(CodeViews, k) ~= nil then
interaction = UI.advanced_button(ctx, k.."code view grab handle", {"DrawBorder", "Hoverable", "Clickable"}, UI.Exact(16), UI.Fill)
if interaction.dragging then
local width = math.max(32, x - code_view_interaction.box_pos.x)
v.width = UI.Exact(width)
elseif interaction.clicked then
v.width = UI.Fill
elseif MovingTab ~= nil and interaction.hovering then
MovingTabInBetween = true
MovingTabDest = k
elseif MovingTabDest == k and MovingTabInBetween then
MovingTabInBetween = false
MovingTabDest = nil
end
else
v.width = UI.Fill
end
end
for k,v in ipairs(CodeViews) do
if next(v.tabs) == nil then
table.remove(CodeViews, k)
if ActiveCodeView > k then
ActiveCodeView = ActiveCodeView - 1
end
end
end
-- render_buffer_search(ctx)
-- render_command_search(ctx)
render_log_window(ctx)
LastMouseX = x
LastMouseY = y
end
function M.open_buffer_search_window(ctx)
-- if BufferSearchOpen or BufferSearchOpenElapsed > 0 then
-- if BufferSearchOpen and BufferSearchOpenElapsed < numFrames then
-- BufferSearchOpenElapsed = BufferSearchOpenElapsed + 1
-- elseif not BufferSearchOpen and BufferSearchOpenElapsed > 0 then
-- BufferSearchOpenElapsed = BufferSearchOpenElapsed - 1
-- end
-- end
-- if BufferSearchOpen or BufferSearchOpenElapsed > 0 then
-- window_percent = 75
-- if BufferSearchOpenElapsed > 0 then
-- window_percent = ((BufferSearchOpenElapsed/numFrames) * 75)
-- end
local input = {
{Editor.Key.Escape, "Close Window", (
function ()
Editor.request_window_close()
BufferSearchOpen = false
end
)},
{Editor.Key.Enter, "Switch to Buffer", (
function ()
buffer_info = Editor.buffer_info_from_index(BufferSearchIndex)
add_buffer_to_code_view(ActiveCodeView, buffer_info.file_path, BufferSearchIndex)
Editor.set_current_buffer_from_index(BufferSearchIndex)
Editor.request_window_close()
BufferSearchOpen = false
end
)},
-- TODO: don't scroll past buffers
{Editor.Key.K, "Move Selection Up", (function () BufferSearchIndex = BufferSearchIndex - 1 end)},
{Editor.Key.J, "Move Selection Down", (function () BufferSearchIndex = BufferSearchIndex + 1 end)},
}
Editor.spawn_floating_window(input, function(ctx)
-- UI.push_parent(ctx, UI.push_floating(ctx, "buffer search canvas", 0, 0))
-- centered(ctx, "buffer search window", UI.Horizontal, UI.PercentOfParent(window_percent), UI.PercentOfParent(window_percent), (
-- function ()
list_with_iter(ctx, "buffer list", BufferSearchIndex, buffer_list_iter(BufferSearchIndex),
function(ctx, buffer_info, is_selected)
flags = {"DrawText"}
if is_selected then
table.insert(flags, 1, "DrawBorder")
end
interaction = UI.advanced_button(ctx, " "..buffer_info.file_path.." ", flags, UI.Fill, UI.FitText)
end
)
UI.buffer(ctx, BufferSearchIndex)
-- UI.push_parent(ctx, UI.push_rect(ctx, "window", true, true, UI.Horizontal, UI.Fill, UI.Fill))
-- UI.push_parent(ctx, UI.push_rect(ctx, "buffer list", false, false, UI.Vertical, UI.Fill, UI.Fill))
-- for buffer_info, i in buffer_list_iter() do
-- flags = {"DrawText"}
--
-- if i == BufferSearchIndex then
-- table.insert(flags, 1, "DrawBorder")
-- end
-- interaction = UI.advanced_button(ctx, " "..buffer_info.file_path.." ", flags, UI.Fill, UI.FitText)
-- end
-- UI.pop_parent(ctx)
-- UI.buffer(ctx, BufferSearchIndex)
-- UI.pop_parent(ctx)
-- end
-- ))
-- UI.pop_parent(ctx)
end)
-- end
end
function M.open_command_palette()
-- if CommandSearchOpen or CommandSearchOpenElapsed > 0 then
-- if CommandSearchOpen and CommandSearchOpenElapsed < numFrames then
-- CommandSearchOpenElapsed = CommandSearchOpenElapsed + 1
-- elseif not CommandSearchOpen and CommandSearchOpenElapsed > 0 then
-- CommandSearchOpenElapsed = CommandSearchOpenElapsed - 1
-- end
-- end
-- if CommandSearchOpen or CommandSearchOpenElapsed > 0 then
-- window_percent_width = 75
-- window_percent_height = 25
-- if CommandSearchOpenElapsed > 0 then
-- window_percent_width = ((CommandSearchOpenElapsed/numFrames) * 75)
-- window_percent_height = ((CommandSearchOpenElapsed/numFrames) * 25)
-- end
-- UI.push_parent(ctx, UI.push_floating(ctx, "buffer search canvas", 0, 0))
-- centered(ctx, "command search window", UI.Horizontal, UI.PercentOfParent(window_percent_width), UI.PercentOfParent(window_percent_height),
-- function ()
local input = {
{Editor.Key.Escape, "Close Window", (
function ()
Editor.request_window_close()
CommandSearchOpen = false
end
)},
{Editor.Key.Enter, "Run Command", (
function ()
if CommandList[CommandSearchIndex] ~= nil then
Editor.run_command("nl.spacegirl.editor.core", CommandList[CommandSearchIndex]["name"])
CommandList = {}
Editor.request_window_close()
CommandSearchOpen = false
end
end
)},
-- TODO: don't scroll past selections
{Editor.Key.K, "Move Selection Up", (function () CommandSearchIndex = CommandSearchIndex - 1 end)},
{Editor.Key.J, "Move Selection Down", (function () CommandSearchIndex = CommandSearchIndex + 1 end)},
}
Editor.spawn_floating_window(input, function(ctx)
list(ctx, "command list", CommandSearchIndex, CommandList,
function(ctx, cmd, is_selected)
flags = {"DrawText"}
if is_selected then
table.insert(flags, 1, "DrawBorder")
end
interaction = UI.advanced_button(ctx, " "..cmd.name..": "..cmd.description.." ", flags, UI.Fill, UI.FitText)
end
)
end)
-- )
-- UI.pop_parent(ctx)
-- end
end
function render_log_window(ctx)
if Editor.get_current_buffer_index() ~= -2 then
LogWindowOpen = false
end
if LogWindowOpen or LogWindowOpenElapsed > 0 then
if LogWindowOpen and LogWindowOpenElapsed < numFrames then
LogWindowOpenElapsed = LogWindowOpenElapsed + 1
elseif not LogWindowOpen and LogWindowOpenElapsed > 0 then
LogWindowOpenElapsed = LogWindowOpenElapsed - 1
end
end
if LogWindowOpen or LogWindowOpenElapsed > 0 then
window_percent = 75
if LogWindowOpenElapsed > 0 then
window_percent = ((LogWindowOpenElapsed/numFrames) * 75)
end
UI.push_parent(ctx, UI.push_floating(ctx, "log window canvas", 0, 0))
centered(ctx, "log window", UI.Horizontal, UI.PercentOfParent(window_percent), UI.PercentOfParent(window_percent), (
function ()
UI.push_parent(ctx, UI.push_rect(ctx, "window", true, true, UI.Horizontal, UI.Fill, UI.Fill))
-- -2 is the log buffer
UI.buffer(ctx, -2)
UI.pop_parent(ctx)
end
))
UI.pop_parent(ctx)
end
end
function handle_buffer_input()
end
function M.OnLoad()
Editor.log("Legacy View plugin loaded")
Editor.register_key_group({
{Editor.Key.Backtick, "Open Editor Logs", (function ()
if not LogWindowOpen then
LogWindowOpen = true
Editor.set_current_buffer_from_index(-2)
else
LogWindowOpen = false
local code_view = CodeViews[ActiveCodeView]
Editor.set_current_buffer_from_index(code_view.tabs[code_view.current_tab]["buffer_index"])
end
end)},
{Editor.Key.Space, "", {
{Editor.Key.P, "Command Palette",
(function ()
CommandSearchOpen = true
CommandSearchIndex = 1
CommandList = Editor.query_command_group("nl.spacegirl.editor.core")
M.open_command_palette()
end),
},
{Editor.Key.B, "Buffer Search", M.open_buffer_search_window}
}}
})
Editor.register_hook(Editor.Hook.OnDraw, M.render_ui_window)
Editor.register_hook(Editor.Hook.OnBufferInput, handle_buffer_input)
end
return M

View File

@ -1 +0,0 @@
stable

View File

@ -8,54 +8,13 @@ import "core:log"
import "vendor:sdl2" import "vendor:sdl2"
import lua "vendor:lua/5.4" import lua "vendor:lua/5.4"
import "../plugin"
Mode :: enum { Mode :: enum {
Normal, Normal,
Insert, Insert,
Visual, Visual,
} }
Window :: struct {
input_map: InputActions,
draw: plugin.WindowDrawProc,
free_user_data: plugin.WindowFreeProc,
get_buffer: plugin.WindowGetBufferProc,
// TODO: create hook for when mode changes happen
user_data: rawptr,
}
NewWindow :: struct {
input_map: InputActions,
lua_draw_proc: i32,
}
request_window_close :: proc(state: ^State) {
state.should_close_window = true;
}
close_window_and_free :: proc(state: ^State) {
if state.window != nil {
if state.window.free_user_data != nil {
state.window.free_user_data(state.plugin_vtable, state.window.user_data);
}
delete_input_actions(&state.window.input_map);
free(state.window);
state.window = nil;
}
if window, ok := &state.new_window.(NewWindow); ok {
delete_input_actions(&window.input_map);
state.new_window = nil
}
state.current_input_map = &state.input_map.mode[.Normal];
}
LuaHookRef :: i32;
EditorCommandList :: map[string][dynamic]EditorCommand; EditorCommandList :: map[string][dynamic]EditorCommand;
State :: struct { State :: struct {
ctx: runtime.Context, ctx: runtime.Context,
@ -82,10 +41,6 @@ State :: struct {
log_buffer: FileBuffer, log_buffer: FileBuffer,
window: ^Window,
new_window: Maybe(NewWindow),
should_close_window: bool,
input_map: InputMap, input_map: InputMap,
current_input_map: ^InputActions, current_input_map: ^InputActions,
@ -95,13 +50,6 @@ State :: struct {
active_panels: [128]Maybe(Panel), active_panels: [128]Maybe(Panel),
panel_catalog: [dynamic]PanelId, panel_catalog: [dynamic]PanelId,
plugins: [dynamic]plugin.Interface,
new_plugins: [dynamic]plugin.NewInterface,
plugin_vtable: plugin.Plugin,
highlighters: map[string]plugin.OnColorBufferProc,
hooks: map[plugin.Hook][dynamic]plugin.OnHookProc,
lua_hooks: map[plugin.Hook][dynamic]LuaHookRef,
} }
EditorCommand :: struct { EditorCommand :: struct {
@ -120,26 +68,13 @@ EditorCommandArgument :: union #no_nil {
i32 i32
} }
PanelId :: union #no_nil { PanelId :: union {
LuaPanelId,
LibPanelId, LibPanelId,
} }
Panel :: union #no_nil { Panel :: union {
LuaPanel,
LibPanel, LibPanel,
} }
LuaPanelId :: struct {
id: string,
name: string,
}
LuaPanel :: struct {
panel_id: LuaPanelId,
index: i32,
render_ref: i32
}
// TODO // TODO
LibPanelId :: struct {} LibPanelId :: struct {}
LibPanel :: struct {} LibPanel :: struct {}
@ -164,30 +99,8 @@ buffer_from_index :: proc(state: ^State, buffer_index: int) -> ^FileBuffer {
return &state.buffers[buffer_index]; return &state.buffers[buffer_index];
} }
add_hook :: proc(state: ^State, hook: plugin.Hook, hook_proc: plugin.OnHookProc) {
if _, exists := state.hooks[hook]; !exists {
state.hooks[hook] = make([dynamic]plugin.OnHookProc);
}
runtime.append(&state.hooks[hook], hook_proc);
}
add_lua_hook :: proc(state: ^State, hook: plugin.Hook, hook_ref: LuaHookRef) {
if _, exists := state.lua_hooks[hook]; !exists {
state.lua_hooks[hook] = make([dynamic]LuaHookRef);
log.info("added lua hook", hook)
}
runtime.append(&state.lua_hooks[hook], hook_ref);
}
LuaEditorAction :: struct {
fn_ref: i32,
maybe_input_map: InputActions,
};
PluginEditorAction :: proc "c" (plugin: plugin.Plugin);
EditorAction :: proc(state: ^State); EditorAction :: proc(state: ^State);
InputGroup :: union {LuaEditorAction, PluginEditorAction, EditorAction, InputActions} InputGroup :: union {EditorAction, InputActions}
Action :: struct { Action :: struct {
action: InputGroup, action: InputGroup,
description: string, description: string,
@ -196,8 +109,8 @@ InputMap :: struct {
mode: map[Mode]InputActions, mode: map[Mode]InputActions,
} }
InputActions :: struct { InputActions :: struct {
key_actions: map[plugin.Key]Action, key_actions: map[Key]Action,
ctrl_key_actions: map[plugin.Key]Action, ctrl_key_actions: map[Key]Action,
} }
new_input_map :: proc() -> InputMap { new_input_map :: proc() -> InputMap {
@ -217,8 +130,8 @@ new_input_map :: proc() -> InputMap {
new_input_actions :: proc() -> InputActions { new_input_actions :: proc() -> InputActions {
input_actions := InputActions { input_actions := InputActions {
key_actions = make(map[plugin.Key]Action), key_actions = make(map[Key]Action),
ctrl_key_actions = make(map[plugin.Key]Action), ctrl_key_actions = make(map[Key]Action),
} }
return input_actions; return input_actions;
@ -234,22 +147,7 @@ delete_input_actions :: proc(input_map: ^InputActions) {
delete(input_map.ctrl_key_actions); delete(input_map.ctrl_key_actions);
} }
// NOTE(pcleavelin): might be a bug in the compiler where it can't coerce register_key_action_single :: proc(input_map: ^InputActions, key: Key, action: EditorAction, description: string = "") {
// `EditorAction` to `InputGroup` when given as a proc parameter, that is why there
// are two functions
register_plugin_key_action_single :: proc(input_map: ^InputActions, key: plugin.Key, action: PluginEditorAction, description: string = "") {
if ok := key in input_map.key_actions; ok {
// TODO: log that key is already registered
log.error("plugin key already registered with single action", key);
}
input_map.key_actions[key] = Action {
action = action,
description = description,
};
}
register_key_action_single :: proc(input_map: ^InputActions, key: plugin.Key, action: EditorAction, description: string = "") {
if ok := key in input_map.key_actions; ok { if ok := key in input_map.key_actions; ok {
// TODO: log that key is already registered // TODO: log that key is already registered
log.error("key already registered with single action", key); log.error("key already registered with single action", key);
@ -261,7 +159,7 @@ register_key_action_single :: proc(input_map: ^InputActions, key: plugin.Key, ac
}; };
} }
register_key_action_group :: proc(input_map: ^InputActions, key: plugin.Key, input_group: InputGroup, description: string = "") { register_key_action_group :: proc(input_map: ^InputActions, key: Key, input_group: InputGroup, description: string = "") {
if ok := key in input_map.key_actions; ok { if ok := key in input_map.key_actions; ok {
// TODO: log that key is already registered // TODO: log that key is already registered
fmt.eprintln("key already registered with single action", key); fmt.eprintln("key already registered with single action", key);
@ -273,7 +171,7 @@ register_key_action_group :: proc(input_map: ^InputActions, key: plugin.Key, inp
}; };
} }
register_ctrl_key_action_single :: proc(input_map: ^InputActions, key: plugin.Key, action: EditorAction, description: string = "") { register_ctrl_key_action_single :: proc(input_map: ^InputActions, key: Key, action: EditorAction, description: string = "") {
if ok := key in input_map.key_actions; ok { if ok := key in input_map.key_actions; ok {
// TODO: log that key is already registered // TODO: log that key is already registered
log.error("key already registered with single action", key); log.error("key already registered with single action", key);
@ -285,7 +183,7 @@ register_ctrl_key_action_single :: proc(input_map: ^InputActions, key: plugin.Ke
}; };
} }
register_ctrl_key_action_group :: proc(input_map: ^InputActions, key: plugin.Key, input_group: InputGroup, description: string = "") { register_ctrl_key_action_group :: proc(input_map: ^InputActions, key: Key, input_group: InputGroup, description: string = "") {
if ok := key in input_map.key_actions; ok { if ok := key in input_map.key_actions; ok {
// TODO: log that key is already registered // TODO: log that key is already registered
log.error("key already registered with single action", key); log.error("key already registered with single action", key);
@ -297,7 +195,7 @@ register_ctrl_key_action_group :: proc(input_map: ^InputActions, key: plugin.Key
}; };
} }
register_key_action :: proc{register_plugin_key_action_single, register_key_action_single, register_key_action_group}; register_key_action :: proc{register_key_action_single, register_key_action_group};
register_ctrl_key_action :: proc{register_ctrl_key_action_single, register_ctrl_key_action_group}; register_ctrl_key_action :: proc{register_ctrl_key_action_single, register_ctrl_key_action_group};
register_editor_command :: proc(command_list: ^EditorCommandList, command_group, name, description: string, action: EditorAction) { register_editor_command :: proc(command_list: ^EditorCommandList, command_group, name, description: string, action: EditorAction) {
@ -427,10 +325,245 @@ where intrinsics.type_is_struct(T) {
return return
} }
register_panel_lua :: proc(state: ^State, name: string, id: string) { Key :: enum {
append(&state.panel_catalog, LuaPanelId { UNKNOWN = 0,
id = id, ENTER = 13,
name = name, ESCAPE = 27,
}) BACKSPACE = 8,
TAB = 9,
SPACE = 32,
EXCLAIM = 33,
QUOTEDBL = 34,
HASH = 35,
PERCENT = 37,
DOLLAR = 36,
AMPERSAND = 38,
QUOTE = 39,
LEFTPAREN = 40,
RIGHTPAREN = 41,
ASTERISK = 42,
PLUS = 43,
COMMA = 44,
MINUS = 45,
PERIOD = 46,
SLASH = 47,
NUM0 = 48,
NUM1 = 49,
NUM2 = 50,
NUM3 = 51,
NUM4 = 52,
NUM5 = 53,
NUM6 = 54,
NUM7 = 55,
NUM8 = 56,
NUM9 = 57,
COLON = 58,
SEMICOLON = 59,
LESS = 60,
EQUAL = 61,
GREATER = 62,
QUESTION = 63,
AT = 64,
LEFTBRACKET = 91,
BACKSLASH = 92,
RIGHTBRACKET = 93,
CARET = 94,
UNDERSCORE = 95,
BACKQUOTE = 96,
A = 97,
B = 98,
C = 99,
D = 100,
E = 101,
F = 102,
G = 103,
H = 104,
I = 105,
J = 106,
K = 107,
L = 108,
M = 109,
N = 110,
O = 111,
P = 112,
Q = 113,
R = 114,
S = 115,
T = 116,
U = 117,
V = 118,
W = 119,
X = 120,
Y = 121,
Z = 122,
CAPSLOCK = 1073741881,
F1 = 1073741882,
F2 = 1073741883,
F3 = 1073741884,
F4 = 1073741885,
F5 = 1073741886,
F6 = 1073741887,
F7 = 1073741888,
F8 = 1073741889,
F9 = 1073741890,
F10 = 1073741891,
F11 = 1073741892,
F12 = 1073741893,
PRINTSCREEN = 1073741894,
SCROLLLOCK = 1073741895,
PAUSE = 1073741896,
INSERT = 1073741897,
HOME = 1073741898,
PAGEUP = 1073741899,
DELETE = 127,
END = 1073741901,
PAGEDOWN = 1073741902,
RIGHT = 1073741903,
LEFT = 1073741904,
DOWN = 1073741905,
UP = 1073741906,
NUMLOCKCLEAR = 1073741907,
KP_DIVIDE = 1073741908,
KP_MULTIPLY = 1073741909,
KP_MINUS = 1073741910,
KP_PLUS = 1073741911,
KP_ENTER = 1073741912,
KP_1 = 1073741913,
KP_2 = 1073741914,
KP_3 = 1073741915,
KP_4 = 1073741916,
KP_5 = 1073741917,
KP_6 = 1073741918,
KP_7 = 1073741919,
KP_8 = 1073741920,
KP_9 = 1073741921,
KP_0 = 1073741922,
KP_PERIOD = 1073741923,
APPLICATION = 1073741925,
POWER = 1073741926,
KP_EQUALS = 1073741927,
F13 = 1073741928,
F14 = 1073741929,
F15 = 1073741930,
F16 = 1073741931,
F17 = 1073741932,
F18 = 1073741933,
F19 = 1073741934,
F20 = 1073741935,
F21 = 1073741936,
F22 = 1073741937,
F23 = 1073741938,
F24 = 1073741939,
EXECUTE = 1073741940,
HELP = 1073741941,
MENU = 1073741942,
SELECT = 1073741943,
STOP = 1073741944,
AGAIN = 1073741945,
UNDO = 1073741946,
CUT = 1073741947,
COPY = 1073741948,
PASTE = 1073741949,
FIND = 1073741950,
MUTE = 1073741951,
VOLUMEUP = 1073741952,
VOLUMEDOWN = 1073741953,
KP_COMMA = 1073741957,
KP_EQUALSAS400 = 1073741958,
ALTERASE = 1073741977,
SYSREQ = 1073741978,
CANCEL = 1073741979,
CLEAR = 1073741980,
PRIOR = 1073741981,
RETURN2 = 1073741982,
SEPARATOR = 1073741983,
OUT = 1073741984,
OPER = 1073741985,
CLEARAGAIN = 1073741986,
CRSEL = 1073741987,
EXSEL = 1073741988,
KP_00 = 1073742000,
KP_000 = 1073742001,
THOUSANDSSEPARATOR = 1073742002,
DECIMALSEPARATOR = 1073742003,
CURRENCYUNIT = 1073742004,
CURRENCYSUBUNIT = 1073742005,
KP_LEFTPAREN = 1073742006,
KP_RIGHTPAREN = 1073742007,
KP_LEFTBRACE = 1073742008,
KP_RIGHTBRACE = 1073742009,
KP_TAB = 1073742010,
KP_BACKSPACE = 1073742011,
KP_A = 1073742012,
KP_B = 1073742013,
KP_C = 1073742014,
KP_D = 1073742015,
KP_E = 1073742016,
KP_F = 1073742017,
KP_XOR = 1073742018,
KP_POWER = 1073742019,
KP_PERCENT = 1073742020,
KP_LESS = 1073742021,
KP_GREATER = 1073742022,
KP_AMPERSAND = 1073742023,
KP_DBLAMPERSAND = 1073742024,
KP_VERTICALBAR = 1073742025,
KP_DBLVERTICALBAR = 1073742026,
KP_COLON = 1073742027,
KP_HASH = 1073742028,
KP_SPACE = 1073742029,
KP_AT = 1073742030,
KP_EXCLAM = 1073742031,
KP_MEMSTORE = 1073742032,
KP_MEMRECALL = 1073742033,
KP_MEMCLEAR = 1073742034,
KP_MEMADD = 1073742035,
KP_MEMSUBTRACT = 1073742036,
KP_MEMMULTIPLY = 1073742037,
KP_MEMDIVIDE = 1073742038,
KP_PLUSMINUS = 1073742039,
KP_CLEAR = 1073742040,
KP_CLEARENTRY = 1073742041,
KP_BINARY = 1073742042,
KP_OCTAL = 1073742043,
KP_DECIMAL = 1073742044,
KP_HEXADECIMAL = 1073742045,
LCTRL = 1073742048,
LSHIFT = 1073742049,
LALT = 1073742050,
LGUI = 1073742051,
RCTRL = 1073742052,
RSHIFT = 1073742053,
RALT = 1073742054,
RGUI = 1073742055,
MODE = 1073742081,
AUDIONEXT = 1073742082,
AUDIOPREV = 1073742083,
AUDIOSTOP = 1073742084,
AUDIOPLAY = 1073742085,
AUDIOMUTE = 1073742086,
MEDIASELECT = 1073742087,
WWW = 1073742088,
MAIL = 1073742089,
CALCULATOR = 1073742090,
COMPUTER = 1073742091,
AC_SEARCH = 1073742092,
AC_HOME = 1073742093,
AC_BACK = 1073742094,
AC_FORWARD = 1073742095,
AC_STOP = 1073742096,
AC_REFRESH = 1073742097,
AC_BOOKMARKS = 1073742098,
BRIGHTNESSDOWN = 1073742099,
BRIGHTNESSUP = 1073742100,
DISPLAYSWITCH = 1073742101,
KBDILLUMTOGGLE = 1073742102,
KBDILLUMDOWN = 1073742103,
KBDILLUMUP = 1073742104,
EJECT = 1073742105,
SLEEP = 1073742106,
APP1 = 1073742107,
APP2 = 1073742108,
AUDIOREWIND = 1073742109,
AUDIOFASTFORWARD = 1073742110,
} }

View File

@ -11,7 +11,6 @@ import "base:runtime"
import "core:strings" import "core:strings"
import "../theme" import "../theme"
import "../plugin"
ScrollDir :: enum { ScrollDir :: enum {
Up, Up,
@ -752,32 +751,6 @@ next_buffer :: proc(state: ^State, prev_buffer: ^int) -> int {
return index; return index;
} }
into_buffer_info :: proc(state: ^State, buffer: ^FileBuffer) -> plugin.BufferInfo {
return plugin.BufferInfo {
buffer = buffer,
input = plugin.BufferInput {
bytes = raw_data(buffer.input_buffer),
length = len(buffer.input_buffer),
},
cursor = plugin.Cursor {
col = buffer.cursor.col,
line = buffer.cursor.line,
index = plugin.BufferIndex {
slice_index = buffer.cursor.index.slice_index,
content_index = buffer.cursor.index.content_index,
}
},
file_path = strings.clone_to_cstring(buffer.file_path, context.temp_allocator),
glyph_buffer_width = buffer.glyph_buffer_width,
glyph_buffer_height = buffer.glyph_buffer_height,
top_line = buffer.top_line,
};
}
into_buffer_info_from_index :: proc(state: ^State, buffer_index: int) -> plugin.BufferInfo {
buffer := buffer_from_index(state, buffer_index);
return into_buffer_info(state, buffer);
}
free_file_buffer :: proc(buffer: ^FileBuffer) { free_file_buffer :: proc(buffer: ^FileBuffer) {
delete(buffer.original_content); delete(buffer.original_content);
delete(buffer.added_content); delete(buffer.added_content);
@ -876,9 +849,8 @@ update_glyph_buffer :: proc(buffer: ^FileBuffer) {
draw_file_buffer :: proc(state: ^State, buffer: ^FileBuffer, x: int, y: int, show_line_numbers: bool = true) { draw_file_buffer :: proc(state: ^State, buffer: ^FileBuffer, x: int, y: int, show_line_numbers: bool = true) {
update_glyph_buffer(buffer); update_glyph_buffer(buffer);
if highlighter, exists := state.highlighters[buffer.extension]; exists {
highlighter(state.plugin_vtable, buffer); // TODO: syntax highlighting
}
padding := 0; padding := 0;
if show_line_numbers { if show_line_numbers {

File diff suppressed because it is too large Load Diff

View File

@ -1,693 +0,0 @@
package plugin
import "core:strings"
import core "../core"
import ui "../ui"
import lua "vendor:lua/5.4"
state: ^core.State = nil
new_lua_state :: proc() -> ^lua.State {
L := lua.L_newstate();
lua.L_openlibs(L);
bbb: [^]lua.L_Reg;
editor_lib := [?]lua.L_Reg {
lua.L_Reg {
"quit",
proc "c" (L: ^lua.State) -> i32 {
context = state.ctx;
state.should_close = true;
return i32(lua.OK);
}
},
lua.L_Reg {
"log",
proc "c" (L: ^lua.State) -> i32 {
context = state.ctx;
text := string(lua.L_checkstring(L, 1));
log.info("[LUA]:", text);
return i32(lua.OK);
}
},
lua.L_Reg {
"register_hook",
proc "c" (L: ^lua.State) -> i32 {
context = state.ctx;
hook := lua.L_checkinteger(L, 1);
lua.L_checktype(L, 2, i32(lua.TFUNCTION));
lua.pushvalue(L, 2);
fn_ref := lua.L_ref(L, i32(lua.REGISTRYINDEX));
core.add_lua_hook(&state, Hook(hook), fn_ref);
return i32(lua.OK);
}
},
lua.L_Reg {
"register_key_group",
proc "c" (L: ^lua.State) -> i32 {
context = state.ctx;
lua.L_checktype(L, 1, i32(lua.TTABLE));
table_to_action :: proc(L: ^lua.State, index: i32, input_map: ^core.InputActions) {
lua.len(L, index);
key_group_len := lua.tointeger(L, -1);
lua.pop(L, 1);
for i in 1..=key_group_len {
lua.rawgeti(L, index, i);
defer lua.pop(L, 1);
lua.rawgeti(L, -1, 1);
key:= Key(lua.tointeger(L, -1));
lua.pop(L, 1);
lua.rawgeti(L, -1, 2);
desc := strings.clone(string(lua.tostring(L, -1)));
lua.pop(L, 1);
switch lua.rawgeti(L, -1, 3) {
case i32(lua.TTABLE):
if action, exists := input_map.key_actions[key]; exists {
switch value in action.action {
case core.LuaEditorAction:
log.warn("Plugin attempted to register input group on existing key action (added from Lua)");
case core.PluginEditorAction:
log.warn("Plugin attempted to register input group on existing key action (added from Plugin)");
case core.EditorAction:
log.warn("Plugin attempted to register input group on existing key action");
case core.InputActions:
input_map := &(&input_map.key_actions[key]).action.(core.InputActions);
table_to_action(L, lua.gettop(L), input_map);
}
} else {
core.register_key_action(input_map, key, core.new_input_actions(), desc);
table_to_action(L, lua.gettop(L), &((&input_map.key_actions[key]).action.(core.InputActions)));
}
lua.pop(L, 1);
case i32(lua.TFUNCTION):
fn_ref := lua.L_ref(L, i32(lua.REGISTRYINDEX));
if lua.rawgeti(L, -1, 4) == i32(lua.TTABLE) {
maybe_input_map := core.new_input_actions();
table_to_action(L, lua.gettop(L), &maybe_input_map);
core.register_key_action_group(input_map, key, core.LuaEditorAction { fn_ref, maybe_input_map }, desc);
} else {
core.register_key_action_group(input_map, key, core.LuaEditorAction { fn_ref, core.InputActions {} }, desc);
}
case:
lua.pop(L, 1);
}
}
}
table_to_action(L, 1, state.current_input_map);
return i32(lua.OK);
}
},
lua.L_Reg {
"request_window_close",
proc "c" (L: ^lua.State) -> i32 {
context = state.ctx;
core.request_window_close(&state);
return i32(lua.OK);
}
},
lua.L_Reg {
"get_current_buffer_index",
proc "c" (L: ^lua.State) -> i32 {
context = state.ctx;
lua.pushinteger(L, lua.Integer(state.current_buffer));
return 1;
}
},
lua.L_Reg {
"set_current_buffer_from_index",
proc "c" (L: ^lua.State) -> i32 {
context = state.ctx;
buffer_index := int(lua.L_checkinteger(L, 1));
if buffer_index != -2 && (buffer_index < 0 || buffer_index >= len(state.buffers)) {
return i32(lua.ERRRUN);
} else {
state.current_buffer = buffer_index;
}
return i32(lua.OK);
}
},
lua.L_Reg {
"buffer_info_from_index",
proc "c" (L: ^lua.State) -> i32 {
context = state.ctx;
buffer_index := int(lua.L_checkinteger(L, 1));
if buffer_index < 0 || buffer_index >= len(state.buffers) {
lua.pushnil(L);
} else {
push_lua_buffer_info :: proc(L: ^lua.State, buffer: ^FileBuffer) {
lua.newtable(L);
{
lua.pushlightuserdata(L, buffer);
lua.setfield(L, -2, "buffer");
lua.newtable(L);
{
lua.pushinteger(L, lua.Integer(buffer.cursor.col));
lua.setfield(L, -2, "col");
lua.pushinteger(L, lua.Integer(buffer.cursor.line));
lua.setfield(L, -2, "line");
}
lua.setfield(L, -2, "cursor");
lua.pushstring(L, strings.clone_to_cstring(buffer.file_path, context.temp_allocator));
lua.setfield(L, -2, "full_file_path");
relative_file_path, _ := filepath.rel(state.directory, buffer.file_path, context.temp_allocator)
lua.pushstring(L, strings.clone_to_cstring(relative_file_path, context.temp_allocator));
lua.setfield(L, -2, "file_path");
}
}
push_lua_buffer_info(L, core.buffer_from_index(&state, buffer_index));
}
return 1;
}
},
lua.L_Reg {
"query_command_group",
proc "c" (L: ^lua.State) -> i32 {
context = state.ctx;
group := lua.L_checkstring(L, 1);
cmds := core.query_editor_commands_by_group(&state.commands, string(group), scratch_alloc);
lua.newtable(L);
{
for cmd, i in cmds {
lua.newtable(L);
{
lua.pushstring(L, strings.clone_to_cstring(cmd.name, scratch_alloc));
lua.setfield(L, -2, "name");
lua.pushstring(L, strings.clone_to_cstring(cmd.description, scratch_alloc));
lua.setfield(L, -2, "description");
}
lua.rawseti(L, -2, lua.Integer(i+1));
}
}
return 1;
}
},
lua.L_Reg {
"run_command",
proc "c" (L: ^lua.State) -> i32 {
context = state.ctx;
group := lua.L_checkstring(L, 1);
name := lua.L_checkstring(L, 2);
core.run_command(&state, string(group), string(name));
return 1;
}
}
};
bbb = raw_data(editor_lib[:]);
get_lua_semantic_size :: proc(L: ^lua.State, index: i32) -> ui.SemanticSize {
if lua.istable(L, index) {
lua.rawgeti(L, index, 1);
semantic_kind := ui.SemanticSizeKind(lua.tointeger(L, -1));
lua.pop(L, 1);
lua.rawgeti(L, index, 2);
semantic_value := int(lua.tointeger(L, -1));
lua.pop(L, 1);
return {semantic_kind, semantic_value};
} else {
semantic_kind := ui.SemanticSizeKind(lua.L_checkinteger(L, index));
return {semantic_kind, 0};
}
}
push_lua_semantic_size_table :: proc(L: ^lua.State, size: ui.SemanticSize) {
lua.newtable(L);
{
lua.pushinteger(L, lua.Integer(i32(size.kind)));
lua.rawseti(L, -2, 1);
lua.pushinteger(L, lua.Integer(size.value));
lua.rawseti(L, -2, 2);
}
}
push_lua_box_interaction :: proc(L: ^lua.State, interaction: ui.Interaction) {
lua.newtable(L);
{
lua.pushboolean(L, b32(interaction.clicked));
lua.setfield(L, -2, "clicked");
lua.pushboolean(L, b32(interaction.hovering));
lua.setfield(L, -2, "hovering");
lua.pushboolean(L, b32(interaction.dragging));
lua.setfield(L, -2, "dragging");
lua.newtable(L);
{
lua.pushinteger(L, lua.Integer(interaction.box_pos.x));
lua.setfield(L, -2, "x");
lua.pushinteger(L, lua.Integer(interaction.box_pos.y));
lua.setfield(L, -2, "y");
}
lua.setfield(L, -2, "box_pos");
lua.newtable(L);
{
lua.pushinteger(L, lua.Integer(interaction.box_size.x));
lua.setfield(L, -2, "x");
lua.pushinteger(L, lua.Integer(interaction.box_size.y));
lua.setfield(L, -2, "y");
}
lua.setfield(L, -2, "box_size");
}
}
ui_lib := [?]lua.L_Reg {
lua.L_Reg {
"get_mouse_pos",
proc "c" (L: ^lua.State) -> i32 {
context = state.ctx;
lua.L_checktype(L, 1, i32(lua.TLIGHTUSERDATA));
lua.pushvalue(L, 1);
ui_ctx := transmute(^ui.Context)lua.touserdata(L, -1);
if ui_ctx == nil { return i32(lua.ERRRUN); }
lua.pushinteger(L, lua.Integer(ui_ctx.mouse_x));
lua.pushinteger(L, lua.Integer(ui_ctx.mouse_y));
return 2;
}
},
lua.L_Reg {
"Exact",
proc "c" (L: ^lua.State) -> i32 {
context = state.ctx;
value := lua.L_checknumber(L, 1);
push_lua_semantic_size_table(L, { ui.SemanticSizeKind.Exact, int(value) });
return 1;
}
},
lua.L_Reg {
"PercentOfParent",
proc "c" (L: ^lua.State) -> i32 {
context = state.ctx;
value := lua.L_checknumber(L, 1);
push_lua_semantic_size_table(L, { ui.SemanticSizeKind.PercentOfParent, int(value) });
return 1;
}
},
lua.L_Reg {
"push_parent",
proc "c" (L: ^lua.State) -> i32 {
context = state.ctx;
lua.L_checktype(L, 1, i32(lua.TLIGHTUSERDATA));
lua.pushvalue(L, 1);
ui_ctx := transmute(^ui.Context)lua.touserdata(L, -1);
if ui_ctx == nil { return i32(lua.ERRRUN); }
lua.L_checktype(L, 2, i32(lua.TLIGHTUSERDATA));
lua.pushvalue(L, 2);
box := transmute(^ui.Box)lua.touserdata(L, -1);
if box == nil { return i32(lua.ERRRUN); }
ui.push_parent(ui_ctx, box);
return i32(lua.OK);
}
},
lua.L_Reg {
"pop_parent",
proc "c" (L: ^lua.State) -> i32 {
context = state.ctx;
lua.L_checktype(L, 1, i32(lua.TLIGHTUSERDATA));
lua.pushvalue(L, 1);
ui_ctx := transmute(^ui.Context)lua.touserdata(L, -1);
if ui_ctx == nil { return i32(lua.ERRRUN); }
ui.pop_parent(ui_ctx);
return i32(lua.OK);
}
},
lua.L_Reg {
"push_floating",
proc "c" (L: ^lua.State) -> i32 {
context = state.ctx;
lua.L_checktype(L, 1, i32(lua.TLIGHTUSERDATA));
lua.pushvalue(L, 1);
ui_ctx := transmute(^ui.Context)lua.touserdata(L, -1);
if ui_ctx != nil {
label := lua.L_checkstring(L, 2);
x := int(lua.L_checkinteger(L, 3));
y := int(lua.L_checkinteger(L, 4));
box, interaction := ui.push_floating(ui_ctx, strings.clone(string(label), context.temp_allocator), {x,y});
lua.pushlightuserdata(L, box);
push_lua_box_interaction(L, interaction);
return 2;
}
return i32(lua.ERRRUN);
}
},
lua.L_Reg {
"push_box",
proc "c" (L: ^lua.State) -> i32 {
context = state.ctx;
lua.L_checktype(L, 1, i32(lua.TLIGHTUSERDATA));
lua.pushvalue(L, 1);
ui_ctx := transmute(^ui.Context)lua.touserdata(L, -1);
if ui_ctx != nil {
label := lua.L_checkstring(L, 2);
flags, err := lua_ui_flags(L, 3);
axis := ui.Axis(lua.L_checkinteger(L, 4));
semantic_width := get_lua_semantic_size(L, 5);
semantic_height := get_lua_semantic_size(L, 6);
box, interaction := ui.push_box(ui_ctx, strings.clone(string(label), context.temp_allocator), flags, axis, { semantic_width, semantic_height });
lua.pushlightuserdata(L, box);
push_lua_box_interaction(L, interaction)
return 2;
}
return i32(lua.ERRRUN);
}
},
lua.L_Reg {
"_box_interaction",
proc "c" (L: ^lua.State) -> i32 {
context = state.ctx;
lua.L_checktype(L, 1, i32(lua.TLIGHTUSERDATA));
lua.pushvalue(L, 1);
ui_ctx := transmute(^ui.Context)lua.touserdata(L, -1);
if ui_ctx == nil { return i32(lua.ERRRUN); }
lua.L_checktype(L, 2, i32(lua.TLIGHTUSERDATA));
lua.pushvalue(L, 2);
box := transmute(^ui.Box)lua.touserdata(L, -1);
if box == nil { return i32(lua.ERRRUN); }
interaction := ui.test_box(ui_ctx, box);
push_lua_box_interaction(L, interaction)
return 1;
}
},
lua.L_Reg {
"push_box",
proc "c" (L: ^lua.State) -> i32 {
context = state.ctx;
lua.L_checktype(L, 1, i32(lua.TLIGHTUSERDATA));
lua.pushvalue(L, 1);
ui_ctx := transmute(^ui.Context)lua.touserdata(L, -1);
if ui_ctx != nil {
label := lua.L_checkstring(L, 2);
flags, err := lua_ui_flags(L, 3);
axis := ui.Axis(lua.L_checkinteger(L, 4));
semantic_width := get_lua_semantic_size(L, 5);
semantic_height := get_lua_semantic_size(L, 6);
box, interaction := ui.push_box(ui_ctx, strings.clone(string(label), context.temp_allocator), flags, axis, { semantic_width, semantic_height });
lua.pushlightuserdata(L, box);
push_lua_box_interaction(L, interaction)
return 2;
}
return i32(lua.ERRRUN);
}
},
lua.L_Reg {
"push_rect",
proc "c" (L: ^lua.State) -> i32 {
context = state.ctx;
lua.L_checktype(L, 1, i32(lua.TLIGHTUSERDATA));
lua.pushvalue(L, 1);
ui_ctx := transmute(^ui.Context)lua.touserdata(L, -1);
if ui_ctx != nil {
label := lua.L_checkstring(L, 2);
background := bool(lua.toboolean(L, 3));
border := bool(lua.toboolean(L, 4));
axis := ui.Axis(lua.L_checkinteger(L, 5));
semantic_width := get_lua_semantic_size(L, 6);
semantic_height := get_lua_semantic_size(L, 7);
box, interaction := ui.push_rect(ui_ctx, strings.clone(string(label), context.temp_allocator), background, border, axis, { semantic_width, semantic_height });
lua.pushlightuserdata(L, box);
push_lua_box_interaction(L, interaction)
return 2;
}
return i32(lua.ERRRUN);
}
},
lua.L_Reg {
"spacer",
proc "c" (L: ^lua.State) -> i32 {
context = state.ctx;
lua.L_checktype(L, 1, i32(lua.TLIGHTUSERDATA));
lua.pushvalue(L, 1);
ui_ctx := transmute(^ui.Context)lua.touserdata(L, -1);
if ui_ctx != nil {
label := lua.L_checkstring(L, 2);
interaction := ui.spacer(ui_ctx, strings.clone(string(label), context.temp_allocator), semantic_size = {{.Fill, 0}, {.Fill, 0}});
push_lua_box_interaction(L, interaction)
return 1;
}
return i32(lua.ERRRUN);
}
},
lua.L_Reg {
"label",
proc "c" (L: ^lua.State) -> i32 {
context = state.ctx;
lua.L_checktype(L, 1, i32(lua.TLIGHTUSERDATA));
lua.pushvalue(L, 1);
ui_ctx := transmute(^ui.Context)lua.touserdata(L, -1);
if ui_ctx != nil {
label := lua.L_checkstring(L, 2);
interaction := ui.label(ui_ctx, strings.clone(string(label), context.temp_allocator));
push_lua_box_interaction(L, interaction)
return 1;
}
return i32(lua.ERRRUN);
}
},
lua.L_Reg {
"button",
proc "c" (L: ^lua.State) -> i32 {
context = state.ctx;
lua.L_checktype(L, 1, i32(lua.TLIGHTUSERDATA));
lua.pushvalue(L, 1);
ui_ctx := transmute(^ui.Context)lua.touserdata(L, -1);
if ui_ctx != nil {
label := lua.L_checkstring(L, 2);
interaction := ui.button(ui_ctx, strings.clone(string(label), context.temp_allocator));
push_lua_box_interaction(L, interaction)
return 1;
}
return i32(lua.ERRRUN);
}
},
lua.L_Reg {
"advanced_button",
proc "c" (L: ^lua.State) -> i32 {
context = state.ctx;
lua.L_checktype(L, 1, i32(lua.TLIGHTUSERDATA));
lua.pushvalue(L, 1);
ui_ctx := transmute(^ui.Context)lua.touserdata(L, -1);
if ui_ctx != nil {
label := lua.L_checkstring(L, 2);
flags, err := lua_ui_flags(L, 3);
semantic_width := get_lua_semantic_size(L, 4);
semantic_height := get_lua_semantic_size(L, 5);
interaction := ui.advanced_button(ui_ctx, strings.clone(string(label), context.temp_allocator), flags, { semantic_width, semantic_height });
push_lua_box_interaction(L, interaction)
return 1;
}
return i32(lua.ERRRUN);
}
},
lua.L_Reg {
"buffer",
proc "c" (L: ^lua.State) -> i32 {
context = state.ctx;
lua.L_checktype(L, 1, i32(lua.TLIGHTUSERDATA));
lua.pushvalue(L, 1);
ui_ctx := transmute(^ui.Context)lua.touserdata(L, -1);
if ui_ctx != nil {
buffer_index := int(lua.L_checkinteger(L, 2));
if buffer_index != -2 && (buffer_index < 0 || buffer_index >= len(state.buffers)) {
return i32(lua.ERRRUN);
}
ui_file_buffer(ui_ctx, core.buffer_from_index(&state, buffer_index));
return i32(lua.OK);
}
return i32(lua.ERRRUN);
}
},
lua.L_Reg {
"log_buffer",
proc "c" (L: ^lua.State) -> i32 {
context = state.ctx;
lua.L_checktype(L, 1, i32(lua.TLIGHTUSERDATA));
lua.pushvalue(L, 1);
ui_ctx := transmute(^ui.Context)lua.touserdata(L, -1);
if ui_ctx != nil {
ui_file_buffer(ui_ctx, &state.log_buffer);
return i32(lua.OK);
}
return i32(lua.ERRRUN);
}
}
};
// TODO: generate this from the plugin.Key enum
lua.newtable(state.L);
{
lua.newtable(state.L);
lua.pushinteger(state.L, lua.Integer(Key.B));
lua.setfield(state.L, -2, "B");
lua.pushinteger(state.L, lua.Integer(Key.T));
lua.setfield(state.L, -2, "T");
lua.pushinteger(state.L, lua.Integer(Key.Y));
lua.setfield(state.L, -2, "Y");
lua.pushinteger(state.L, lua.Integer(Key.P));
lua.setfield(state.L, -2, "P");
lua.pushinteger(state.L, lua.Integer(Key.M));
lua.setfield(state.L, -2, "M");
lua.pushinteger(state.L, lua.Integer(Key.K));
lua.setfield(state.L, -2, "K");
lua.pushinteger(state.L, lua.Integer(Key.J));
lua.setfield(state.L, -2, "J");
lua.pushinteger(state.L, lua.Integer(Key.Q));
lua.setfield(state.L, -2, "Q");
lua.pushinteger(state.L, lua.Integer(Key.BACKQUOTE));
lua.setfield(state.L, -2, "Backtick");
lua.pushinteger(state.L, lua.Integer(Key.ESCAPE));
lua.setfield(state.L, -2, "Escape");
lua.pushinteger(state.L, lua.Integer(Key.ENTER));
lua.setfield(state.L, -2, "Enter");
lua.pushinteger(state.L, lua.Integer(Key.SPACE));
lua.setfield(state.L, -2, "Space");
}
lua.setfield(state.L, -2, "Key");
{
lua.newtable(state.L);
lua.pushinteger(state.L, lua.Integer(Hook.BufferInput));
lua.setfield(state.L, -2, "OnBufferInput");
lua.pushinteger(state.L, lua.Integer(Hook.Draw));
lua.setfield(state.L, -2, "OnDraw");
}
lua.setfield(state.L, -2, "Hook");
lua.L_setfuncs(state.L, bbb, 0);
lua.setglobal(state.L, "Editor");
lua.newtable(state.L);
{
lua.pushinteger(state.L, lua.Integer(ui.Axis.Horizontal));
lua.setfield(state.L, -2, "Horizontal");
lua.pushinteger(state.L, lua.Integer(ui.Axis.Vertical));
lua.setfield(state.L, -2, "Vertical");
push_lua_semantic_size_table(state.L, { ui.SemanticSizeKind.Fill, 0 });
lua.setfield(state.L, -2, "Fill");
push_lua_semantic_size_table(state.L, { ui.SemanticSizeKind.ChildrenSum, 0 });
lua.setfield(state.L, -2, "ChildrenSum");
push_lua_semantic_size_table(state.L, { ui.SemanticSizeKind.FitText, 0 });
lua.setfield(state.L, -2, "FitText");
lua.L_setfuncs(state.L, raw_data(&ui_lib), 0);
lua.setglobal(state.L, "UI");
}
return L
}

File diff suppressed because it is too large Load Diff

View File

@ -1,451 +0,0 @@
package plugin;
import "base:intrinsics"
import "core:dynlib"
import "core:fmt"
import "core:log"
import "../theme"
OnInitializeProc :: proc "c" (plugin: Plugin);
OnExitProc :: proc "c" (/* probably needs some state eventually */);
OnDrawProc :: proc "c" (plugin: Plugin);
Interface :: struct {
on_initialize: OnInitializeProc,
on_exit: OnExitProc,
on_draw: OnDrawProc,
}
NewInterface :: struct {
name: string,
version: string,
namespace: string,
}
BufferIndex :: struct {
slice_index: int,
content_index: int,
}
Cursor :: struct {
col: int,
line: int,
index: BufferIndex,
}
BufferIter :: struct {
cursor: Cursor,
buffer: rawptr,
hit_end: bool,
}
IterateResult :: struct {
char: u8,
should_continue: bool,
}
BufferInput :: struct {
bytes: [^]u8,
length: int,
}
BufferInfo :: struct {
buffer: rawptr,
file_path: cstring,
input: BufferInput,
cursor: Cursor,
glyph_buffer_width: int,
glyph_buffer_height: int,
top_line: int,
}
Buffer :: struct {
get_num_buffers: proc "c" () -> int,
get_buffer_info: proc "c" (buffer: rawptr) -> BufferInfo,
get_buffer_info_from_index: proc "c" (buffer_index: int) -> BufferInfo,
color_char_at: proc "c" (buffer: rawptr, start_cursor: Cursor, end_cursor: Cursor, palette_index: i32),
set_current_buffer: proc "c" (buffer_index: int),
open_buffer: proc "c" (path: cstring, line: int, col: int),
open_virtual_buffer: proc "c" () -> rawptr,
free_virtual_buffer: proc "c" (buffer: rawptr),
}
Iterator :: struct {
get_current_buffer_iterator: proc "c" () -> BufferIter,
get_buffer_iterator: proc "c" (buffer: rawptr) -> BufferIter,
get_char_at_iter: proc "c" (it: ^BufferIter) -> u8,
get_buffer_list_iter: proc "c" (prev_buffer: ^int) -> int,
iterate_buffer: proc "c" (it: ^BufferIter) -> IterateResult,
iterate_buffer_reverse: proc "c" (it: ^BufferIter) -> IterateResult,
iterate_buffer_until: proc "c" (it: ^BufferIter, until_proc: rawptr),
iterate_buffer_until_reverse: proc "c" (it: ^BufferIter, until_proc: rawptr),
iterate_buffer_peek: proc "c" (it: ^BufferIter) -> IterateResult,
until_line_break: rawptr,
until_single_quote: rawptr,
until_double_quote: rawptr,
until_end_of_word: rawptr,
}
UiInteraction :: struct {
hovering: bool,
clicked: bool
}
UiAxis :: enum {
Horizontal = 0,
Vertical,
}
UiSemanticSize :: struct {
kind: int,
value: int,
}
UiBox :: rawptr;
UiPushParentProc :: proc "c" (ui_context: rawptr, box: UiBox);
UiPopParentProc :: proc "c" (ui_context: rawptr);
UiFloatingProc :: proc "c" (ui_context: rawptr, label: cstring, pos: [2]int) -> UiBox;
UiCreateBoxProc :: proc "c" (ui_context: rawptr, label: cstring) -> UiBox;
UiRectProc :: proc "c" (ui_context: rawptr, label: cstring, background: bool, border: bool, axis: UiAxis, size: [2]UiSemanticSize) -> UiBox;
UiSimpleProc :: proc "c" (ui_context: rawptr, label: cstring) -> UiInteraction;
UiBufferProc :: proc "c" (ui_context: rawptr, buffer: rawptr, show_line_numbers: bool);
UiBufferIndexProc :: proc "c" (ui_context: rawptr, buffer: int, show_line_numbers: bool);
Ui :: struct {
ui_context: rawptr,
push_parent: UiPushParentProc,
pop_parent: UiPopParentProc,
spacer: UiSimpleProc,
floating: UiFloatingProc,
rect: UiRectProc,
button: UiSimpleProc,
label: UiSimpleProc,
buffer: UiBufferProc,
buffer_from_index: UiBufferIndexProc,
}
OnColorBufferProc :: proc "c" (plugin: Plugin, buffer: rawptr);
InputGroupProc :: proc "c" (plugin: Plugin, input_map: rawptr);
InputActionProc :: proc "c" (plugin: Plugin);
OnHookProc :: proc "c" (plugin: Plugin, buffer: rawptr);
WindowInputProc :: proc "c" (plugin: Plugin, window: rawptr);
WindowDrawProc :: proc "c" (plugin: Plugin, window: rawptr);
WindowGetBufferProc :: proc(plugin: Plugin, window: rawptr) -> rawptr;
WindowFreeProc :: proc "c" (plugin: Plugin, window: rawptr);
Plugin :: struct {
state: rawptr,
iter: Iterator,
buffer: Buffer,
ui: Ui,
register_hook: proc "c" (hook: Hook, on_hook: OnHookProc),
register_highlighter: proc "c" (extension: cstring, on_color_buffer: OnColorBufferProc),
register_input_group: proc "c" (input_map: rawptr, key: Key, register_group: InputGroupProc),
register_input: proc "c" (input_map: rawptr, key: Key, input_action: InputActionProc, description: cstring),
create_window: proc "c" (user_data: rawptr, register_group: InputGroupProc, draw_proc: WindowDrawProc, free_window_proc: WindowFreeProc, get_buffer_proc: WindowGetBufferProc) -> rawptr,
get_window: proc "c" () -> rawptr,
request_window_close: proc "c" (),
get_screen_width: proc "c" () -> int,
get_screen_height: proc "c" () -> int,
get_font_width: proc "c" () -> int,
get_font_height: proc "c" () -> int,
get_current_directory: proc "c" () -> cstring,
enter_insert_mode: proc "c" (),
draw_rect: proc "c" (x: i32, y: i32, width: i32, height: i32, color: theme.PaletteColor),
draw_text: proc "c" (text: cstring, x: f32, y: f32, color: theme.PaletteColor),
draw_buffer_from_index: proc "c" (buffer_index: int, x: int, y: int, glyph_buffer_width: int, glyph_buffer_height: int, show_line_numbers: bool),
draw_buffer: proc "c" (buffer: rawptr, x: int, y: int, glyph_buffer_width: int, glyph_buffer_height: int, show_line_numbers: bool),
}
Hook :: enum {
BufferInput = 0,
Draw = 1,
}
Key :: enum {
UNKNOWN = 0,
ENTER = 13,
ESCAPE = 27,
BACKSPACE = 8,
TAB = 9,
SPACE = 32,
EXCLAIM = 33,
QUOTEDBL = 34,
HASH = 35,
PERCENT = 37,
DOLLAR = 36,
AMPERSAND = 38,
QUOTE = 39,
LEFTPAREN = 40,
RIGHTPAREN = 41,
ASTERISK = 42,
PLUS = 43,
COMMA = 44,
MINUS = 45,
PERIOD = 46,
SLASH = 47,
NUM0 = 48,
NUM1 = 49,
NUM2 = 50,
NUM3 = 51,
NUM4 = 52,
NUM5 = 53,
NUM6 = 54,
NUM7 = 55,
NUM8 = 56,
NUM9 = 57,
COLON = 58,
SEMICOLON = 59,
LESS = 60,
EQUAL = 61,
GREATER = 62,
QUESTION = 63,
AT = 64,
LEFTBRACKET = 91,
BACKSLASH = 92,
RIGHTBRACKET = 93,
CARET = 94,
UNDERSCORE = 95,
BACKQUOTE = 96,
A = 97,
B = 98,
C = 99,
D = 100,
E = 101,
F = 102,
G = 103,
H = 104,
I = 105,
J = 106,
K = 107,
L = 108,
M = 109,
N = 110,
O = 111,
P = 112,
Q = 113,
R = 114,
S = 115,
T = 116,
U = 117,
V = 118,
W = 119,
X = 120,
Y = 121,
Z = 122,
CAPSLOCK = 1073741881,
F1 = 1073741882,
F2 = 1073741883,
F3 = 1073741884,
F4 = 1073741885,
F5 = 1073741886,
F6 = 1073741887,
F7 = 1073741888,
F8 = 1073741889,
F9 = 1073741890,
F10 = 1073741891,
F11 = 1073741892,
F12 = 1073741893,
PRINTSCREEN = 1073741894,
SCROLLLOCK = 1073741895,
PAUSE = 1073741896,
INSERT = 1073741897,
HOME = 1073741898,
PAGEUP = 1073741899,
DELETE = 127,
END = 1073741901,
PAGEDOWN = 1073741902,
RIGHT = 1073741903,
LEFT = 1073741904,
DOWN = 1073741905,
UP = 1073741906,
NUMLOCKCLEAR = 1073741907,
KP_DIVIDE = 1073741908,
KP_MULTIPLY = 1073741909,
KP_MINUS = 1073741910,
KP_PLUS = 1073741911,
KP_ENTER = 1073741912,
KP_1 = 1073741913,
KP_2 = 1073741914,
KP_3 = 1073741915,
KP_4 = 1073741916,
KP_5 = 1073741917,
KP_6 = 1073741918,
KP_7 = 1073741919,
KP_8 = 1073741920,
KP_9 = 1073741921,
KP_0 = 1073741922,
KP_PERIOD = 1073741923,
APPLICATION = 1073741925,
POWER = 1073741926,
KP_EQUALS = 1073741927,
F13 = 1073741928,
F14 = 1073741929,
F15 = 1073741930,
F16 = 1073741931,
F17 = 1073741932,
F18 = 1073741933,
F19 = 1073741934,
F20 = 1073741935,
F21 = 1073741936,
F22 = 1073741937,
F23 = 1073741938,
F24 = 1073741939,
EXECUTE = 1073741940,
HELP = 1073741941,
MENU = 1073741942,
SELECT = 1073741943,
STOP = 1073741944,
AGAIN = 1073741945,
UNDO = 1073741946,
CUT = 1073741947,
COPY = 1073741948,
PASTE = 1073741949,
FIND = 1073741950,
MUTE = 1073741951,
VOLUMEUP = 1073741952,
VOLUMEDOWN = 1073741953,
KP_COMMA = 1073741957,
KP_EQUALSAS400 = 1073741958,
ALTERASE = 1073741977,
SYSREQ = 1073741978,
CANCEL = 1073741979,
CLEAR = 1073741980,
PRIOR = 1073741981,
RETURN2 = 1073741982,
SEPARATOR = 1073741983,
OUT = 1073741984,
OPER = 1073741985,
CLEARAGAIN = 1073741986,
CRSEL = 1073741987,
EXSEL = 1073741988,
KP_00 = 1073742000,
KP_000 = 1073742001,
THOUSANDSSEPARATOR = 1073742002,
DECIMALSEPARATOR = 1073742003,
CURRENCYUNIT = 1073742004,
CURRENCYSUBUNIT = 1073742005,
KP_LEFTPAREN = 1073742006,
KP_RIGHTPAREN = 1073742007,
KP_LEFTBRACE = 1073742008,
KP_RIGHTBRACE = 1073742009,
KP_TAB = 1073742010,
KP_BACKSPACE = 1073742011,
KP_A = 1073742012,
KP_B = 1073742013,
KP_C = 1073742014,
KP_D = 1073742015,
KP_E = 1073742016,
KP_F = 1073742017,
KP_XOR = 1073742018,
KP_POWER = 1073742019,
KP_PERCENT = 1073742020,
KP_LESS = 1073742021,
KP_GREATER = 1073742022,
KP_AMPERSAND = 1073742023,
KP_DBLAMPERSAND = 1073742024,
KP_VERTICALBAR = 1073742025,
KP_DBLVERTICALBAR = 1073742026,
KP_COLON = 1073742027,
KP_HASH = 1073742028,
KP_SPACE = 1073742029,
KP_AT = 1073742030,
KP_EXCLAM = 1073742031,
KP_MEMSTORE = 1073742032,
KP_MEMRECALL = 1073742033,
KP_MEMCLEAR = 1073742034,
KP_MEMADD = 1073742035,
KP_MEMSUBTRACT = 1073742036,
KP_MEMMULTIPLY = 1073742037,
KP_MEMDIVIDE = 1073742038,
KP_PLUSMINUS = 1073742039,
KP_CLEAR = 1073742040,
KP_CLEARENTRY = 1073742041,
KP_BINARY = 1073742042,
KP_OCTAL = 1073742043,
KP_DECIMAL = 1073742044,
KP_HEXADECIMAL = 1073742045,
LCTRL = 1073742048,
LSHIFT = 1073742049,
LALT = 1073742050,
LGUI = 1073742051,
RCTRL = 1073742052,
RSHIFT = 1073742053,
RALT = 1073742054,
RGUI = 1073742055,
MODE = 1073742081,
AUDIONEXT = 1073742082,
AUDIOPREV = 1073742083,
AUDIOSTOP = 1073742084,
AUDIOPLAY = 1073742085,
AUDIOMUTE = 1073742086,
MEDIASELECT = 1073742087,
WWW = 1073742088,
MAIL = 1073742089,
CALCULATOR = 1073742090,
COMPUTER = 1073742091,
AC_SEARCH = 1073742092,
AC_HOME = 1073742093,
AC_BACK = 1073742094,
AC_FORWARD = 1073742095,
AC_STOP = 1073742096,
AC_REFRESH = 1073742097,
AC_BOOKMARKS = 1073742098,
BRIGHTNESSDOWN = 1073742099,
BRIGHTNESSUP = 1073742100,
DISPLAYSWITCH = 1073742101,
KBDILLUMTOGGLE = 1073742102,
KBDILLUMDOWN = 1073742103,
KBDILLUMUP = 1073742104,
EJECT = 1073742105,
SLEEP = 1073742106,
APP1 = 1073742107,
APP2 = 1073742108,
AUDIOREWIND = 1073742109,
AUDIOFASTFORWARD = 1073742110,
}
load_proc_address :: proc(lib_path: string, library: dynlib.Library, symbol: string, $ProcType: typeid) -> ProcType
where intrinsics.type_is_proc(ProcType)
{
if address, found := dynlib.symbol_address(library, symbol); found {
return transmute(ProcType)address;
} else {
log.warn("Could not find symbol", symbol, "in library", lib_path);
}
return nil;
}
try_load_plugin :: proc(lib_path: string) -> (plugin: Interface, success: bool) {
library, ok := dynlib.load_library(lib_path)
if !ok {
return {}, false;
}
interface := Interface {
on_initialize = load_proc_address(lib_path, library, "OnInitialize", OnInitializeProc),
on_exit = load_proc_address(lib_path, library, "OnExit", OnExitProc),
on_draw = load_proc_address(lib_path, library, "OnDraw", OnDrawProc),
};
if interface.on_initialize == nil do return interface, false;
if interface.on_exit == nil do return interface, false;
return interface, true
}

View File

@ -1,729 +0,0 @@
package ui
import "core:fmt"
import "core:strings"
import "core:math"
import "core:log"
import "vendor:sdl2"
import "../core"
import "../theme"
Context :: struct {
root: ^Box,
current_parent: ^Box,
persistent: map[string]^Box,
current_interaction_index: int,
clips: [dynamic]Rect,
renderer: ^sdl2.Renderer,
last_mouse_x: int,
last_mouse_y: int,
mouse_x: int,
mouse_y: int,
mouse_left_down: bool,
last_mouse_left_down: bool,
mouse_right_down: bool,
last_mouse_right_down: bool,
}
Rect :: struct {
pos: [2]int,
size: [2]int,
}
Key :: struct {
label: string,
value: int,
}
Interaction :: struct {
hovering: bool,
clicked: bool,
dragging: bool,
box_pos: [2]int,
box_size: [2]int,
}
Flag :: enum {
Clickable,
Hoverable,
Scrollable,
DrawText,
DrawBorder,
DrawBackground,
RoundedBorder,
Floating,
CustomDrawFunc,
}
SemanticSizeKind :: enum {
FitText = 0,
Exact,
ChildrenSum,
Fill,
PercentOfParent,
}
SemanticSize :: struct {
kind: SemanticSizeKind,
value: int,
}
Axis :: enum {
Horizontal = 0,
Vertical = 1,
}
CustomDrawFunc :: proc(state: ^core.State, box: ^Box, user_data: rawptr);
Box :: struct {
first: ^Box,
last: ^Box,
next: ^Box,
prev: ^Box,
parent: ^Box,
key: Key,
last_interacted_index: int,
flags: bit_set[Flag],
label: string,
axis: Axis,
semantic_size: [2]SemanticSize,
computed_size: [2]int,
computed_child_size: [2]int,
computed_pos: [2]int,
scroll_offset: int,
hot: int,
active: int,
custom_draw_func: CustomDrawFunc,
user_data: rawptr,
}
init :: proc(renderer: ^sdl2.Renderer) -> Context {
root := new(Box);
root.key = gen_key(nil, "root", 69);
root.label = strings.clone("root")
return Context {
root = root,
current_parent = root,
persistent = make(map[string]^Box),
clips = make([dynamic]Rect),
renderer = renderer,
};
}
gen_key :: proc(ctx: ^Context, label: string, value: int) -> Key {
key_label: string;
if ctx != nil && (ctx.current_parent == nil || len(ctx.current_parent.key.label) < 1) {
key_label = strings.clone(label);
} else if ctx != nil {
key_label = fmt.aprintf("%s:%s", ctx.current_parent.key.label, label);
} else {
key_label = strings.clone(label);
}
return Key {
label = key_label,
value = value,
};
}
@(private)
make_box :: proc(ctx: ^Context, key: Key, label: string, flags: bit_set[Flag], axis: Axis, semantic_size: [2]SemanticSize) -> ^Box {
box: ^Box = nil;
if cached_box, exists := ctx.persistent[key.label]; exists {
// NOTE(pcleavelin): its important to note that the _cached_ key _is not_ free'd
// as that would invalid the maps reference to the key causing memory leaks because
// the map would think that an entry doesn't exist (in some cases)
delete(key.label)
if cached_box.last_interacted_index < ctx.current_interaction_index-1 {
box = cached_box;
box.last_interacted_index = ctx.current_interaction_index;
box.hot = 0;
box.active = 0;
} else {
box = cached_box;
}
} else {
box = new(Box);
ctx.persistent[key.label] = box;
box.key = key;
box.last_interacted_index = ctx.current_interaction_index;
}
box.label = strings.clone(label, context.temp_allocator);
box.first = nil;
box.last = nil;
box.next = nil;
box.prev = ctx.current_parent.last;
box.parent = ctx.current_parent;
box.flags = flags;
box.axis = axis;
box.semantic_size = semantic_size;
if ctx.current_parent.last != nil {
ctx.current_parent.last.next = box;
}
if ctx.current_parent.first == nil {
ctx.current_parent.first = box;
}
ctx.current_parent.last = box;
return box;
}
make_semantic_size :: proc(kind: SemanticSizeKind, value: int = 0) -> SemanticSize {
return SemanticSize {
kind = kind,
value = value
};
}
FitText :[2]SemanticSize: {
SemanticSize {
kind = .FitText,
},
SemanticSize {
kind = .FitText,
}
};
ChildrenSum :[2]SemanticSize: {
SemanticSize {
kind = .ChildrenSum,
},
SemanticSize {
kind = .ChildrenSum,
}
};
Fill :[2]SemanticSize: {
SemanticSize {
kind = .Fill,
},
SemanticSize {
kind = .Fill,
}
};
push_box :: proc(ctx: ^Context, label: string, flags: bit_set[Flag], axis: Axis = .Horizontal, semantic_size: [2]SemanticSize = FitText) -> (^Box, Interaction) {
key := gen_key(ctx, label, 0);
box := make_box(ctx, key, label, flags, axis, semantic_size);
interaction := test_box(ctx, box);
return box, interaction;
}
push_parent :: proc(ctx: ^Context, box: ^Box) {
ctx.current_parent = box;
push_clip(ctx, box.computed_pos, box.computed_size, !(.Floating in box.flags));
}
pop_parent :: proc(ctx: ^Context) {
if ctx.current_parent.parent != nil {
ctx.current_parent = ctx.current_parent.parent;
}
pop_clip(ctx);
}
test_box :: proc(ctx: ^Context, box: ^Box) -> Interaction {
hovering: bool;
mouse_is_clicked := !ctx.last_mouse_left_down && ctx.mouse_left_down;
mouse_is_released := ctx.last_mouse_left_down && !ctx.mouse_left_down;
mouse_is_dragging := !mouse_is_clicked && ctx.mouse_left_down && (ctx.last_mouse_x != ctx.mouse_x || ctx.last_mouse_y != ctx.mouse_y);
if ctx.mouse_x >= box.computed_pos.x && ctx.mouse_x <= box.computed_pos.x + box.computed_size.x &&
ctx.mouse_y >= box.computed_pos.y && ctx.mouse_y <= box.computed_pos.y + box.computed_size.y
{
if len(ctx.clips) > 0 {
clip := ctx.clips[len(ctx.clips)-1];
if ctx.mouse_x >= clip.pos.x && ctx.mouse_x <= clip.pos.x + clip.size.x &&
ctx.mouse_y >= clip.pos.y && ctx.mouse_y <= clip.pos.y + clip.size.y
{
hovering = true;
} else {
hovering = false;
}
} else {
hovering = true;
}
}
if hovering || box.active > 0 {
box.hot += 1;
} else {
box.hot = 0;
}
if hovering && mouse_is_clicked && !mouse_is_dragging {
box.active = 1;
} else if hovering && mouse_is_dragging && box.active > 0 {
box.active += 1;
} else if !ctx.mouse_left_down && !mouse_is_released {
box.active = 0;
}
// TODO: change this to use the scroll wheel input
if .Scrollable in box.flags && box.active > 1 && ctx.mouse_left_down {
box.scroll_offset -= ctx.mouse_y - ctx.last_mouse_y;
box.scroll_offset = math.min(0, box.scroll_offset);
}
if box.hot > 0 || box.active > 0 {
box.last_interacted_index = ctx.current_interaction_index;
}
return Interaction {
hovering = .Hoverable in box.flags && (hovering || box.active > 0),
clicked = .Clickable in box.flags && (hovering && mouse_is_released && box.active > 0),
dragging = box.active > 1,
box_pos = box.computed_pos,
box_size = box.computed_size,
};
}
delete_box_children :: proc(ctx: ^Context, box: ^Box, keep_persistent: bool = true) {
iter := BoxIter { box.first, 0 };
for box in iterate_box(&iter) {
delete_box(ctx, box, keep_persistent);
}
}
delete_box :: proc(ctx: ^Context, box: ^Box, keep_persistent: bool = true) {
delete_box_children(ctx, box, keep_persistent);
if box.last_interacted_index < ctx.current_interaction_index-1 {
delete_key(&ctx.persistent, box.key.label)
}
if !(box.key.label in ctx.persistent) || !keep_persistent {
delete(box.key.label);
free(box);
}
}
prune :: proc(ctx: ^Context) {
iter := BoxIter { ctx.root.first, 0 };
for box in iterate_box(&iter) {
delete_box_children(ctx, box);
if !(box.key.label in ctx.persistent) && box != ctx.root {
free(box);
}
}
ctx.root.first = nil;
ctx.root.last = nil;
ctx.root.next = nil;
ctx.root.prev = nil;
ctx.root.parent = nil;
ctx.current_parent = ctx.root;
ctx.current_interaction_index += 1;
}
// TODO: consider not using `ctx` here
ancestor_size :: proc(ctx: ^Context, box: ^Box, axis: Axis) -> int {
if box == nil || box.parent == nil || .Floating in box.flags {
return ctx.root.computed_size[axis];
}
switch box.parent.semantic_size[axis].kind {
case .FitText: fallthrough
case .Exact: fallthrough
case .Fill: fallthrough
case .PercentOfParent:
return box.parent.computed_size[axis];
case .ChildrenSum:
return ancestor_size(ctx, box.parent, axis);
}
return 1337;
}
prev_non_floating_sibling :: proc(ctx: ^Context, box: ^Box) -> ^Box {
if box == nil {
return nil;
} else if box.prev == nil {
return nil;
} else if !(.Floating in box.prev.flags) {
return box.prev;
} else {
return prev_non_floating_sibling(ctx, box.prev);
}
}
compute_layout :: proc(ctx: ^Context, canvas_size: [2]int, font_width: int, font_height: int, box: ^Box) {
if box == nil { return; }
axis := Axis.Horizontal;
if box.parent != nil && !(.Floating in box.flags) {
axis = box.parent.axis;
box.computed_pos = box.parent.computed_pos;
box.computed_pos[axis] += box.parent.scroll_offset;
}
if .Floating in box.flags {
// box.computed_pos = {0,0};
} else if box.prev != nil {
prev := prev_non_floating_sibling(ctx, box);
if prev != nil {
box.computed_pos[axis] = prev.computed_pos[axis] + prev.computed_size[axis];
}
}
post_compute_size := [2]bool { false, false };
compute_children := true;
if box == ctx.root {
box.computed_size = canvas_size;
} else {
switch box.semantic_size.x.kind {
case .FitText: {
box.computed_size.x = len(box.label) * font_width;
}
case .Exact: {
box.computed_size.x = box.semantic_size.x.value;
}
case .ChildrenSum: {
post_compute_size[int(Axis.Horizontal)] = true;
}
case .Fill: {
if .Floating in box.flags {
box.computed_size.x = ancestor_size(ctx, box, .Horizontal);
}
}
case .PercentOfParent: {
box.computed_size.x = int(f32(ancestor_size(ctx, box, .Horizontal))*(f32(box.semantic_size.x.value)/100.0));
}
}
switch box.semantic_size.y.kind {
case .FitText: {
box.computed_size.y = font_height;
}
case .Exact: {
box.computed_size.y = box.semantic_size.y.value;
}
case .ChildrenSum: {
post_compute_size[Axis.Vertical] = true;
}
case .Fill: {
if .Floating in box.flags {
box.computed_size.y = ancestor_size(ctx, box, .Vertical);
}
}
case .PercentOfParent: {
box.computed_size.y = int(f32(ancestor_size(ctx, box, .Vertical))*(f32(box.semantic_size.y.value)/100.0));
}
}
}
if true || compute_children {
iter := BoxIter { box.first, 0 };
child_size: [2]int = {0,0};
// NOTE: the number of fills for the opposite axis of this box needs to be 1
// because it will never get incremented in the loop below and cause a divide by zero
// and the number of fills for the axis of the box needs to start at zero or else it will
// be n+1 causing incorrect sizes
number_of_fills: [2]int = {1,1};
number_of_fills[box.axis] = 0;
our_size := box.computed_size;
for axis in 0..<2 {
if box.semantic_size[axis].kind == .ChildrenSum {
if box.computed_size[axis] == 0 {
our_size[axis] = ancestor_size(ctx, box, Axis(axis))
} else {
our_size[axis] = box.computed_child_size[axis]
}
}
}
for child in iterate_box(&iter) {
if .Floating in child.flags { continue; }
compute_layout(ctx, canvas_size, font_width, font_height, child);
if child.semantic_size[box.axis].kind == .Fill {
number_of_fills[box.axis] += 1;
} else {
child_size[box.axis] += child.computed_size[box.axis];
}
}
iter = BoxIter { box.first, 0 };
for child in iterate_box(&iter) {
if !(.Floating in child.flags) {
for axis in 0..<2 {
if child.semantic_size[axis].kind == .Fill {
child.computed_size[axis] = (our_size[axis] - child_size[axis]) / number_of_fills[axis];
}
}
}
compute_layout(ctx, canvas_size, font_width, font_height, child);
}
}
{
box.computed_child_size[Axis.Horizontal] = 0;
iter := BoxIter { box.first, 0 };
for child in iterate_box(&iter) {
if .Floating in child.flags { continue; }
switch box.axis {
case .Horizontal: {
box.computed_child_size[Axis.Horizontal] += child.computed_size[Axis.Horizontal];
}
case .Vertical: {
if child.computed_size[Axis.Horizontal] > box.computed_child_size[Axis.Horizontal] {
box.computed_child_size[Axis.Horizontal] = child.computed_size[Axis.Horizontal];
}
}
}
}
}
if post_compute_size[Axis.Horizontal] {
box.computed_size[Axis.Horizontal] = box.computed_child_size[Axis.Horizontal];
}
{
box.computed_child_size[Axis.Vertical] = 0;
iter := BoxIter { box.first, 0 };
for child in iterate_box(&iter) {
if .Floating in child.flags { continue; }
switch box.axis {
case .Horizontal: {
if child.computed_size[Axis.Vertical] > box.computed_child_size[Axis.Vertical] {
box.computed_child_size[Axis.Vertical] = child.computed_size[Axis.Vertical];
}
}
case .Vertical: {
box.computed_child_size[Axis.Vertical] += child.computed_size[Axis.Vertical];
}
}
}
}
if post_compute_size[Axis.Vertical] {
box.computed_size[Axis.Vertical] = box.computed_child_size[Axis.Vertical];
}
}
push_clip :: proc(ctx: ^Context, pos: [2]int, size: [2]int, inside_parent: bool = true) {
rect := Rect { pos, size };
if len(ctx.clips) > 0 && inside_parent {
parent_rect := ctx.clips[len(ctx.clips)-1];
if rect.pos.x >= parent_rect.pos.x &&
rect.pos.y >= parent_rect.pos.y &&
rect.pos.x < parent_rect.pos.x + parent_rect.size.x &&
rect.pos.y < parent_rect.pos.y + parent_rect.size.y
{
//rect.pos.x = math.max(rect.pos.x, parent_rect.pos.x);
//rect.pos.y = math.max(rect.pos.y, parent_rect.pos.y);
rect.size.x = math.min(rect.pos.x + rect.size.x, parent_rect.pos.x + parent_rect.size.x);
rect.size.y = math.min(rect.pos.y + rect.size.y, parent_rect.pos.y + parent_rect.size.y);
rect.size.x -= rect.pos.x;
rect.size.y -= rect.pos.y;
} else {
rect = parent_rect;
}
}
// sdl2.RenderSetClipRect(ctx.renderer, &sdl2.Rect {
// i32(rect.pos.x),
// i32(rect.pos.y),
// i32(rect.size.x),
// i32(rect.size.y)
// });
append(&ctx.clips, rect);
}
pop_clip :: proc(ctx: ^Context) {
if len(ctx.clips) > 0 {
// rect := pop(&ctx.clips);
// sdl2.RenderSetClipRect(ctx.renderer, &sdl2.Rect {
// i32(rect.pos.x),
// i32(rect.pos.y),
// i32(rect.size.x),
// i32(rect.size.y)
// });
} else {
// sdl2.RenderSetClipRect(ctx.renderer, nil);
}
}
draw :: proc(ctx: ^Context, state: ^core.State, font_width: int, font_height: int, box: ^Box) {
if box == nil { return; }
push_clip(ctx, box.computed_pos, box.computed_size, !(.Floating in box.flags));
{
defer pop_clip(ctx);
if .Hoverable in box.flags && box.hot > 0 {
core.draw_rect_blend(
state,
box.computed_pos.x,
box.computed_pos.y,
box.computed_size.x,
box.computed_size.y,
.Background1,
.Background2,
f32(math.min(box.hot, 20))/20.0
);
} else if .DrawBackground in box.flags {
core.draw_rect(
state,
box.computed_pos.x,
box.computed_pos.y,
box.computed_size.x,
box.computed_size.y,
.Background1
);
}
if .DrawText in box.flags {
core.draw_text(state, box.label, box.computed_pos.x, box.computed_pos.y);
}
if .CustomDrawFunc in box.flags && box.custom_draw_func != nil {
box.custom_draw_func(state, box, box.user_data);
}
iter := BoxIter { box.first, 0 };
for child in iterate_box(&iter) {
draw(ctx, state, font_width, font_height, child);
}
}
push_clip(ctx, box.computed_pos, box.computed_size);
defer pop_clip(ctx);
if .DrawBorder in box.flags {
core.draw_rect_outline(
state,
box.computed_pos.x,
box.computed_pos.y,
box.computed_size.x,
box.computed_size.y,
.Background4
);
}
}
BoxIter :: struct {
box: ^Box,
index: int,
}
iterate_box :: proc(iter: ^BoxIter, print: bool = false) -> (box: ^Box, idx: int, cond: bool) {
if iter.box == nil {
return nil, iter.index, false;
}
box = iter.box;
idx = iter.index;
iter.box = iter.box.next;
iter.index += 1;
return box, iter.index, true;
}
debug_print :: proc(ctx: ^Context, box: ^Box, depth: int = 0) {
iter := BoxIter { box.first, 0 };
for box, idx in iterate_box(&iter, true) {
for _ in 0..<(depth*6) {
log.debug("-");
}
if depth > 0 {
log.debug(">");
}
log.debug(idx, "Box _", box.label, "#", box.key.label, "ptr", transmute(rawptr)box); //, "_ first", transmute(rawptr)box.first, "parent", transmute(rawptr)box.parent, box.computed_size);
debug_print(ctx, box, depth+1);
}
if depth == 0 {
log.debug("persistent");
for p in ctx.persistent {
log.debug(p);
}
}
}
spacer :: proc(ctx: ^Context, label: string, flags: bit_set[Flag] = {}, semantic_size: [2]SemanticSize = {{.Fill, 0}, {.Fill,0}}) -> Interaction {
box, interaction := push_box(ctx, label, flags, semantic_size = semantic_size);
return interaction;
}
push_floating :: proc(ctx: ^Context, label: string, pos: [2]int, flags: bit_set[Flag] = {.Floating}, axis: Axis = .Horizontal, semantic_size: [2]SemanticSize = Fill) -> (^Box, Interaction) {
box, interaction := push_box(ctx, label, flags, semantic_size = semantic_size);
box.computed_pos = pos;
return box, interaction;
}
push_rect :: proc(ctx: ^Context, label: string, background: bool = true, border: bool = true, axis: Axis = .Vertical, semantic_size: [2]SemanticSize = Fill) -> (^Box, Interaction) {
return push_box(ctx, label, {.DrawBackground if background else nil, .DrawBorder if border else nil}, axis, semantic_size = semantic_size);
}
label :: proc(ctx: ^Context, label: string) -> Interaction {
box, interaction := push_box(ctx, label, {.DrawText});
return interaction;
}
button :: proc(ctx: ^Context, label: string) -> Interaction {
return advanced_button(ctx, label);
}
advanced_button :: proc(ctx: ^Context, label: string, flags: bit_set[Flag] = {.Clickable, .Hoverable, .DrawText, .DrawBorder, .DrawBackground}, semantic_size: [2]SemanticSize = FitText) -> Interaction {
box, interaction := push_box(ctx, label, flags, semantic_size = semantic_size);
return interaction;
}
custom :: proc(ctx: ^Context, label: string, draw_func: CustomDrawFunc, user_data: rawptr) -> Interaction {
box, interaction := push_box(ctx, label, {.CustomDrawFunc}, semantic_size = { make_semantic_size(.Fill), make_semantic_size(.Fill) });
box.custom_draw_func = draw_func;
box.user_data = user_data;
return interaction;
}

View File

@ -13,6 +13,8 @@ State :: struct {
num_prev: int, num_prev: int,
curr_elements: []UI_Element, curr_elements: []UI_Element,
prev_elements: []UI_Element, prev_elements: []UI_Element,
max_size: [2]int,
} }
UI_Element :: struct { UI_Element :: struct {
@ -134,7 +136,11 @@ close_element :: proc(state: ^State, loc := #caller_location) -> UI_Layout {
child_index = child.next child_index = child.next
} }
} }
case Grow: { /* Done in the Grow pass */ } case Grow: {
if _, ok := e.parent.?; !ok {
e.layout.size = state.max_size
}
}
} }
switch v in e.layout.kind.y { switch v in e.layout.kind.y {
@ -186,10 +192,26 @@ close_element :: proc(state: ^State, loc := #caller_location) -> UI_Layout {
} }
} }
@(private)
non_fit_parent_size :: proc(state: ^State, index: int, axis: int) -> [2]int {
if _, ok := state.curr_elements[index].layout.kind[axis].(Fit); ok {
if parent_index, ok := state.curr_elements[index].parent.?; ok {
return non_fit_parent_size(state, parent_index, axis)
} else {
return state.max_size
}
} else {
return state.curr_elements[index].layout.size
}
}
@(private) @(private)
grow_children :: proc(state: ^State, index: int) { grow_children :: proc(state: ^State, index: int) {
e := &state.curr_elements[index] e := &state.curr_elements[index]
x_e := non_fit_parent_size(state, index, 0);
y_e := non_fit_parent_size(state, index, 1);
children_size: [2]int children_size: [2]int
num_growing: [2]int num_growing: [2]int
@ -220,7 +242,7 @@ grow_children :: proc(state: ^State, index: int) {
} }
if num_growing.x > 0 || num_growing.y > 0 { if num_growing.x > 0 || num_growing.y > 0 {
remaining_size := e.layout.size - children_size remaining_size := [2]int{ x_e.x, y_e.y } - children_size
to_grow: [2]int to_grow: [2]int
to_grow.x = 0 if num_growing.x < 1 else remaining_size.x/num_growing.x to_grow.x = 0 if num_growing.x < 1 else remaining_size.x/num_growing.x
to_grow.y = 0 if num_growing.y < 1 else remaining_size.y/num_growing.y to_grow.y = 0 if num_growing.y < 1 else remaining_size.y/num_growing.y
@ -318,7 +340,7 @@ compute_layout_2 :: proc(state: ^State) {
} }
} }
new_draw :: proc(state: ^State, core_state: ^core.State) { draw :: proc(state: ^State, core_state: ^core.State) {
for i in 0..<state.num_curr { for i in 0..<state.num_curr {
e := &state.curr_elements[i] e := &state.curr_elements[i]

50
todo.md
View File

@ -1,14 +1,32 @@
- Get input maps working with `spawn_floating_window` # Bugs
- Fix crash when cursor is over a new-line
- Fix jumping forward a word jumping past consecutive brackets
# Planned Features
- LSP Integration
- [ ] Language Server Configurations
- [ ] Diagnostics
- [ ] In-line errors
- [ ] Go-to Definition/
- [ ] Find references
- Vim-like Macro replays
- Re-implement lost features from Plugins
- [ ] Syntax Highlighting
- [ ] Integrate tree-sitter
- [ ] Bootleg Telescope
- [ ] Grepping Files
- [ ] Open Buffer Search
- Re-write the UI (again)
- [x] New UI
- [ ] Styling
- Save/Load files - Save/Load files
- Undo/Redo - Undo/Redo
- Edit History Tree - [ ] Edit History Tree
- [ ] Undo history saved to disk
- Finish selections - Finish selections
- [x] Guarantee that start and end are always ordered - [x] Guarantee that start and end are always ordered
- Add in text actions - Add in text actions
- Yank - [ ] Yank
- [x] Delete - [x] Delete
- [x] Change - [x] Change
- Virtual Whitespace - Virtual Whitespace
@ -17,24 +35,6 @@
- Vim's f and F movement commands - Vim's f and F movement commands
- Vim's r command - Vim's r command
- Command Search and Execution - Command Search and Execution
- Refactor to remove generics added specifically for plugins
- Palette based UI? - Palette based UI?
- [ ] Registering Plugin Commands that can be run in palette and via other plugins - Persist end of line cursor position
- [x] A way to query these commands by-plugin
- Re-write the UI (again)
- Separate ui components from layout
- Re-do plugin system
- Potentially have a C# plugins system? Use it instead of Lua? (probably not)
- Re-do dylib plugin system
- Re-do Lua plugin system
- Core Events ~Define ultra-core common API~
- ~3rd-party core UI modifications must abide by this API (for example, another plugin must be able to open a file and have it be focused)~
- e.g. Open File, currently focused UI panel recieves event
- Ability to probe core for installed plugins and their version
- Plugins can register to be UI components that the user can define where they are laid out
- Persist end of line cursor position
- Generate key mappings from the plugin.Key enum
- Fix jumping forward a word jumping past consecutive brackets
- Re-do highlighter
- Use tree-sitter?
- Purely run highlighter on glyph-buffer?
- Figure out someway to cache multi-line coloring (ex. quotes)