View difference between Paste ID: 16G4HX0d and inkmF3nT
SHOW: | | - or go back to the newest paste.
1-
local component = require("component")
1+
local component = require("component")
2-
local event = require("event")
2+
local event = require("event")
3-
local fs = require("filesystem")
3+
local fs = require("filesystem")
4-
local keyboard = require("keyboard")
4+
local keyboard = require("keyboard")
5-
local shell = require("shell")
5+
local shell = require("shell")
6-
local term = require("term")
6+
local term = require("term")
7-
local text = require("text")
7+
local text = require("text")
8-
local unicode = require("unicode")
8+
local unicode = require("unicode")
9-
9+
10-
if not term.isAvailable() then
10+
if not term.isAvailable() then
11-
  return
11+
  return
12-
end
12+
end
13-
13+
14-
local args, options = shell.parse(...)
14+
local args, options = shell.parse(...)
15-
if #args == 0 then
15+
if #args == 0 then
16-
  io.write("Usage: edit <filename>")
16+
  io.write("Usage: edit <filename>")
17-
  return
17+
  return
18-
end
18+
end
19-
19+
20-
local filename = shell.resolve(args[1])
20+
local filename = shell.resolve(args[1])
21-
21+
22-
local readonly = options.r or fs.get(filename) == nil or fs.get(filename).isReadOnly()
22+
local readonly = options.r or fs.get(filename) == nil or fs.get(filename).isReadOnly()
23-
23+
24-
if not fs.exists(filename) then
24+
if not fs.exists(filename) then
25-
  if fs.isDirectory(filename) then
25+
  if fs.isDirectory(filename) then
26-
    io.stderr:write("file is a directory")
26+
    io.stderr:write("file is a directory")
27-
    return
27+
    return
28-
  elseif readonly then
28+
  elseif readonly then
29-
    io.stderr:write("file system is read only")
29+
    io.stderr:write("file system is read only")
30-
    return
30+
    return
31-
  end
31+
  end
32-
end
32+
end
33-
33+
34-
local function loadConfig()
34+
local function loadConfig()
35-
  -- Try to load user settings.
35+
  -- Try to load user settings.
36-
  local env = {}
36+
  local env = {}
37-
  local config = loadfile("/etc/edit.cfg", nil, env)
37+
  local config = loadfile("/etc/edit.cfg", nil, env)
38-
  if config then
38+
  if config then
39-
    pcall(config)
39+
    pcall(config)
40-
  end
40+
  end
41-
  -- Fill in defaults.
41+
  -- Fill in defaults.
42-
  env.keybinds = env.keybinds or {
42+
  env.keybinds = env.keybinds or {
43-
    left = {{"left"}},
43+
    left = {{"left"}},
44-
    right = {{"right"}},
44+
    right = {{"right"}},
45-
    up = {{"up"}},
45+
    up = {{"up"}},
46-
    down = {{"down"}},
46+
    down = {{"down"}},
47-
    home = {{"home"}},
47+
    home = {{"home"}},
48-
    eol = {{"end"}},
48+
    eol = {{"end"}},
49-
    pageUp = {{"pageUp"}},
49+
    pageUp = {{"pageUp"}},
50-
    pageDown = {{"pageDown"}},
50+
    pageDown = {{"pageDown"}},
51-
51+
52-
    backspace = {{"back"}},
52+
    backspace = {{"back"}},
53-
    delete = {{"delete"}},
53+
    delete = {{"delete"}},
54-
    deleteLine = {{"control", "delete"}, {"shift", "delete"}},
54+
    deleteLine = {{"control", "delete"}, {"shift", "delete"}},
55-
    newline = {{"enter"}},
55+
    newline = {{"enter"}},
56-
56+
57-
    save = {{"control", "s"}},
57+
    save = {{"control", "s"}},
58-
    close = {{"control", "w"}},
58+
    close = {{"control", "w"}},
59-
    find = {{"control", "f"}},
59+
    find = {{"control", "f"}},
60-
    findnext = {{"control", "g"}, {"control", "n"}, {"f3"}}
60+
    findnext = {{"control", "g"}, {"control", "n"}, {"f3"}}
61-
  }
61+
  }
62-
  -- Generate config file if it didn't exist.
62+
  -- Generate config file if it didn't exist.
63-
  if not config then
63+
  if not config then
64-
    local root = fs.get("/")
64+
    local root = fs.get("/")
65-
    if root and not root.isReadOnly() then
65+
    if root and not root.isReadOnly() then
66-
      fs.makeDirectory("/etc")
66+
      fs.makeDirectory("/etc")
67-
      local f = io.open("/etc/edit.cfg", "w")
67+
      local f = io.open("/etc/edit.cfg", "w")
68-
      if f then
68+
      if f then
69-
        local serialization = require("serialization")
69+
        local serialization = require("serialization")
70-
        for k, v in pairs(env) do
70+
        for k, v in pairs(env) do
71-
          f:write(k.."="..tostring(serialization.serialize(v, math.huge)).."\n")
71+
          f:write(k.."="..tostring(serialization.serialize(v, math.huge)).."\n")
72-
        end
72+
        end
73-
        f:close()
73+
        f:close()
74-
      end
74+
      end
75-
    end
75+
    end
76-
  end
76+
  end
77-
  return env
77+
  return env
78-
end
78+
end
79-
79+
80-
term.clear()
80+
term.clear()
81-
term.setCursorBlink(true)
81+
term.setCursorBlink(true)
82-
82+
83-
local running = true
83+
local running = true
84-
local buffer = {}
84+
local buffer = {}
85-
local scrollX, scrollY = 0, 0
85+
local scrollX, scrollY = 0, 0
86-
local config = loadConfig()
86+
local config = loadConfig()
87-
87+
88-
local getKeyBindHandler -- forward declaration for refind()
88+
local getKeyBindHandler -- forward declaration for refind()
89-
89+
90-
local function helpStatusText()
90+
local function helpStatusText()
91-
  local function prettifyKeybind(label, command)
91+
  local function prettifyKeybind(label, command)
92-
    local keybind = type(config.keybinds) == "table" and config.keybinds[command]
92+
    local keybind = type(config.keybinds) == "table" and config.keybinds[command]
93-
    if type(keybind) ~= "table" or type(keybind[1]) ~= "table" then return "" end
93+
    if type(keybind) ~= "table" or type(keybind[1]) ~= "table" then return "" end
94-
    local alt, control, shift, key
94+
    local alt, control, shift, key
95-
    for _, value in ipairs(keybind[1]) do
95+
    for _, value in ipairs(keybind[1]) do
96-
      if value == "alt" then alt = true
96+
      if value == "alt" then alt = true
97-
      elseif value == "control" then control = true
97+
      elseif value == "control" then control = true
98-
      elseif value == "shift" then shift = true
98+
      elseif value == "shift" then shift = true
99-
      else key = value end
99+
      else key = value end
100-
    end
100+
    end
101-
    if not key then return "" end
101+
    if not key then return "" end
102-
    return label .. ": [" ..
102+
    return label .. ": [" ..
103-
           (control and "Ctrl+" or "") ..
103+
           (control and "Ctrl+" or "") ..
104-
           (alt and "Alt+" or "") ..
104+
           (alt and "Alt+" or "") ..
105-
           (shift and "Shift+" or "") ..
105+
           (shift and "Shift+" or "") ..
106-
           unicode.upper(key) ..
106+
           unicode.upper(key) ..
107-
           "] "
107+
           "] "
108-
  end
108+
  end
109-
  return prettifyKeybind("Save", "save") ..
109+
  return prettifyKeybind("Save", "save") ..
110-
         prettifyKeybind("Close", "close") ..
110+
         prettifyKeybind("Close", "close") ..
111-
         prettifyKeybind("Find", "find")
111+
         prettifyKeybind("Find", "find")
112-
end
112+
end
113-
113+
114-
-------------------------------------------------------------------------------
114+
-------------------------------------------------------------------------------
115-
115+
116-
local function setStatus(value)
116+
local function setStatus(value)
117-
  local w, h = component.gpu.getResolution()
117+
  local w, h = component.gpu.getResolution()
118-
  component.gpu.set(1, h, text.padRight(unicode.sub(value, 1, w - 10), w - 10))
118+
  component.gpu.set(1, h, text.padRight(unicode.sub(value, 1, w - 10), w - 10))
119-
end
119+
end
120-
120+
121-
local function getSize()
121+
local function getSize()
122-
  local w, h = component.gpu.getResolution()
122+
  local w, h = component.gpu.getResolution()
123-
  return w, h - 1
123+
  return w, h - 1
124-
end
124+
end
125-
125+
126-
local function getCursor()
126+
local function getCursor()
127-
  local cx, cy = term.getCursor()
127+
  local cx, cy = term.getCursor()
128-
  return cx + scrollX, cy + scrollY
128+
  return cx + scrollX, cy + scrollY
129-
end
129+
end
130-
130+
131-
local function line()
131+
local function line()
132-
  local cbx, cby = getCursor()
132+
  local cbx, cby = getCursor()
133-
  return buffer[cby]
133+
  return buffer[cby]
134-
end
134+
end
135-
135+
136-
local function setCursor(nbx, nby)
136+
local function setCursor(nbx, nby)
137-
  local w, h = getSize()
137+
  local w, h = getSize()
138-
  nby = math.max(1, math.min(#buffer, nby))
138+
  nby = math.max(1, math.min(#buffer, nby))
139-
139+
140-
  local ncy = nby - scrollY
140+
  local ncy = nby - scrollY
141-
  if ncy > h then
141+
  if ncy > h then
142-
    term.setCursorBlink(false)
142+
    term.setCursorBlink(false)
143-
    local sy = nby - h
143+
    local sy = nby - h
144-
    local dy = math.abs(scrollY - sy)
144+
    local dy = math.abs(scrollY - sy)
145-
    scrollY = sy
145+
    scrollY = sy
146-
    component.gpu.copy(1, 1 + dy, w, h - dy, 0, -dy)
146+
    component.gpu.copy(1, 1 + dy, w, h - dy, 0, -dy)
147-
    for by = nby - (dy - 1), nby do
147+
    for by = nby - (dy - 1), nby do
148-
      local str = text.padRight(unicode.sub(buffer[by], 1 + scrollX), w)
148+
      local str = text.padRight(unicode.sub(buffer[by], 1 + scrollX), w)
149-
      component.gpu.set(1, by - scrollY, str)
149+
      component.gpu.set(1, by - scrollY, str)
150-
    end
150+
    end
151-
  elseif ncy < 1 then
151+
  elseif ncy < 1 then
152-
    term.setCursorBlink(false)
152+
    term.setCursorBlink(false)
153-
    local sy = nby - 1
153+
    local sy = nby - 1
154-
    local dy = math.abs(scrollY - sy)
154+
    local dy = math.abs(scrollY - sy)
155-
    scrollY = sy
155+
    scrollY = sy
156-
    component.gpu.copy(1, 1, w, h - dy, 0, dy)
156+
    component.gpu.copy(1, 1, w, h - dy, 0, dy)
157-
    for by = nby, nby + (dy - 1) do
157+
    for by = nby, nby + (dy - 1) do
158-
      local str = text.padRight(unicode.sub(buffer[by], 1 + scrollX), w)
158+
      local str = text.padRight(unicode.sub(buffer[by], 1 + scrollX), w)
159-
      component.gpu.set(1, by - scrollY, str)
159+
      component.gpu.set(1, by - scrollY, str)
160-
    end
160+
    end
161-
  end
161+
  end
162-
  term.setCursor(term.getCursor(), nby - scrollY)
162+
  term.setCursor(term.getCursor(), nby - scrollY)
163-
163+
164-
  nbx = math.max(1, math.min(unicode.len(line()) + 1, nbx))
164+
  nbx = math.max(1, math.min(unicode.len(line()) + 1, nbx))
165-
  local ncx = nbx - scrollX
165+
  local ncx = nbx - scrollX
166-
  if ncx > w then
166+
  if ncx > w then
167-
    term.setCursorBlink(false)
167+
    term.setCursorBlink(false)
168-
    local sx = nbx - w
168+
    local sx = nbx - w
169-
    local dx = math.abs(scrollX - sx)
169+
    local dx = math.abs(scrollX - sx)
170-
    scrollX = sx
170+
    scrollX = sx
171-
    component.gpu.copy(1 + dx, 1, w - dx, h, -dx, 0)
171+
    component.gpu.copy(1 + dx, 1, w - dx, h, -dx, 0)
172-
    for by = 1 + scrollY, math.min(h + scrollY, #buffer) do
172+
    for by = 1 + scrollY, math.min(h + scrollY, #buffer) do
173-
      local str = unicode.sub(buffer[by], nbx - (dx - 1), nbx)
173+
      local str = unicode.sub(buffer[by], nbx - (dx - 1), nbx)
174-
      str = text.padRight(str, dx)
174+
      str = text.padRight(str, dx)
175-
      component.gpu.set(1 + (w - dx), by - scrollY, str)
175+
      component.gpu.set(1 + (w - dx), by - scrollY, str)
176-
    end
176+
    end
177-
  elseif ncx < 1 then
177+
  elseif ncx < 1 then
178-
    term.setCursorBlink(false)
178+
    term.setCursorBlink(false)
179-
    local sx = nbx - 1
179+
    local sx = nbx - 1
180-
    local dx = math.abs(scrollX - sx)
180+
    local dx = math.abs(scrollX - sx)
181-
    scrollX = sx
181+
    scrollX = sx
182-
    component.gpu.copy(1, 1, w - dx, h, dx, 0)
182+
    component.gpu.copy(1, 1, w - dx, h, dx, 0)
183-
    for by = 1 + scrollY, math.min(h + scrollY, #buffer) do
183+
    for by = 1 + scrollY, math.min(h + scrollY, #buffer) do
184-
      local str = unicode.sub(buffer[by], nbx, nbx + dx)
184+
      local str = unicode.sub(buffer[by], nbx, nbx + dx)
185-
      --str = text.padRight(str, dx)
185+
      --str = text.padRight(str, dx)
186-
      component.gpu.set(1, by - scrollY, str)
186+
      component.gpu.set(1, by - scrollY, str)
187-
    end
187+
    end
188-
  end
188+
  end
189-
  term.setCursor(nbx - scrollX, nby - scrollY)
189+
  term.setCursor(nbx - scrollX, nby - scrollY)
190-
190+
191-
  component.gpu.set(w - 9, h + 1, text.padLeft(string.format("%d,%d", nby, nbx), 10))
191+
  component.gpu.set(w - 9, h + 1, text.padLeft(string.format("%d,%d", nby, nbx), 10))
192-
end
192+
end
193-
193+
194-
local function highlight(bx, by, length, enabled)
194+
local function highlight(bx, by, length, enabled)
195-
  local w, h = getSize()
195+
  local w, h = getSize()
196-
  local cx, cy = bx - scrollX, by - scrollY
196+
  local cx, cy = bx - scrollX, by - scrollY
197-
  cx = math.max(1, math.min(w, cx))
197+
  cx = math.max(1, math.min(w, cx))
198-
  cy = math.max(1, math.min(h, cy))
198+
  cy = math.max(1, math.min(h, cy))
199-
  length = math.max(1, math.min(w - cx, length))
199+
  length = math.max(1, math.min(w - cx, length))
200-
200+
201-
  local fg, fgp = component.gpu.getForeground()
201+
  local fg, fgp = component.gpu.getForeground()
202-
  local bg, bgp = component.gpu.getBackground()
202+
  local bg, bgp = component.gpu.getBackground()
203-
  if enabled then
203+
  if enabled then
204-
    component.gpu.setForeground(bg, bgp)
204+
    component.gpu.setForeground(bg, bgp)
205-
    component.gpu.setBackground(fg, fgp)
205+
    component.gpu.setBackground(fg, fgp)
206-
  end
206+
  end
207-
  local value = ""
207+
  local value = ""
208-
  for x = cx, cx + length - 1 do
208+
  for x = cx, cx + length - 1 do
209-
    value = value .. component.gpu.get(x, cy)
209+
    value = value .. component.gpu.get(x, cy)
210-
  end
210+
  end
211-
  component.gpu.set(cx, cy, value)
211+
  component.gpu.set(cx, cy, value)
212-
  if enabled then
212+
  if enabled then
213-
    component.gpu.setForeground(fg, fgp)
213+
    component.gpu.setForeground(fg, fgp)
214-
    component.gpu.setBackground(bg, bgp)
214+
    component.gpu.setBackground(bg, bgp)
215-
  end
215+
  end
216-
end
216+
end
217-
217+
218-
local function home()
218+
local function home()
219-
  local cbx, cby = getCursor()
219+
  local cbx, cby = getCursor()
220-
  setCursor(1, cby)
220+
  setCursor(1, cby)
221-
end
221+
end
222-
222+
223-
local function ende()
223+
local function ende()
224-
  local cbx, cby = getCursor()
224+
  local cbx, cby = getCursor()
225-
  setCursor(unicode.len(line()) + 1, cby)
225+
  setCursor(unicode.len(line()) + 1, cby)
226-
end
226+
end
227-
227+
228-
local function left()
228+
local function left()
229-
  local cbx, cby = getCursor()
229+
  local cbx, cby = getCursor()
230-
  if cbx > 1 then
230+
  if cbx > 1 then
231-
    setCursor(cbx - 1, cby)
231+
    setCursor(cbx - 1, cby)
232-
    return true -- for backspace
232+
    return true -- for backspace
233-
  elseif cby > 1 then
233+
  elseif cby > 1 then
234-
    setCursor(cbx, cby - 1)
234+
    setCursor(cbx, cby - 1)
235-
    ende()
235+
    ende()
236-
    return true -- again, for backspace
236+
    return true -- again, for backspace
237-
  end
237+
  end
238-
end
238+
end
239-
239+
240-
local function right(n)
240+
local function right(n)
241-
  n = n or 1
241+
  n = n or 1
242-
  local cbx, cby = getCursor()
242+
  local cbx, cby = getCursor()
243-
  local be = unicode.len(line()) + 1
243+
  local be = unicode.len(line()) + 1
244-
  if cbx < be then
244+
  if cbx < be then
245-
    setCursor(cbx + n, cby)
245+
    setCursor(cbx + n, cby)
246-
  elseif cby < #buffer then
246+
  elseif cby < #buffer then
247-
    setCursor(1, cby + 1)
247+
    setCursor(1, cby + 1)
248-
  end
248+
  end
249-
end
249+
end
250-
250+
251-
local function up(n)
251+
local function up(n)
252-
  n = n or 1
252+
  n = n or 1
253-
  local cbx, cby = getCursor()
253+
  local cbx, cby = getCursor()
254-
  if cby > 1 then
254+
  if cby > 1 then
255-
    setCursor(cbx, cby - n)
255+
    setCursor(cbx, cby - n)
256-
    if getCursor() > unicode.len(line()) then
256+
    if getCursor() > unicode.len(line()) then
257-
      ende()
257+
      ende()
258-
    end
258+
    end
259-
  end
259+
  end
260-
end
260+
end
261-
261+
262-
local function down(n)
262+
local function down(n)
263-
  n = n or 1
263+
  n = n or 1
264-
  local cbx, cby = getCursor()
264+
  local cbx, cby = getCursor()
265-
  if cby < #buffer then
265+
  if cby < #buffer then
266-
    setCursor(cbx, cby + n)
266+
    setCursor(cbx, cby + n)
267-
    if getCursor() > unicode.len(line()) then
267+
    if getCursor() > unicode.len(line()) then
268-
      ende()
268+
      ende()
269-
    end
269+
    end
270-
  end
270+
  end
271-
end
271+
end
272-
272+
273-
local function delete(fullRow)
273+
local function delete(fullRow)
274-
  local cx, cy = term.getCursor()
274+
  local cx, cy = term.getCursor()
275-
  local cbx, cby = getCursor()
275+
  local cbx, cby = getCursor()
276-
  local w, h = getSize()
276+
  local w, h = getSize()
277-
  local function deleteRow(row)
277+
  local function deleteRow(row)
278-
    local content = table.remove(buffer, row)
278+
    local content = table.remove(buffer, row)
279-
    local rcy = cy + (row - cby)
279+
    local rcy = cy + (row - cby)
280-
    if rcy <= h then
280+
    if rcy <= h then
281-
      component.gpu.copy(1, rcy + 1, w, h - rcy, 0, -1)
281+
      component.gpu.copy(1, rcy + 1, w, h - rcy, 0, -1)
282-
      component.gpu.set(1, h, text.padRight(buffer[row + (h - rcy)], w))
282+
      component.gpu.set(1, h, text.padRight(buffer[row + (h - rcy)], w))
283-
    end
283+
    end
284-
    return content
284+
    return content
285-
  end
285+
  end
286-
  if fullRow then
286+
  if fullRow then
287-
    term.setCursorBlink(false)
287+
    term.setCursorBlink(false)
288-
    if #buffer > 1 then
288+
    if #buffer > 1 then
289-
      deleteRow(cby)
289+
      deleteRow(cby)
290-
    else
290+
    else
291-
      buffer[cby] = ""
291+
      buffer[cby] = ""
292-
      component.gpu.fill(1, cy, w, 1, " ")
292+
      component.gpu.fill(1, cy, w, 1, " ")
293-
    end
293+
    end
294-
    setCursor(1, cby)
294+
    setCursor(1, cby)
295-
  elseif cbx <= unicode.len(line()) then
295+
  elseif cbx <= unicode.len(line()) then
296-
    term.setCursorBlink(false)
296+
    term.setCursorBlink(false)
297-
    buffer[cby] = unicode.sub(line(), 1, cbx - 1) ..
297+
    buffer[cby] = unicode.sub(line(), 1, cbx - 1) ..
298-
                  unicode.sub(line(), cbx + 1)
298+
                  unicode.sub(line(), cbx + 1)
299-
    component.gpu.copy(cx + 1, cy, w - cx, 1, -1, 0)
299+
    component.gpu.copy(cx + 1, cy, w - cx, 1, -1, 0)
300-
    local br = cbx + (w - cx)
300+
    local br = cbx + (w - cx)
301-
    local char = unicode.sub(line(), br, br)
301+
    local char = unicode.sub(line(), br, br)
302-
    if not char or unicode.len(char) == 0 then
302+
    if not char or unicode.len(char) == 0 then
303-
      char = " "
303+
      char = " "
304-
    end
304+
    end
305-
    component.gpu.set(w, cy, char)
305+
    component.gpu.set(w, cy, char)
306-
  elseif cby < #buffer then
306+
  elseif cby < #buffer then
307-
    term.setCursorBlink(false)
307+
    term.setCursorBlink(false)
308-
    local append = deleteRow(cby + 1)
308+
    local append = deleteRow(cby + 1)
309-
    buffer[cby] = buffer[cby] .. append
309+
    buffer[cby] = buffer[cby] .. append
310-
    component.gpu.set(cx, cy, append)
310+
    component.gpu.set(cx, cy, append)
311-
  else
311+
  else
312-
    return
312+
    return
313-
  end
313+
  end
314-
  setStatus(helpStatusText())
314+
  setStatus(helpStatusText())
315-
end
315+
end
316-
316+
317-
local function insert(value)
317+
local function insert(value)
318-
  if not value or unicode.len(value) < 1 then
318+
  if not value or unicode.len(value) < 1 then
319-
    return
319+
    return
320-
  end
320+
  end
321-
  term.setCursorBlink(false)
321+
  term.setCursorBlink(false)
322-
  local cx, cy = term.getCursor()
322+
  local cx, cy = term.getCursor()
323-
  local cbx, cby = getCursor()
323+
  local cbx, cby = getCursor()
324-
  local w, h = getSize()
324+
  local w, h = getSize()
325-
  buffer[cby] = unicode.sub(line(), 1, cbx - 1) ..
325+
  buffer[cby] = unicode.sub(line(), 1, cbx - 1) ..
326-
                value ..
326+
                value ..
327-
                unicode.sub(line(), cbx)
327+
                unicode.sub(line(), cbx)
328-
  local len = unicode.len(value)
328+
  local len = unicode.len(value)
329-
  local n = w - (cx - 1) - len
329+
  local n = w - (cx - 1) - len
330-
  if n > 0 then
330+
  if n > 0 then
331-
    component.gpu.copy(cx, cy, n, 1, len, 0)
331+
    component.gpu.copy(cx, cy, n, 1, len, 0)
332-
  end
332+
  end
333-
  component.gpu.set(cx, cy, value)
333+
  component.gpu.set(cx, cy, value)
334-
  right(len)
334+
  right(len)
335-
  setStatus(helpStatusText())
335+
  setStatus(helpStatusText())
336-
end
336+
end
337-
337+
338-
local function enter()
338+
local function enter()
339-
  term.setCursorBlink(false)
339+
  term.setCursorBlink(false)
340-
  local cx, cy = term.getCursor()
340+
  local cx, cy = term.getCursor()
341-
  local cbx, cby = getCursor()
341+
  local cbx, cby = getCursor()
342-
  local w, h = getSize()
342+
  local w, h = getSize()
343-
  table.insert(buffer, cby + 1, unicode.sub(buffer[cby], cbx))
343+
  table.insert(buffer, cby + 1, unicode.sub(buffer[cby], cbx))
344-
  buffer[cby] = unicode.sub(buffer[cby], 1, cbx - 1)
344+
  buffer[cby] = unicode.sub(buffer[cby], 1, cbx - 1)
345-
  component.gpu.fill(cx, cy, w - (cx - 1), 1, " ")
345+
  component.gpu.fill(cx, cy, w - (cx - 1), 1, " ")
346-
  if cy < h then
346+
  if cy < h then
347-
    if cy < h - 1 then
347+
    if cy < h - 1 then
348-
      component.gpu.copy(1, cy + 1, w, h - (cy + 1), 0, 1)
348+
      component.gpu.copy(1, cy + 1, w, h - (cy + 1), 0, 1)
349-
    end
349+
    end
350-
    component.gpu.set(1, cy + 1, text.padRight(buffer[cby + 1], w))
350+
    component.gpu.set(1, cy + 1, text.padRight(buffer[cby + 1], w))
351-
  end
351+
  end
352-
  setCursor(1, cby + 1)
352+
  setCursor(1, cby + 1)
353-
  setStatus(helpStatusText())
353+
  setStatus(helpStatusText())
354-
end
354+
end
355-
355+
356-
local findText = ""
356+
local findText = ""
357-
357+
358-
local function find()
358+
local function find()
359-
  local w, h = getSize()
359+
  local w, h = getSize()
360-
  local cx, cy = term.getCursor()
360+
  local cx, cy = term.getCursor()
361-
  local cbx, cby = getCursor()
361+
  local cbx, cby = getCursor()
362-
  local ibx, iby = cbx, cby
362+
  local ibx, iby = cbx, cby
363-
  while running do
363+
  while running do
364-
    if unicode.len(findText) > 0 then
364+
    if unicode.len(findText) > 0 then
365-
      local sx, sy
365+
      local sx, sy
366-
      for syo = 1, #buffer do -- iterate lines with wraparound
366+
      for syo = 1, #buffer do -- iterate lines with wraparound
367-
        sy = (iby + syo - 1 + #buffer - 1) % #buffer + 1
367+
        sy = (iby + syo - 1 + #buffer - 1) % #buffer + 1
368-
        sx = string.find(buffer[sy], findText, syo == 1 and ibx or 1, true)
368+
        sx = string.find(buffer[sy], findText, syo == 1 and ibx or 1, true)
369-
        if sx and (sx >= ibx or syo > 1) then
369+
        if sx and (sx >= ibx or syo > 1) then
370-
          break
370+
          break
371-
        end
371+
        end
372-
      end
372+
      end
373-
      if not sx then -- special case for single matches
373+
      if not sx then -- special case for single matches
374-
        sy = iby
374+
        sy = iby
375-
        sx = string.find(buffer[sy], findText, nil, true)
375+
        sx = string.find(buffer[sy], findText, nil, true)
376-
      end
376+
      end
377-
      if sx then
377+
      if sx then
378-
        cbx, cby = sx, sy
378+
        cbx, cby = sx, sy
379-
        setCursor(cbx, cby)
379+
        setCursor(cbx, cby)
380-
        highlight(cbx, cby, unicode.len(findText), true)
380+
        highlight(cbx, cby, unicode.len(findText), true)
381-
      end
381+
      end
382-
    end
382+
    end
383-
    term.setCursor(7 + unicode.len(findText), h + 1)
383+
    term.setCursor(7 + unicode.len(findText), h + 1)
384-
    setStatus("Find: " .. findText)
384+
    setStatus("Find: " .. findText)
385-
385+
386-
    local _, _, char, code = event.pull("key_down")
386+
    local _, _, char, code = event.pull("key_down")
387-
    local handler, name = getKeyBindHandler(code)
387+
    local handler, name = getKeyBindHandler(code)
388-
    highlight(cbx, cby, unicode.len(findText), false)
388+
    highlight(cbx, cby, unicode.len(findText), false)
389-
    if name == "newline" then
389+
    if name == "newline" then
390-
      break
390+
      break
391-
    elseif name == "close" then
391+
    elseif name == "close" then
392-
      handler()
392+
      handler()
393-
    elseif name == "backspace" then
393+
    elseif name == "backspace" then
394-
      findText = unicode.sub(findText, 1, -2)
394+
      findText = unicode.sub(findText, 1, -2)
395-
    elseif name == "find" or name == "findnext" then
395+
    elseif name == "find" or name == "findnext" then
396-
      ibx = cbx + 1
396+
      ibx = cbx + 1
397-
      iby = cby
397+
      iby = cby
398-
    elseif not keyboard.isControl(char) then
398+
    elseif not keyboard.isControl(char) then
399-
      findText = findText .. unicode.char(char)
399+
      findText = findText .. unicode.char(char)
400-
    end
400+
    end
401-
  end
401+
  end
402-
  setCursor(cbx, cby)
402+
  setCursor(cbx, cby)
403-
  setStatus(helpStatusText())
403+
  setStatus(helpStatusText())
404-
end
404+
end
405-
405+
406-
-------------------------------------------------------------------------------
406+
-------------------------------------------------------------------------------
407-
407+
408-
local keyBindHandlers = {
408+
local keyBindHandlers = {
409-
  left = left,
409+
  left = left,
410-
  right = right,
410+
  right = right,
411-
  up = up,
411+
  up = up,
412-
  down = down,
412+
  down = down,
413-
  home = home,
413+
  home = home,
414-
  eol = ende,
414+
  eol = ende,
415-
  pageUp = function()
415+
  pageUp = function()
416-
    local w, h = getSize()
416+
    local w, h = getSize()
417-
    up(h - 1)
417+
    up(h - 1)
418-
  end,
418+
  end,
419-
  pageDown = function()
419+
  pageDown = function()
420-
    local w, h = getSize()
420+
    local w, h = getSize()
421-
    down(h - 1)
421+
    down(h - 1)
422-
  end,
422+
  end,
423-
423+
424-
  backspace = function()
424+
  backspace = function()
425-
    if not readonly and left() then
425+
    if not readonly and left() then
426-
      delete()
426+
      delete()
427-
    end
427+
    end
428-
  end,
428+
  end,
429-
  delete = function()
429+
  delete = function()
430-
    if not readonly then
430+
    if not readonly then
431-
      delete()
431+
      delete()
432-
    end
432+
    end
433-
  end,
433+
  end,
434-
  deleteLine = function()
434+
  deleteLine = function()
435-
    if not readonly then
435+
    if not readonly then
436-
      delete(true)
436+
      delete(true)
437-
    end
437+
    end
438-
  end,
438+
  end,
439-
  newline = function()
439+
  newline = function()
440-
    if not readonly then
440+
    if not readonly then
441-
      enter()
441+
      enter()
442-
    end
442+
    end
443-
  end,
443+
  end,
444-
444+
445-
  save = function()
445+
  save = function()
446-
    if readonly then return end
446+
    if readonly then return end
447-
    local new = not fs.exists(filename)
447+
    local new = not fs.exists(filename)
448-
    local backup
448+
    local backup
449-
    if not new then
449+
    if not new then
450-
      backup = filename .. "~"
450+
      backup = filename .. "~"
451-
      for i = 1, math.huge do
451+
      for i = 1, math.huge do
452-
        if not fs.exists(backup) then
452+
        if not fs.exists(backup) then
453-
          break
453+
          break
454-
        end
454+
        end
455-
        backup = filename .. "~" .. i
455+
        backup = filename .. "~" .. i
456-
      end
456+
      end
457-
      fs.copy(filename, backup)
457+
      fs.copy(filename, backup)
458-
    end
458+
    end
459-
    local f, reason = io.open(filename, "w")
459+
    local f, reason = io.open(filename, "w")
460-
    if f then
460+
    if f then
461-
      local chars, firstLine = 0, true
461+
      local chars, firstLine = 0, true
462-
      for _, line in ipairs(buffer) do
462+
      for _, line in ipairs(buffer) do
463-
        if not firstLine then
463+
        if not firstLine then
464-
          line = "\n" .. line
464+
          line = "\n" .. line
465-
        end
465+
        end
466-
        firstLine = false
466+
        firstLine = false
467-
        f:write(line)
467+
        f:write(line)
468-
        chars = chars + unicode.len(line)
468+
        chars = chars + unicode.len(line)
469-
      end
469+
      end
470-
      f:close()
470+
      f:close()
471-
      local format
471+
      local format
472-
      if new then
472+
      if new then
473-
        format = [["%s" [New] %dL,%dC written]]
473+
        format = [["%s" [New] %dL,%dC written]]
474-
      else
474+
      else
475-
        format = [["%s" %dL,%dC written]]
475+
        format = [["%s" %dL,%dC written]]
476-
      end
476+
      end
477-
      setStatus(string.format(format, fs.name(filename), #buffer, chars))
477+
      setStatus(string.format(format, fs.name(filename), #buffer, chars))
478-
    else
478+
    else
479-
      setStatus(reason)
479+
      setStatus(reason)
480-
    end
480+
    end
481-
    if not new then
481+
    if not new then
482-
      fs.remove(backup)
482+
      fs.remove(backup)
483-
    end
483+
    end
484-
  end,
484+
  end,
485-
  close = function()
485+
  close = function()
486-
    -- TODO ask to save if changed
486+
    -- TODO ask to save if changed
487-
    running = false
487+
    running = false
488-
  end,
488+
  end,
489-
  find = function()
489+
  find = function()
490-
    findText = ""
490+
    findText = ""
491-
    find()
491+
    find()
492-
  end,
492+
  end,
493-
  findnext = find
493+
  findnext = find
494-
}
494+
}
495-
495+
496-
getKeyBindHandler = function(code)
496+
getKeyBindHandler = function(code)
497-
  if type(config.keybinds) ~= "table" then return end
497+
  if type(config.keybinds) ~= "table" then return end
498-
  -- Look for matches, prefer more 'precise' keybinds, e.g. prefer
498+
  -- Look for matches, prefer more 'precise' keybinds, e.g. prefer
499-
  -- ctrl+del over del.
499+
  -- ctrl+del over del.
500-
  local result, resultName, resultWeight = nil, nil, 0
500+
  local result, resultName, resultWeight = nil, nil, 0
501-
  for command, keybinds in pairs(config.keybinds) do
501+
  for command, keybinds in pairs(config.keybinds) do
502-
    if type(keybinds) == "table" and keyBindHandlers[command] then
502+
    if type(keybinds) == "table" and keyBindHandlers[command] then
503-
      for _, keybind in ipairs(keybinds) do
503+
      for _, keybind in ipairs(keybinds) do
504-
        if type(keybind) == "table" then
504+
        if type(keybind) == "table" then
505-
          local alt, control, shift, key
505+
          local alt, control, shift, key
506-
          for _, value in ipairs(keybind) do
506+
          for _, value in ipairs(keybind) do
507-
            if value == "alt" then alt = true
507+
            if value == "alt" then alt = true
508-
            elseif value == "control" then control = true
508+
            elseif value == "control" then control = true
509-
            elseif value == "shift" then shift = true
509+
            elseif value == "shift" then shift = true
510-
            else key = value end
510+
            else key = value end
511-
          end
511+
          end
512-
          if (not alt or keyboard.isAltDown()) and
512+
          if (not alt or keyboard.isAltDown()) and
513-
             (not control or keyboard.isControlDown()) and
513+
             (not control or keyboard.isControlDown()) and
514-
             (not shift or keyboard.isShiftDown()) and
514+
             (not shift or keyboard.isShiftDown()) and
515-
             code == keyboard.keys[key] and
515+
             code == keyboard.keys[key] and
516-
             #keybind > resultWeight
516+
             #keybind > resultWeight
517-
          then
517+
          then
518-
            resultWeight = #keybind
518+
            resultWeight = #keybind
519-
            resultName = command
519+
            resultName = command
520-
            result = keyBindHandlers[command]
520+
            result = keyBindHandlers[command]
521-
          end
521+
          end
522-
        end
522+
        end
523-
      end
523+
      end
524-
    end
524+
    end
525-
  end
525+
  end
526-
  return result, resultName
526+
  return result, resultName
527-
end
527+
end
528-
528+
529-
-------------------------------------------------------------------------------
529+
-------------------------------------------------------------------------------
530-
530+
531-
local function onKeyDown(char, code)
531+
local function onKeyDown(char, code)
532-
  local handler = getKeyBindHandler(code)
532+
  local handler = getKeyBindHandler(code)
533-
  if handler then
533+
  if handler then
534-
    handler()
534+
    handler()
535-
  elseif readonly and code == keyboard.keys.q then
535+
  elseif readonly and code == keyboard.keys.q then
536-
    running = false
536+
    running = false
537-
  elseif not readonly then
537+
  elseif not readonly then
538-
    if not keyboard.isControl(char) then
538+
    if not keyboard.isControl(char) then
539-
      insert(unicode.char(char))
539+
      insert(unicode.char(char))
540-
    elseif unicode.char(char) == "\t" then
540+
    elseif unicode.char(char) == "\t" then
541-
      insert("  ")
541+
      insert("  ")
542-
    end
542+
    end
543-
  end
543+
  end
544-
end
544+
end
545-
545+
546-
local function onClipboard(value)
546+
local function onClipboard(value)
547-
  value = value:gsub("\r\n", "\n")
547+
  value = value:gsub("\r\n", "\n")
548-
  local cbx, cby = getCursor()
548+
  local cbx, cby = getCursor()
549-
  local start = 1
549+
  local start = 1
550-
  local l = value:find("\n", 1, true)
550+
  local l = value:find("\n", 1, true)
551-
  if l then
551+
  if l then
552-
    repeat
552+
    repeat
553-
      local line = string.sub(value, start, l - 1)
553+
      local line = string.sub(value, start, l - 1)
554-
      line = text.detab(line, 2)
554+
      line = text.detab(line, 2)
555-
      insert(line)
555+
      insert(line)
556-
      enter()
556+
      enter()
557-
      start = l + 1
557+
      start = l + 1
558-
      l = value:find("\n", start, true)
558+
      l = value:find("\n", start, true)
559-
    until not l
559+
    until not l
560-
  end
560+
  end
561-
  insert(string.sub(value, start))
561+
  insert(string.sub(value, start))
562-
end
562+
end
563-
563+
564-
local function onClick(x, y)
564+
local function onClick(x, y)
565-
  setCursor(x + scrollX, y + scrollY)
565+
  setCursor(x + scrollX, y + scrollY)
566-
end
566+
end
567-
567+
568-
local function onScroll(direction)
568+
local function onScroll(direction)
569-
  local cbx, cby = getCursor()
569+
  local cbx, cby = getCursor()
570-
  setCursor(cbx, cby - direction * 12)
570+
  setCursor(cbx, cby - direction * 12)
571-
end
571+
end
572-
572+
573-
-------------------------------------------------------------------------------
573+
-------------------------------------------------------------------------------
574-
574+
575-
do
575+
do
576-
  local f = io.open(filename)
576+
  local f = io.open(filename)
577-
  if f then
577+
  if f then
578-
    local w, h = getSize()
578+
    local w, h = getSize()
579-
    local chars = 0
579+
    local chars = 0
580-
    for line in f:lines() do
580+
    for line in f:lines() do
581-
      if line:sub(-1) == "\r" then
581+
      if line:sub(-1) == "\r" then
582-
        line = line:sub(1, -2)
582+
        line = line:sub(1, -2)
583-
      end
583+
      end
584-
      table.insert(buffer, line)
584+
      table.insert(buffer, line)
585-
      chars = chars + unicode.len(line)
585+
      chars = chars + unicode.len(line)
586-
      if #buffer <= h then
586+
      if #buffer <= h then
587-
        component.gpu.set(1, #buffer, line)
587+
        component.gpu.set(1, #buffer, line)
588-
      end
588+
      end
589-
    end
589+
    end
590-
    f:close()
590+
    f:close()
591-
    if #buffer == 0 then
591+
    if #buffer == 0 then
592-
      table.insert(buffer, "")
592+
      table.insert(buffer, "")
593-
    end
593+
    end
594-
    local format
594+
    local format
595-
    if readonly then
595+
    if readonly then
596-
      format = [["%s" [readonly] %dL,%dC]]
596+
      format = [["%s" [readonly] %dL,%dC]]
597-
    else
597+
    else
598-
      format = [["%s" %dL,%dC]]
598+
      format = [["%s" %dL,%dC]]
599-
    end
599+
    end
600-
    setStatus(string.format(format, fs.name(filename), #buffer, chars))
600+
    setStatus(string.format(format, fs.name(filename), #buffer, chars))
601-
  else
601+
  else
602-
    table.insert(buffer, "")
602+
    table.insert(buffer, "")
603-
    setStatus(string.format([["%s" [New File] ]], fs.name(filename)))
603+
    setStatus(string.format([["%s" [New File] ]], fs.name(filename)))
604-
  end
604+
  end
605-
  setCursor(1, 1)
605+
  setCursor(1, 1)
606-
end
606+
end
607-
607+
608-
while running do
608+
while running do
609-
  local event, address, arg1, arg2, arg3 = event.pull()
609+
  local event, address, arg1, arg2, arg3 = event.pull()
610-
  if type(address) == "string" and component.isPrimary(address) then
610+
  if type(address) == "string" and component.isPrimary(address) then
611-
    local blink = true
611+
    local blink = true
612-
    if event == "key_down" then
612+
    if event == "key_down" then
613-
      onKeyDown(arg1, arg2)
613+
      onKeyDown(arg1, arg2)
614-
    elseif event == "clipboard" and not readonly then
614+
    elseif event == "clipboard" and not readonly then
615-
      onClipboard(arg1)
615+
      onClipboard(arg1)
616-
    elseif event == "touch" or event == "drag" then
616+
    elseif event == "touch" or event == "drag" then
617-
      onClick(arg1, arg2)
617+
      onClick(arg1, arg2)
618-
    elseif event == "scroll" then
618+
    elseif event == "scroll" then
619-
      onScroll(arg3)
619+
      onScroll(arg3)
620-
    else
620+
    else
621-
      blink = false
621+
      blink = false
622-
    end
622+
    end
623-
    if blink then
623+
    if blink then
624-
      term.setCursorBlink(true)
624+
      term.setCursorBlink(true)
625-
      term.setCursorBlink(true) -- force toggle to caret
625+
      term.setCursorBlink(true) -- force toggle to caret
626-
    end
626+
    end
627-
  end
627+
  end
628-
end
628+
end
629-
629+
630-
term.clear()
630+
term.clear()
631
term.setCursorBlink(false)