245 lines
6.1 KiB
Odin
245 lines
6.1 KiB
Odin
/*
|
|
- TODO implement movement up and down by paragraph
|
|
- TODO implement scrolling/viewport
|
|
- TODO implement deletion by word
|
|
- TODO implement selection
|
|
- TODO implement duplicate selection/line
|
|
- TODO implement move selection/line up/down
|
|
- TODO implement copy/cut/paste
|
|
- TODO implement search
|
|
- TODO implement file picker
|
|
- TODO implement "execute" command
|
|
*/
|
|
|
|
package main
|
|
|
|
import "core:fmt"
|
|
import "core:mem"
|
|
import "core:os"
|
|
import "core:strings"
|
|
import utf8 "core:unicode/utf8"
|
|
import r "vendor:raylib"
|
|
|
|
read_file :: proc(path: string) -> (string, bool) {
|
|
content: []u8
|
|
success: bool
|
|
content, success = os.read_entire_file(path)
|
|
return string(content), success
|
|
}
|
|
|
|
Cursor :: struct {
|
|
line: int,
|
|
char: int,
|
|
}
|
|
|
|
font_size: f32 = 20.0
|
|
text_height: f32
|
|
cursor: Cursor
|
|
lines: [dynamic][dynamic]u8
|
|
|
|
main :: proc() {
|
|
r.InitWindow(800, 600, "odit")
|
|
r.SetTargetFPS(60)
|
|
font := r.LoadFont("/Users/grant/Library/Fonts/BerkeleyMono-Regular.otf")
|
|
r.GuiSetFont(font)
|
|
two_lines_size := r.MeasureTextEx(font, "foo\nbar", font_size, 0.0)
|
|
text_height = two_lines_size[1] / 2
|
|
|
|
content, _success := read_file("src/main.odin")
|
|
|
|
for line in strings.split_lines_iterator(&content) {
|
|
bs := make([dynamic]u8, 0, len(line) == 0 ? 1 : len(line) * 2)
|
|
append(&bs, line)
|
|
append(&lines, bs)
|
|
}
|
|
|
|
text: string
|
|
dirty := true
|
|
for !r.WindowShouldClose() {
|
|
for c := r.GetCharPressed(); c != rune(0); c = r.GetCharPressed() {
|
|
chars, _ := utf8.encode_rune(c)
|
|
char := chars[0]
|
|
inject_at(&lines[cursor.line], cursor.char, char)
|
|
cursor.char += 1
|
|
}
|
|
|
|
if repeatable_key_pressed(r.KeyboardKey.RIGHT) {
|
|
preferred_position := cursor.char + 1
|
|
if cursor.char == len(current_line()) && cursor.line != len(lines) - 1 {
|
|
cursor.char = 0
|
|
cursor.line += 1
|
|
}
|
|
if r.IsKeyDown(r.KeyboardKey.LEFT_ALT) && cursor.char + 1 < len(current_line()) {
|
|
seen_space := false
|
|
for c, c_index in current_line()[cursor.char + 1:] {
|
|
if seen_space && !is_whitespace(c) {
|
|
preferred_position = cursor.char + c_index
|
|
break
|
|
}
|
|
if is_whitespace(c) {
|
|
seen_space = true
|
|
}
|
|
}
|
|
if !seen_space {
|
|
preferred_position = len(current_line())
|
|
}
|
|
}
|
|
cursor.char = min(preferred_position, len(current_line()))
|
|
}
|
|
|
|
if repeatable_key_pressed(r.KeyboardKey.LEFT) {
|
|
preferred_position := cursor.char - 1
|
|
if cursor.char == 0 && cursor.line != 0 {
|
|
cursor.line -= 1
|
|
cursor.char = len(current_line())
|
|
}
|
|
if r.IsKeyDown(r.KeyboardKey.LEFT_ALT) && 0 < cursor.char {
|
|
seen_space := false
|
|
#reverse for c, c_index in current_line()[:cursor.char - 1] {
|
|
if is_whitespace(c) {
|
|
seen_space = true
|
|
preferred_position = c_index + 1
|
|
break
|
|
}
|
|
}
|
|
if !seen_space {
|
|
preferred_position = 0
|
|
}
|
|
}
|
|
cursor.char = max(preferred_position, 0)
|
|
}
|
|
|
|
if repeatable_key_pressed(r.KeyboardKey.UP) {
|
|
preferred_line := cursor.line - 1
|
|
|
|
if r.IsKeyDown(r.KeyboardKey.LEFT_ALT) && 0 < cursor.line {
|
|
#reverse for l, l_index in lines[:cursor.line] {
|
|
if len(l) == 0 || all_whitespace(l[:]) {
|
|
preferred_line = l_index
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
cursor.line = max(0, preferred_line)
|
|
if len(current_line()) < cursor.char {
|
|
cursor.char = len(current_line())
|
|
}
|
|
}
|
|
|
|
if repeatable_key_pressed(r.KeyboardKey.DOWN) {
|
|
preferred_line := cursor.line + 1
|
|
|
|
if r.IsKeyDown(r.KeyboardKey.LEFT_ALT) && cursor.line + 1 < len(lines) {
|
|
for l, l_index in lines[cursor.line + 1:] {
|
|
if len(l) == 0 || all_whitespace(l[:]) {
|
|
preferred_line = cursor.line + l_index + 1
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
cursor.line = min(preferred_line, len(lines))
|
|
if len(current_line()) < cursor.char {
|
|
cursor.char = len(current_line())
|
|
}
|
|
}
|
|
|
|
if repeatable_key_pressed(r.KeyboardKey.ENTER) {
|
|
new_line_cap := len(current_line()) - cursor.char
|
|
bs := make([dynamic]u8, 0, len(current_line()) == 0 ? 1 : new_line_cap * 2)
|
|
if cursor.char < len(current_line()) {
|
|
for c, i in current_line()[cursor.char:] {
|
|
append(&bs, c)
|
|
}
|
|
}
|
|
inject_at(&lines, cursor.line + 1, bs)
|
|
resize(current_line(), cursor.char)
|
|
cursor.line += 1
|
|
cursor.char = 0
|
|
}
|
|
|
|
if r.IsKeyDown(r.KeyboardKey.LEFT_SUPER) && r.IsKeyPressed(r.KeyboardKey.S) {
|
|
builder := strings.builder_make()
|
|
defer strings.builder_destroy(&builder)
|
|
for line in lines {
|
|
strings.write_bytes(&builder, line[:])
|
|
strings.write_byte(&builder, '\n')
|
|
}
|
|
os.write_entire_file("foo.txt", transmute([]u8)strings.to_string(builder))
|
|
fmt.println("Wrote file!")
|
|
}
|
|
|
|
if repeatable_key_pressed(r.KeyboardKey.BACKSPACE) {
|
|
if cursor.line == 0 && cursor.char == 0 do continue
|
|
if cursor.char == 0 {
|
|
old_len := len(lines[cursor.line - 1])
|
|
append(&lines[cursor.line - 1], string(current_line()[:]))
|
|
line_to_remove := current_line()^
|
|
defer delete(line_to_remove)
|
|
ordered_remove(&lines, cursor.line)
|
|
cursor.line -= 1
|
|
cursor.char = old_len
|
|
} else {
|
|
ordered_remove(&lines[cursor.line], cursor.char - 1)
|
|
cursor.char -= 1
|
|
}
|
|
}
|
|
|
|
r.BeginDrawing()
|
|
r.ClearBackground(r.BLACK)
|
|
|
|
for &line, line_index in lines {
|
|
if cap(line) == 0 {
|
|
reserve(&line, 1)
|
|
}
|
|
if len(line) == cap(line) {
|
|
reserve(&line, len(line) * 2)
|
|
}
|
|
raw_data(line)[len(line)] = 0
|
|
|
|
cstr := strings.unsafe_string_to_cstring(string(line[:]))
|
|
pos := r.Vector2{0, font_size * f32(line_index)}
|
|
r.DrawTextEx(font, cstr, pos, font_size, 0, r.WHITE)
|
|
}
|
|
|
|
render_cursor(&cursor)
|
|
|
|
r.DrawFPS(10, 10)
|
|
|
|
r.EndDrawing()
|
|
|
|
free_all(context.temp_allocator)
|
|
dirty = false
|
|
}
|
|
}
|
|
|
|
render_cursor :: proc(cursor: ^Cursor) {
|
|
x := cursor.char * int(font_size / 2)
|
|
y := i32(font_size) * i32(cursor.line)
|
|
r.DrawLine(i32(x), y, i32(x), y + i32(text_height), r.WHITE)
|
|
r.DrawLine(i32(x) + 1, y, i32(x) + 1, y + i32(text_height), r.WHITE)
|
|
}
|
|
|
|
current_line :: proc() -> ^[dynamic]u8 {
|
|
return &lines[cursor.line]
|
|
}
|
|
|
|
is_whitespace :: proc(c: u8) -> bool {
|
|
return c == ' ' || c == '\t' || c == '\n'
|
|
}
|
|
|
|
all_whitespace :: proc(cs: []u8) -> bool {
|
|
for c in cs {
|
|
if !is_whitespace(c) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
repeatable_key_pressed :: proc(k: r.KeyboardKey) -> bool {
|
|
return r.IsKeyPressed(k) || r.IsKeyPressedRepeat(k)
|
|
}
|