Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --[[
- editor (tentative name) v0.2
- by KinoftheFlames
- This is an IDE intended to replace the native file editor "edit".
- The goal of this program is to make editting lua programs for ComputerCraft as fluid as possible.
- For more info: http://www.computercraft.info/forums2/index.php?/topic/4270-wip-in-game-ideedit-program/
- CONTROLS:
- F3 - Save
- F4 - Exit
- F5 - Run
- CHANGELOG for v0.2:
- - Refactored and simplified code to reduce possible errors
- - Added message display at the bottom for general information (e.g. program saved, program execution success/failure)
- - Added exit without saving confirmation
- - Added in-editor program execution (F5)
- - Added tab support (more annoying to implement than you'd think...)
- - Added page up and page down support
- - Changed exit code to return true if exiting normally or false if due to an error
- ]]
- --get filepath
- local arg = { ... }
- if #arg == 0 then
- print("Syntax: editor <path>")
- error()
- end
- --debug
- local bDebug = false --display debugging info if true
- local sDebugText = "" --debug text shown if debugging is on
- --settings
- local bInsert = true --insert text if true, override text if false
- local bShowLineNum = true --toggle visibility of line numbers
- local bShowLineNumSep = true --toggle visibility of line numbers seperator
- local bShowMessages = true --toggle displaying of messages at the bottom of the screen
- local nMinLineDigitsVisible = 1 --[POS] defines how much area for the line numbers is displayed at minimum (program will expand space as needed)
- local nSpacesPerTab = 3 --[POS] defines how many visual spaces are in a tab
- --flags
- local bUnsavedChanges = false --tracks whether the file in the editor has been changed since last saved, helps with exit before saving confirmation
- local bConfirmingExit = false --if the user tries to exit without saving, this requires them to try again before succeeding
- --variables
- local bRunning = true --false when the program exits
- local sFilepath = arg[1] --path to the file being editted
- local tFile = { } --table of lines for the file being editted
- local xMin, yMin = 1, 1 --min values for file display
- local xMax, yMax = term.getSize() --max values for file display
- local xFile, yFile = 1, 1 --the location of the cursor in the text file
- local xScroll, yScroll = 0, 0 --amount file is scrolled on screen
- local sMessage = "" --message displayed at the bottom of the screen until next input
- ------------------------
- -- FILE HANDLING --
- ------------------------
- --load a file from hdd into editor
- function load()
- --error handling file
- if fs.exists(sFilepath) then
- if fs.isDir(sFilepath) then
- print("Cannot read directory as a file.")
- error() --exit program
- else
- --load file
- local file = fs.open(sFilepath, "r")
- while true do
- local line = file.readLine()
- if line == nil then --if reached end of file
- break end
- table.insert(tFile, line)
- end
- file.close()
- end
- else
- table.insert(tFile, "")
- end
- end
- --save editor's text to a file
- function save()
- file = fs.open(sFilepath, "w")
- for i,line in ipairs(tFile) do
- file.writeLine(line) end
- file.close()
- sMessage = "SAVED"
- bUnsavedChanges = false
- end
- ------------------------
- -- VISUAL --
- ------------------------
- --scrolls the screen with the cursor
- function scroll()
- alignCursor()
- local x,y = term.getCursorPos()
- if y > yMax then
- yScroll = yScroll + (y - yMax)
- elseif y < yMin then
- yScroll = yScroll - (yMin - y)
- end
- if x > xMax then
- xScroll = xScroll + (x - xMax)
- elseif x < xMin then
- xScroll = xScroll - (xMin - x)
- end
- end
- --brings the visual cursor to the location of the cursor in file
- function alignCursor()
- local charsLeftOfCursor = string.sub(tFile[yFile], 1, xFile-1) --chars left of cursor
- local dummy, numTabs = string.gsub(charsLeftOfCursor, "\t", "\t") --gets the number of tabs
- local x = xMin + (xFile - 1) - xScroll + numTabs * (nSpacesPerTab - 1)
- local y = yMin + (yFile - 1) - yScroll
- term.setCursorPos(x, y)
- end
- --moves cursor around
- function shiftCursor(xShift, yShift)
- --format input for error handling
- if yFile + yShift < 1 then
- yShift = -(yFile - 1)
- elseif yFile + yShift > #tFile then
- yShift = #tFile - yFile
- end
- --handle left/right movement
- if xFile == 1 and xShift < 0 then --if at start of line and moving left, move to end of above line (if exists)
- if yFile ~= 1 then
- xFile = #tFile[yFile-1] + 1
- yFile = yFile - 1
- end
- elseif xFile > #tFile[yFile] and xShift > 0 then --if at end of line and moving right, move to start of next line (if exists)
- if yFile ~= #tFile then
- xFile = 1
- yFile = yFile + 1
- end
- else --horizontal only movement
- xFile = xFile + xShift--normal move
- end
- --handle up/down movement
- if yFile == 1 and yShift < 0 then --if at first line and moving up, move to start of line
- xFile = 1
- elseif yFile == #tFile and yShift > 0 then --if at last line and moving down, move to end of line
- xFile = #tFile[yFile] + 1
- else --vertical only movement
- yFile = yFile + yShift --normal move
- --if the cursor is past the end of the last character after having moved, move it to the last character
- if xFile > #tFile[yFile] then
- xFile = 1 + #tFile[yFile]
- end
- end
- end
- --move cursor to the start of the line
- function shiftCursorHome()
- xFile = 1
- end
- --move cursor to the end of the line (end of text)
- function shiftCursorEnd()
- xFile = 1 + #tFile[yFile]
- end
- --adjusts borders of editable area depending on settings and situational factors
- function applyBoundrySettings()
- if bShowLineNum then
- local chars = #tostring(#tFile) --characters in line num area = num of digits of last line
- if nMinLineDigitsVisible > chars then --unless there is a minimum character count to enforce which is greater
- chars = nMinLineDigitsVisible end
- xMin = chars + 2 --adjust boundry for editable area
- else
- xMin = 1 end
- end
- ------------------------
- -- DRAWING --
- ------------------------
- --redraws entire screen
- function draw()
- --clear screen
- term.clear()
- --draw line numbers
- if bShowLineNum then
- drawLineNum() end
- --draw file text
- for i=yMin,yMax do
- if tFile[i+yScroll] ~= nil then --if haven't reached end of file
- term.setCursorPos(xMin, i)
- local modifiedLine = string.gsub(tFile[i+yScroll], "\t", string.rep(" ", nSpacesPerTab)) --replace visual line with defined number of spaces
- term.write(string.sub(modifiedLine, 1 + xScroll, xMax - xMin + 1 + xScroll)) --only draw visible text
- end
- end
- --draw message
- if bShowMessages then
- drawMessage() end
- --draw debug
- if bDebug then
- drawDebug(sDebugText) end
- --place visual cursor in correct position
- alignCursor()
- end
- --draws line numbers and seperator
- function drawLineNum()
- applyBoundrySettings()
- local chars = #tostring(#tFile) --characters in line num area = num of digits of last line
- if nMinLineDigitsVisible > chars then --unless there is a minimum character count to enforce which is greater
- chars = nMinLineDigitsVisible end
- for i=yMin,yMax do
- local lineNum = i+yScroll --line number of file line being drawn
- if tFile[lineNum] ~= nil then --don't draw line numbers for lines that dont exists in the file
- local numSpaces = chars - #tostring(lineNum) --spaces required to right-align numbers
- --draw finally
- term.setCursorPos(1, i)
- term.write(string.rep(" ", numSpaces)) --spaces before line number
- term.write(lineNum) --line number
- if bShowLineNumSep then
- term.write("|") end --seperator --TODO: add customizable line seperator (single char)
- end
- end
- --sDebugText = sDebugText .. " xMin:"..xMin
- end
- --draw general message at the bottom of the screen for one loop
- function drawMessage()
- if sMessage == "" then
- return end
- --add dashes on the side to draw attention
- local openSpace = xMax - #sMessage --openSpace = screenWidth - messageWidth
- local fillerDrawn = (openSpace/2)-1 --dashes drawn on one side of message
- if fillerDrawn < 0 then
- fillerDrawn = 0 end
- if openSpace % 2 == 0 then
- sMessage = string.rep("-", fillerDrawn) .. " " .. sMessage .. " " .. string.rep("-", fillerDrawn)
- else
- sMessage = string.rep("-", fillerDrawn) .. " " .. sMessage .. " " .. string.rep("-", fillerDrawn) end
- --display message
- term.setCursorPos(1,yMax) --bottom left
- term.write(sMessage)
- sMessage = ""
- end
- --display debug info in bottom right
- function drawDebug(sText)
- term.setCursorPos(xMax + 1 - #sText, yMax) --set cursor at bottom rightmost while still showing all text
- term.write(sText)
- end
- ------------------------
- -- DEBUGGING --
- ------------------------
- --run the program being edited
- function run()
- save() --save so its running the program in the editor
- term.clear()
- term.setCursorPos(1,1)
- local success = shell.run(sFilepath)
- --display whether program succeeded or failed, then prompt to continue
- if success then
- sMessage = "SUCCESS - Press any key"
- else
- sMessage = "FAILED - Press any key" end
- drawMessage()
- os.pullEvent("key")
- os.sleep(0) --if the key pressed produces a "char" event, this prevents it from going to the next input loop
- sMessage = "" --clear any messages generated by the run process
- end
- ------------------------
- -- OTHER --
- ------------------------
- --inserts characters the user types in
- function insertText(sText)
- tFile[yFile] = string.sub(tFile[yFile], 1, xFile-1) .. sText .. string.sub(tFile[yFile], xFile)
- shiftCursor(#sText, 0)
- bUnsavedChanges = true
- end
- ------------------------
- -- CORE --
- ------------------------
- --intialization
- function intialize()
- load() --load file
- --setup screen
- applyBoundrySettings()
- draw()
- term.setCursorBlink(true)
- end
- --handles user input
- function input()
- --wait for key input
- local e, p1 = os.pullEvent()
- --reset exit w/ unsaved changes confirmation if the user presses any other input than exit
- if bConfirmingExit == true and e == "key" and p1 == keys.f4 then
- exit()
- else
- bConfirmingExit = false end
- if e == "char" then --handles character input
- insertText(p1)
- elseif e == "key" then -- handles non-character input
- --DONT USE F2, F10, F11, ALT, ESC
- --functional keys
- if p1 == keys.f1 then --menu
- elseif p1 == keys.f3 then --save
- save()
- elseif p1 == keys.f4 then --exit
- exit()
- elseif p1 == keys.f5 then --run program in editor
- run()
- --arrow keys
- elseif p1 == keys.up then
- shiftCursor(0, -1)
- elseif p1 == keys.down then
- shiftCursor(0, 1)
- elseif p1 == keys.left then
- shiftCursor(-1, 0)
- elseif p1 == keys.right then
- shiftCursor(1, 0)
- --home, end, page up, page down
- elseif p1 == keys.home then --home
- shiftCursorHome()
- elseif p1 == keys["end"] then --end
- shiftCursorEnd()
- elseif p1 == keys.pageUp then --pasge up
- shiftCursor(0, -(yMax-yMin))
- elseif p1 == keys.pageDown then --page down
- shiftCursor(0, yMax-yMin)
- --backspace, delete
- elseif p1 == keys.backspace then --backspace
- if not (yFile == 1 and xFile == 1) then --if not at the first character in the file
- if xFile == 1 then --if at the start of a line, concatenate line above with this line
- shiftCursor(0,-1)
- shiftCursorEnd()
- tFile[yFile] = tFile[yFile] .. tFile[yFile+1] --concat lines
- table.remove(tFile, yFile+1) --remove second line
- else --otherwise just delete one character
- shiftCursor(-1,0)
- tFile[yFile] = string.sub(tFile[yFile], 1, xFile-1) .. string.sub(tFile[yFile], xFile+1)
- end
- bUnsavedChanges = true
- end
- elseif p1 == keys.delete then --delete
- if not (yFile == #tFile and xFile == #tFile[yFile] + 1) then --if not at the last character in the file
- if xFile > #tFile[yFile] then --if cursor is at the end of the line, concatenate with line below
- tFile[yFile] = tFile[yFile] .. tFile[yFile+1] --concat lines
- table.remove(tFile, yFile+1) --remove second line
- else --otherwise just delete one character
- tFile[yFile] = string.sub(tFile[yFile], 1, xFile-1) .. string.sub(tFile[yFile], xFile+1)
- end
- bUnsavedChanges = true
- end
- --enter, tab
- elseif p1 == keys.enter then --enter
- if xFile <= #tFile[yFile] then --if cursor is between characters
- table.insert(tFile, yFile+1, string.sub(tFile[yFile], xFile)) --move remainder of characters to a new line
- tFile[yFile] = string.sub(tFile[yFile], 1, xFile-1) --and delete the moved characters from the previous line
- else --cursor at the end of the line
- table.insert(tFile, yFile+1, "")
- end
- shiftCursorHome()
- shiftCursor(0,1)
- bUnsavedChanges = true
- elseif p1 == keys.tab then --tab
- insertText("\t")
- bUnsavedChanges = true
- end
- end
- end
- --main loop
- function mainLoop()
- while bRunning do
- --clear debug info
- sDebugText = ""
- input() --get input and respond
- --debug info
- local x,y = term.getCursorPos()
- sDebugText = sDebugText .. " File:"..xFile..","..yFile .." Scrn:"..x..","..y
- scroll() --shift screen to show cursor
- draw() --update screen
- end
- end
- --exit program
- function exit()
- if bUnsavedChanges and not bConfirmingExit then --if there are unsaved changes, and this warning hasnt displayed yet
- sMessage = "UNSAVED CHANGES - Press F4 to exit"
- bConfirmingExit = true
- else
- bRunning = false end
- end
- --have the program exit cleanly and tidily
- function exitCleanup()
- term.clear()
- term.setCursorPos(1,1)
- end
- intialize()
- mainLoop()
- exitCleanup()
Advertisement
Add Comment
Please, Sign In to add comment