Advertisement
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
- F6 - Run with arguments
- CHANGELOG for v0.2.1:
- This is a very minor update to fix a visual bug with the new version, CC 1.42, which will now be the solely supported version.
- I tried to allow exiting the running programs by pressing F4, but it's not feasible to do without user-assistance (which doesn't really seem worth it).
- If you want to exit a running program, you can still use Ctrl+T if your program allows it, and it will return cleanly to the IDE.
- And in case you didn't know, pasting is already natively supported by the system, so it works in this IDE. The only reason it doesn't work in edit is because they have a Ctrl toggled menu.
- - Now compatible with and supporting CC 1.42 (may not work with previous versions)
- - Added message shown when file is saving
- - Now resets program settings after program execution
- - Now able to run programs with arguments, press F6 to do so
- ]]
- --get filepath
- local arg = { ... }
- if #arg == 0 then
- print("Syntax: editor <path>")
- error()
- end
- --debug
- local bDebug = true --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
- local sArguments = "" --stores the last arguments used, bringing them up again when run w/ arguments is called
- ------------------------
- -- 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()
- sMessage = "SAVING..."
- draw()
- if bUnsavedChanges then --only save if needed (saves on CPU time)
- file = fs.open(sFilepath, "w")
- for i,line in ipairs(tFile) do
- file.writeLine(line) end
- file.close()
- end
- 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(tostring(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(bArguments)
- local tArguments = {}
- --get arugments for program execution
- if bArguments then
- --draw seperator dashes below argument input
- term.setCursorPos(1,2)
- term.write(string.rep("-", xMax))
- --clear top line and get arguments
- term.setCursorPos(1,1)
- term.clearLine()
- term.write("Arguments: ")
- sArguments = read(nil, sArguments)
- --format arguments (stolen from shell)
- for match in string.gmatch(sArguments, "[^ \t]+") do --grabs each argument, space/tab delimitted
- table.insert(tArguments, match)
- end
- end
- save() --save so its running the program in the editor
- term.clear()
- term.setCursorPos(1,1)
- local progSuccess = shell.run(sFilepath, unpack(tArguments))
- --display whether program succeeded or failed, then prompt to continue
- if progSuccess 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
- initializeSystem() --reset program systerm-oriented settings (in case they've been changed)
- 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 --
- ------------------------
- --initializes system settings (this is called at startup and after program execution)
- function initializeSystem()
- term.setCursorBlink(true)
- alignCursor()
- end
- --intialization
- function intialize()
- load() --load file
- --setup screen
- applyBoundrySettings()
- draw()
- initializeSystem()
- 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
- sMessage = shell.getRunningProgram()
- elseif p1 == keys.f3 then --save
- save()
- elseif p1 == keys.f4 then --exit
- exit()
- elseif p1 == keys.f5 then --run program in editor
- run(false)
- elseif p1 == keys.f6 then --run with arguments
- run(true)
- --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()
- if bRunning == true then
- sDebugText = sDebugText .. " Running:" ..tostring(bRunning) end
- --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()
- print("EOF HIT for "..shell.getRunningProgram())
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement