409 lines
12 KiB
Lua
409 lines
12 KiB
Lua
-- SPDX-FileCopyrightText: 2017 Daniel Ratcliffe
|
|
--
|
|
-- SPDX-License-Identifier: LicenseRef-CCPL
|
|
|
|
-- Get file to edit
|
|
|
|
local x, y = 1, 1
|
|
local w, h = term.getSize()
|
|
local scrollX, scrollY = 0, 0
|
|
|
|
local tLines = {""}
|
|
local bRunning = true
|
|
|
|
local returnvalue = nil
|
|
|
|
-- Colours
|
|
local highlightColour, keywordColour, textColour, bgColour, errorColour
|
|
|
|
bgColour = colours.black
|
|
textColour = colours.white
|
|
highlightColour = colours.white
|
|
keywordColour = colours.white
|
|
errorColour = colours.white
|
|
|
|
-- Menus
|
|
local menu = require "cc.internal.menu"
|
|
local current_menu
|
|
local menu_items = {}
|
|
table.insert(menu_items, "Done")
|
|
|
|
local status_ok, status_text
|
|
|
|
local tCompletions
|
|
local nCompletion
|
|
|
|
local function redrawLines(line, endLine)
|
|
if not endLine then endLine = line end
|
|
|
|
local colour = term.getTextColour()
|
|
|
|
-- Highlight all lines between line and endLine, highlighting further lines if their
|
|
-- lexer state has changed and aborting at the end of the screen.
|
|
local changed = false
|
|
while (changed or line <= endLine) and line - scrollY < h do
|
|
term.setCursorPos(1 - scrollX, line - scrollY)
|
|
term.clearLine()
|
|
|
|
local contents = tLines[line]
|
|
if not contents then break end
|
|
print(contents)
|
|
|
|
line = line + 1
|
|
end
|
|
|
|
term.setTextColor(colours.white)
|
|
term.setCursorPos(x - scrollX, y - scrollY)
|
|
end
|
|
|
|
local function redrawText()
|
|
redrawLines(scrollY + 1, scrollY + h - 1)
|
|
end
|
|
|
|
local function redrawMenu()
|
|
-- Clear line
|
|
term.setCursorPos(1, h)
|
|
term.clearLine()
|
|
|
|
-- Draw line numbers
|
|
term.setCursorPos(w - #("Ln " .. y) + 1, h)
|
|
term.setTextColour(highlightColour)
|
|
term.write("Ln ")
|
|
term.setTextColour(textColour)
|
|
term.write(y)
|
|
|
|
term.setCursorPos(1, h)
|
|
if current_menu then
|
|
-- Draw menu
|
|
menu.draw(current_menu)
|
|
else
|
|
-- Draw status
|
|
term.setTextColour(colors.yellow)
|
|
term.write("Press ctrl to view options")
|
|
term.setTextColour(textColour)
|
|
end
|
|
|
|
-- Reset cursor
|
|
term.setCursorPos(x - scrollX, y - scrollY)
|
|
term.setCursorBlink(not current_menu)
|
|
end
|
|
|
|
local tMenuFuncs = {
|
|
Done = function()
|
|
local order = table.concat(tLines,"\n")
|
|
term.clear()
|
|
returnvalue = order
|
|
end
|
|
}
|
|
|
|
local function setCursor(newX, newY)
|
|
local _, oldY = x, y
|
|
x, y = newX, newY
|
|
local screenX = x - scrollX
|
|
local screenY = y - scrollY
|
|
|
|
local bRedraw = false
|
|
if screenX < 1 then
|
|
scrollX = x - 1
|
|
screenX = 1
|
|
bRedraw = true
|
|
elseif screenX > w then
|
|
scrollX = x - w
|
|
screenX = w
|
|
bRedraw = true
|
|
end
|
|
|
|
if screenY < 1 then
|
|
scrollY = y - 1
|
|
screenY = 1
|
|
bRedraw = true
|
|
elseif screenY > h - 1 then
|
|
scrollY = y - (h - 1)
|
|
screenY = h - 1
|
|
bRedraw = true
|
|
end
|
|
|
|
if bRedraw then
|
|
redrawText()
|
|
elseif y ~= oldY then
|
|
redrawLines(math.min(y, oldY), math.max(y, oldY))
|
|
else
|
|
redrawLines(y)
|
|
end
|
|
|
|
redrawMenu()
|
|
end
|
|
|
|
-- Actual program functionality begins
|
|
|
|
term.setBackgroundColour(bgColour)
|
|
term.clear()
|
|
term.setCursorPos(x, y)
|
|
term.setCursorBlink(true)
|
|
|
|
redrawText()
|
|
redrawMenu()
|
|
|
|
local function acceptCompletion()
|
|
if nCompletion then
|
|
-- Append the completion
|
|
local sCompletion = tCompletions[nCompletion]
|
|
tLines[y] = tLines[y] .. sCompletion
|
|
setCursor(x + #sCompletion, y)
|
|
end
|
|
end
|
|
|
|
local function handleMenuEvent(event)
|
|
assert(current_menu)
|
|
|
|
local result = menu.handle_event(current_menu, table.unpack(event, 1, event.n))
|
|
if result == false then
|
|
current_menu = nil
|
|
redrawMenu()
|
|
elseif result ~= nil then
|
|
tMenuFuncs[result]()
|
|
current_menu = nil
|
|
redrawMenu()
|
|
end
|
|
end
|
|
|
|
-- Handle input
|
|
while bRunning do
|
|
local event = table.pack(os.pullEvent())
|
|
if event[1] == "key" then
|
|
if current_menu then
|
|
handleMenuEvent(event)
|
|
if returnvalue then
|
|
return returnvalue
|
|
end
|
|
else
|
|
local key = event[2]
|
|
if key == keys.up then
|
|
if nCompletion then
|
|
-- Cycle completions
|
|
nCompletion = nCompletion - 1
|
|
if nCompletion < 1 then
|
|
nCompletion = #tCompletions
|
|
end
|
|
redrawLines(y)
|
|
|
|
elseif y > 1 then
|
|
-- Move cursor up
|
|
setCursor(
|
|
math.min(x, #tLines[y - 1] + 1),
|
|
y - 1
|
|
)
|
|
end
|
|
|
|
elseif key == keys.down then
|
|
if nCompletion then
|
|
-- Cycle completions
|
|
nCompletion = nCompletion + 1
|
|
if nCompletion > #tCompletions then
|
|
nCompletion = 1
|
|
end
|
|
redrawLines(y)
|
|
|
|
elseif y < #tLines then
|
|
-- Move cursor down
|
|
setCursor(
|
|
math.min(x, #tLines[y + 1] + 1),
|
|
y + 1
|
|
)
|
|
end
|
|
|
|
elseif key == keys.tab then
|
|
if nCompletion and x == #tLines[y] + 1 then
|
|
-- Accept autocomplete
|
|
acceptCompletion()
|
|
else
|
|
-- Indent line
|
|
local sLine = tLines[y]
|
|
tLines[y] = string.sub(sLine, 1, x - 1) .. " " .. string.sub(sLine, x)
|
|
setCursor(x + 4, y)
|
|
end
|
|
|
|
elseif key == keys.pageUp then
|
|
-- Move up a page
|
|
local newY
|
|
if y - (h - 1) >= 1 then
|
|
newY = y - (h - 1)
|
|
else
|
|
newY = 1
|
|
end
|
|
setCursor(
|
|
math.min(x, #tLines[newY] + 1),
|
|
newY
|
|
)
|
|
elseif key == keys.pageDown then
|
|
-- Move down a page
|
|
local newY
|
|
if y + (h - 1) <= #tLines then
|
|
newY = y + (h - 1)
|
|
else
|
|
newY = #tLines
|
|
end
|
|
local newX = math.min(x, #tLines[newY] + 1)
|
|
setCursor(newX, newY)
|
|
|
|
elseif key == keys.home then
|
|
-- Move cursor to the beginning
|
|
if x > 1 then
|
|
setCursor(1, y)
|
|
end
|
|
|
|
elseif key == keys["end"] then
|
|
-- Move cursor to the end
|
|
local nLimit = #tLines[y] + 1
|
|
if x < nLimit then
|
|
setCursor(nLimit, y)
|
|
end
|
|
|
|
elseif key == keys.left then
|
|
if x > 1 then
|
|
-- Move cursor left
|
|
setCursor(x - 1, y)
|
|
elseif x == 1 and y > 1 then
|
|
setCursor(#tLines[y - 1] + 1, y - 1)
|
|
end
|
|
|
|
elseif key == keys.right then
|
|
local nLimit = #tLines[y] + 1
|
|
if x < nLimit then
|
|
-- Move cursor right
|
|
setCursor(x + 1, y)
|
|
elseif nCompletion and x == #tLines[y] + 1 then
|
|
-- Accept autocomplete
|
|
acceptCompletion()
|
|
elseif x == nLimit and y < #tLines then
|
|
-- Go to next line
|
|
setCursor(1, y + 1)
|
|
end
|
|
|
|
elseif key == keys.delete then
|
|
local nLimit = #tLines[y] + 1
|
|
if x < nLimit then
|
|
local sLine = tLines[y]
|
|
tLines[y] = string.sub(sLine, 1, x - 1) .. string.sub(sLine, x + 1)
|
|
redrawLines(y)
|
|
elseif y < #tLines then
|
|
tLines[y] = tLines[y] .. tLines[y + 1]
|
|
table.remove(tLines, y + 1)
|
|
redrawText()
|
|
end
|
|
|
|
elseif key == keys.backspace then
|
|
if x > 1 then
|
|
-- Remove character
|
|
local sLine = tLines[y]
|
|
if x > 4 and string.sub(sLine, x - 4, x - 1) == " " and not string.sub(sLine, 1, x - 1):find("%S") then
|
|
tLines[y] = string.sub(sLine, 1, x - 5) .. string.sub(sLine, x)
|
|
setCursor(x - 4, y)
|
|
else
|
|
tLines[y] = string.sub(sLine, 1, x - 2) .. string.sub(sLine, x)
|
|
setCursor(x - 1, y)
|
|
end
|
|
elseif y > 1 then
|
|
-- Remove newline
|
|
local sPrevLen = #tLines[y - 1]
|
|
tLines[y - 1] = tLines[y - 1] .. tLines[y]
|
|
table.remove(tLines, y)
|
|
setCursor(sPrevLen + 1, y - 1)
|
|
redrawText()
|
|
end
|
|
|
|
elseif (key == keys.enter or key == keys.numPadEnter) then
|
|
-- Newline
|
|
local sLine = tLines[y]
|
|
local _, spaces = string.find(sLine, "^[ ]+")
|
|
if not spaces then
|
|
spaces = 0
|
|
end
|
|
tLines[y] = string.sub(sLine, 1, x - 1)
|
|
table.insert(tLines, y + 1, string.rep(' ', spaces) .. string.sub(sLine, x))
|
|
setCursor(spaces + 1, y + 1)
|
|
redrawText()
|
|
|
|
elseif key == keys.leftCtrl or key == keys.rightCtrl then
|
|
current_menu = menu.create(menu_items)
|
|
redrawMenu()
|
|
end
|
|
end
|
|
elseif event[1] == "char" then
|
|
if current_menu then
|
|
handleMenuEvent(event)
|
|
else
|
|
-- Input text
|
|
local sLine = tLines[y]
|
|
tLines[y] = string.sub(sLine, 1, x - 1) .. event[2] .. string.sub(sLine, x)
|
|
setCursor(x + 1, y)
|
|
end
|
|
|
|
elseif event[1] == "paste" then
|
|
-- Close menu if open
|
|
if current_menu then
|
|
current_menu = nil
|
|
redrawMenu()
|
|
end
|
|
|
|
-- Input text
|
|
local text = event[2]
|
|
local sLine = tLines[y]
|
|
tLines[y] = string.sub(sLine, 1, x - 1) .. text .. string.sub(sLine, x)
|
|
setCursor(x + #text, y)
|
|
|
|
elseif event[1] == "mouse_click" then
|
|
local button, cx, cy = event[2], event[3], event[4]
|
|
if current_menu then
|
|
handleMenuEvent(event)
|
|
else
|
|
if button == 1 then
|
|
-- Left click
|
|
if cy < h then
|
|
local newY = math.min(math.max(scrollY + cy, 1), #tLines)
|
|
local newX = math.min(math.max(scrollX + cx, 1), #tLines[newY] + 1)
|
|
setCursor(newX, newY)
|
|
else
|
|
current_menu = menu.create(menu_items)
|
|
redrawMenu()
|
|
end
|
|
end
|
|
end
|
|
|
|
elseif event[1] == "mouse_scroll" then
|
|
if not current_menu then
|
|
local direction = event[2]
|
|
if direction == -1 then
|
|
-- Scroll up
|
|
if scrollY > 0 then
|
|
-- Move cursor up
|
|
scrollY = scrollY - 1
|
|
redrawText()
|
|
end
|
|
|
|
elseif direction == 1 then
|
|
-- Scroll down
|
|
local nMaxScroll = #tLines - (h - 1)
|
|
if scrollY < nMaxScroll then
|
|
-- Move cursor down
|
|
scrollY = scrollY + 1
|
|
redrawText()
|
|
end
|
|
|
|
end
|
|
end
|
|
|
|
elseif event[1] == "term_resize" then
|
|
w, h = term.getSize()
|
|
setCursor(x, y)
|
|
redrawMenu()
|
|
redrawText()
|
|
|
|
end
|
|
end
|
|
|
|
-- Cleanup
|
|
term.clear()
|
|
term.setCursorBlink(false)
|
|
term.setCursorPos(1, 1)
|