diff options
| author | hachem <im@hachem.wtf> | 2026-10-06 12:49:46 +0200 |
|---|---|---|
| committer | hachem <im@hachem.wtf> | 2026-10-06 12:49:46 +0200 |
| commit | 5d4a723a5f157c0ed38a348099a600d1d21c3c2b (patch) | |
| tree | 70e5fdf560a64cd0eb3a13d88ce046ddd29fd077 /gils/ed.rv | |
| parent | 52dab7fc85d877e80e3032f0afabae906adebad8 (diff) | |
feat: gils core utils and other shit
Diffstat (limited to 'gils/ed.rv')
| -rw-r--r-- | gils/ed.rv | 190 |
1 files changed, 190 insertions, 0 deletions
diff --git a/gils/ed.rv b/gils/ed.rv new file mode 100644 index 0000000..00ff50a --- /dev/null +++ b/gils/ed.rv @@ -0,0 +1,190 @@ +# simple ass modal ass line ass editor + +fn list_insert(items, index, value) do + let result = {} + for position in 0..index do result:push(items[position]) end + result:push(value) + for position in index..items:len() do result:push(items[position]) end + return result +end + +fn list_delete(items, low, high) do + let result = {} + for position in 0..items:len() do if (position < low or position > high) result:push(items[position]) end + return result +end + +fn ed_is_digit(character) do + const code = character:ascii() + return code >= 48 and code <= 57 +end + +# parse one address at index +fn ed_addr(state, line, index) do + if index >= line:len() return { value = -1, next = index } + if line[index] == "." return { value = state.dot, next = index + 1 } + if line[index] == "$" return { value = state.lines:len() - 1, next = index + 1 } + if ed_is_digit(line[index]) do + let number = 0 + let scan = index + while (scan < line:len() and ed_is_digit(line[scan])) do number = number * 10 + (line[scan]:ascii() - 48) scan += 1 end + return { value = number - 1, next = scan } + end + return { value = -1, next = index } +end + +# split a command line into low, high and command +fn ed_parse(state, line) do + let index = 0 + let low = -1 + let high = -1 + if (line:len() > 0 and (line[0] == "," or line[0] == "%")) do + low = 0 + high = state.lines:len() - 1 + index = 1 + end + else do + const first_addr = ed_addr(state, line, 0) + if first_addr.value >= 0 do low = first_addr.value index = first_addr.next end + if (index < line:len() and line[index] == ",") do + index += 1 + const second_addr = ed_addr(state, line, index) + if second_addr.value >= 0 do high = second_addr.value index = second_addr.next end + else do high = state.lines:len() - 1 end + end + end + if low < 0 do low = state.dot end + if high < 0 do high = low end + const command = if index < line:len() line[index..] else "" + return { low = low, high = high, command = command } +end + +fn ed_print(state, low, high, numbered) do + let index = low + while index <= high do + if (index >= 0 and index < state.lines:len()) do + if numbered do puts("#{index + 1}\t#{state.lines[index]}\n") end + else do puts("#{state.lines[index]}\n") end + end + index += 1 + end + if (high >= 0 and high < state.lines:len()) do state.dot = high end +end + +fn ed_goto(state, low) do + if state.lines:len() == 0 do puts("?\n") return :nil end + let target = low + if target < 0 do target = 0 end + if target >= state.lines:len() do target = state.lines:len() - 1 end + state.dot = target + puts("#{state.lines[target]}\n") +end + +fn ed_begin_input(state, position) do + let clamped = position + if clamped < 0 do clamped = 0 end + if clamped > state.lines:len() do clamped = state.lines:len() end + state.input = :true + state.insert_at = clamped +end + +fn ed_delete(state, low, high) do + state.lines = list_delete(state.lines, low, high) + state.dirty = :true + let new_dot = low + if new_dot >= state.lines:len() do new_dot = state.lines:len() - 1 end + if new_dot < 0 do new_dot = 0 end + state.dot = new_dot +end + +fn ed_change(state, low, high) do + ed_delete(state, low, high) + ed_begin_input(state, low) +end + +fn ed_join(items) do + let result = "" + for index in 0..items:len() do result = result ~ items[index] ~ "\n" end + return result +end + +fn ed_write(state, command) do + let path = state.path + if command:len() > 1 do + const filename = command[1..]:trim() + if filename:len() > 0 do path = abspath(filename) end + end + if path:len() == 0 do puts("?\n") return :nil end + const contents = ed_join(state.lines) + fs:store(path, contents) + state.path = path + state.dirty = :false + puts("#{contents:len()}\n") +end + +fn ed_quit(state) do + state.active = :false + puts("% ") +end + +global ed_state = { active = :false, input = :false, path = "", lines = {}, dot = 0, insert_at = 0, dirty = :false } + +fn gils_ed(context) do + ed_state.active = :true + ed_state.input = :false + ed_state.dirty = :false + ed_state.lines = {} + ed_state.path = if context.argv:len() > 1 abspath(context.argv[1]) else "" + if ed_state.path:len() > 0 do + const size = fs:size(ed_state.path) + if size > 0 do + const contents = fs:read_bytes(ed_state.path, 0, size) + const raw_lines = contents:split("\n") + for index in 0..raw_lines:len() do + if (index < raw_lines:len() - 1 or raw_lines[index]:len() > 0) do ed_state.lines:push(raw_lines[index]) end + end + puts("#{size}\n") + end + else do puts("#{ed_state.path}: new file\n") end + end + ed_state.dot = if ed_state.lines:len() > 0 ed_state.lines:len() - 1 else 0 + return :hold +end + +fn gils_ed_feed(line) do + const state = ed_state + if state.input do + if line == "." do state.input = :false end + else do + state.lines = list_insert(state.lines, state.insert_at, line) + state.insert_at += 1 + state.dot = state.insert_at - 1 + state.dirty = :true + end + return :nil + end + if line:len() == 0 do + if state.lines:len() == 0 do puts("?\n") return :nil end + if state.dot < state.lines:len() - 1 do state.dot += 1 end + puts("#{state.lines[state.dot]}\n") + return :nil + end + const parsed = ed_parse(state, line) + const command_char = if parsed.command:len() > 0 parsed.command[0] else "" + match command_char + | "p" => ed_print(state, parsed.low, parsed.high, :false) + | "n" => ed_print(state, parsed.low, parsed.high, :true) + | "a" => ed_begin_input(state, parsed.low + 1) + | "i" => ed_begin_input(state, parsed.low) + | "c" => ed_change(state, parsed.low, parsed.high) + | "d" => ed_delete(state, parsed.low, parsed.high) + | "w" => ed_write(state, parsed.command) + | "q" => ed_quit(state) + | "=" => puts("#{state.dot + 1}\n") + | "" => ed_goto(state, parsed.low) + | _ => puts("?\n") +end + +fn gils_ed_active() ed_state.active + +register("ed [path]", "gils", "the standard editor (a i d p w q)", gils_ed) |
