KinoftheFlames

Untitled

Sep 23rd, 2012
89
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 15.43 KB | None | 0 0
  1. --[[
  2.  
  3. ed v0.2.1
  4. by KinoftheFlames
  5.  
  6. ed is an IDE intended to replace the native file editor "edit".
  7. The goal of this program is to make editting lua programs for ComputerCraft as fluid as possible.
  8.  
  9. For more info: http://www.computercraft.info/forums2/index.php?/topic/4270-wip-in-game-ideedit-program/
  10.  
  11. CONTROLS:
  12. F3 - Save
  13. F4 - Exit
  14. F5 - Run
  15. F6 - Run with arguments
  16.  
  17. CHANGELOG for v0.2.1:
  18. 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.
  19. 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).
  20. 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.
  21. 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.
  22.  
  23. - Now compatible with and supporting CC 1.42 (may not work with previous versions)
  24. - Added message shown when file is saving
  25. - Now resets program settings after program execution
  26. - Now able to run programs with arguments, press F6 to do so
  27.     --Note that running the IDE from itself will not exit cleanly at the momnent; see http://www.computercraft.info/forums2/index.php?/topic/4336-brunning-false-but-still-running/
  28. ]]
  29.  
  30. --get filepath
  31. local arg = { ... }
  32. if #arg == 0 then
  33.     print("Syntax: editor <path>")
  34.     error()
  35. end
  36.  
  37. --debug
  38. local bDebug = false --display debugging info if true
  39. local sDebugText = "" --debug text shown if debugging is on
  40.  
  41. --settings
  42. local bInsert = true --insert text if true, override text if false
  43. local bShowLineNum = true --toggle visibility of line numbers
  44. local bShowLineNumSep = true --toggle visibility of line numbers seperator
  45. local bShowMessages = true --toggle displaying of messages at the bottom of the screen
  46. local nMinLineDigitsVisible = 1 --[POS] defines how much area for the line numbers is displayed at minimum (program will expand space as needed)
  47. local nSpacesPerTab = 3 --[POS] defines how many visual spaces are in a tab
  48.  
  49. --flags
  50. local bUnsavedChanges = false --tracks whether the file in the editor has been changed since last saved, helps with exit before saving confirmation
  51. local bConfirmingExit = false --if the user tries to exit without saving, this requires them to try again before succeeding
  52.  
  53. --variables
  54. local bRunning = true --false when the program exits
  55. local sFilepath = arg[1] --path to the file being editted
  56. local tFile = { } --table of lines for the file being editted
  57. local xMin, yMin = 1, 1 --min values for file display
  58. local xMax, yMax = term.getSize() --max values for file display
  59. local xFile, yFile = 1, 1 --the location of the cursor in the text file
  60. local xScroll, yScroll = 0, 0 --amount file is scrolled on screen
  61. local sMessage = "" --message displayed at the bottom of the screen until next input
  62. local sArguments = "" --stores the last arguments used, bringing them up again when run w/ arguments is called
  63.  
  64.  
  65.  
  66. ------------------------
  67. --    FILE HANDLING   --
  68. ------------------------
  69.  
  70. --load a file from hdd into editor
  71. function load()
  72.     --error handling file
  73.     if fs.exists(sFilepath) then
  74.         if fs.isDir(sFilepath) then
  75.             print("Cannot read directory as a file.")
  76.             error() --exit program
  77.         else
  78.             --load file
  79.             local file = fs.open(sFilepath, "r")
  80.             while true do
  81.                 local line = file.readLine()
  82.                 if line == nil then --if reached end of file
  83.                     break end
  84.                
  85.                 table.insert(tFile, line)
  86.             end
  87.             file.close()
  88.         end
  89.     else
  90.         table.insert(tFile, "")
  91.     end
  92. end
  93.  
  94. --save editor's text to a file
  95. function save()
  96.     sMessage = "SAVING..."
  97.     draw()
  98.    
  99.     if bUnsavedChanges then --only save if needed (saves on CPU time)
  100.         file = fs.open(sFilepath, "w")
  101.         for i,line in ipairs(tFile) do
  102.             file.writeLine(line) end
  103.         file.close()
  104.     end
  105.    
  106.     sMessage = "SAVED"
  107.     bUnsavedChanges = false
  108. end
  109.  
  110.  
  111.  
  112. ------------------------
  113. --       VISUAL       --
  114. ------------------------
  115.  
  116. --scrolls the screen with the cursor
  117. function scroll()
  118.     alignCursor()
  119.     local x,y = term.getCursorPos()
  120.    
  121.     if y > yMax then
  122.         yScroll = yScroll + (y - yMax)
  123.     elseif y < yMin then
  124.         yScroll = yScroll - (yMin - y)
  125.     end
  126.     if x > xMax then
  127.         xScroll = xScroll + (x - xMax)
  128.     elseif x < xMin then
  129.         xScroll = xScroll - (xMin - x)
  130.     end
  131. end
  132.  
  133. --brings the visual cursor to the location of the cursor in file
  134. function alignCursor()
  135.     local charsLeftOfCursor = string.sub(tFile[yFile], 1, xFile-1) --chars left of cursor
  136.     local dummy, numTabs = string.gsub(charsLeftOfCursor, "\t", "\t") --gets the number of tabs
  137.    
  138.     local x = xMin + (xFile - 1) - xScroll + numTabs * (nSpacesPerTab - 1)
  139.     local y = yMin + (yFile - 1) - yScroll
  140.    
  141.     term.setCursorPos(x, y)
  142. end
  143.  
  144. --moves cursor around
  145. function shiftCursor(xShift, yShift)
  146.     --format input for error handling
  147.     if yFile + yShift < 1 then
  148.         yShift = -(yFile - 1)
  149.     elseif yFile + yShift > #tFile then
  150.         yShift = #tFile - yFile
  151.     end
  152.        
  153.    
  154.     --handle left/right movement
  155.     if xFile == 1 and xShift < 0 then --if at start of line and moving left, move to end of above line (if exists)
  156.         if yFile ~= 1 then
  157.             xFile = #tFile[yFile-1] + 1
  158.             yFile = yFile - 1
  159.         end
  160.     elseif xFile > #tFile[yFile] and xShift > 0 then --if at end of line and moving right, move to start of next line (if exists)
  161.         if yFile ~= #tFile then
  162.             xFile = 1
  163.             yFile = yFile + 1
  164.         end
  165.     else --horizontal only movement
  166.         xFile = xFile + xShift--normal move
  167.     end
  168.    
  169.     --handle up/down movement
  170.     if yFile == 1 and yShift < 0 then --if at first line and moving up, move to start of line
  171.         xFile = 1
  172.     elseif yFile == #tFile and yShift > 0 then --if at last line and moving down, move to end of line
  173.         xFile = #tFile[yFile] + 1
  174.     else --vertical only movement
  175.         yFile = yFile + yShift --normal move
  176.        
  177.         --if the cursor is past the end of the last character after having moved, move it to the last character
  178.         if xFile > #tFile[yFile] then
  179.             xFile = 1 + #tFile[yFile]
  180.         end
  181.     end
  182. end
  183.  
  184. --move cursor to the start of the line
  185. function shiftCursorHome()
  186.     xFile = 1
  187. end
  188.  
  189. --move cursor to the end of the line (end of text)
  190. function shiftCursorEnd()
  191.     xFile = 1 + #tFile[yFile]
  192. end
  193.  
  194. --adjusts borders of editable area depending on settings and situational factors
  195. function applyBoundrySettings()
  196.     if bShowLineNum then
  197.         local chars = #tostring(#tFile) --characters in line num area = num of digits of last line
  198.         if nMinLineDigitsVisible > chars then --unless there is a minimum character count to enforce which is greater
  199.             chars = nMinLineDigitsVisible end
  200.         xMin = chars + 2 --adjust boundry for editable area
  201.     else
  202.         xMin = 1 end
  203. end
  204.  
  205.  
  206.  
  207. ------------------------
  208. --      DRAWING       --
  209. ------------------------
  210.  
  211. --redraws entire screen
  212. function draw()
  213.     --clear screen
  214.     term.clear()
  215.    
  216.     --draw line numbers
  217.     if bShowLineNum then
  218.         drawLineNum() end
  219.    
  220.     --draw file text
  221.     for i=yMin,yMax do
  222.         if tFile[i+yScroll] ~= nil then --if haven't reached end of file
  223.             term.setCursorPos(xMin, i)
  224.             local modifiedLine = string.gsub(tFile[i+yScroll], "\t", string.rep(" ", nSpacesPerTab)) --replace visual line with defined number of spaces
  225.             term.write(string.sub(modifiedLine, 1 + xScroll, xMax - xMin + 1 + xScroll)) --only draw visible text
  226.         end
  227.     end
  228.    
  229.     --draw message
  230.     if bShowMessages then
  231.         drawMessage() end
  232.    
  233.     --draw debug
  234.     if bDebug then
  235.         drawDebug(sDebugText) end
  236.    
  237.     --place visual cursor in correct position
  238.     alignCursor()
  239. end
  240.  
  241. --draws line numbers and seperator
  242. function drawLineNum()
  243.     applyBoundrySettings()
  244.     local chars = #tostring(#tFile) --characters in line num area = num of digits of last line
  245.     if nMinLineDigitsVisible > chars then --unless there is a minimum character count to enforce which is greater
  246.         chars = nMinLineDigitsVisible end
  247.    
  248.     for i=yMin,yMax do
  249.         local lineNum = i+yScroll --line number of file line being drawn
  250.         if tFile[lineNum] ~= nil then --don't draw line numbers for lines that dont exists in the file
  251.             local numSpaces = chars - #tostring(lineNum) --spaces required to right-align numbers
  252.            
  253.             --draw finally
  254.             term.setCursorPos(1, i)
  255.             term.write(string.rep(" ", numSpaces)) --spaces before line number
  256.             term.write(tostring(lineNum)) --line number
  257.             if bShowLineNumSep then
  258.                 term.write("|") end --seperator --TODO: add customizable line seperator (single char)
  259.         end
  260.     end
  261.    
  262.     --sDebugText = sDebugText .. " xMin:"..xMin
  263. end
  264.  
  265. --draw general message at the bottom of the screen for one loop
  266. function drawMessage()
  267.     if sMessage == "" then
  268.         return end
  269.    
  270.     --add dashes on the side to draw attention
  271.     local openSpace = xMax - #sMessage --openSpace = screenWidth - messageWidth
  272.     local fillerDrawn = (openSpace/2)-1 --dashes drawn on one side of message
  273.    
  274.     if fillerDrawn < 0 then
  275.         fillerDrawn = 0 end
  276.    
  277.     if openSpace % 2 == 0 then
  278.         sMessage = string.rep("-", fillerDrawn) .. " " .. sMessage .. " " .. string.rep("-", fillerDrawn)
  279.     else
  280.         sMessage = string.rep("-", fillerDrawn) .. " " .. sMessage .. "  " .. string.rep("-", fillerDrawn) end
  281.    
  282.     --display message
  283.     term.setCursorPos(1,yMax) --bottom left
  284.     term.write(sMessage)
  285.    
  286.     sMessage = ""
  287. end
  288.  
  289. --display debug info in bottom right
  290. function drawDebug(sText)
  291.     term.setCursorPos(xMax + 1 - #sText, yMax) --set cursor at bottom rightmost while still showing all text
  292.     term.write(sText)
  293. end
  294.  
  295.  
  296.  
  297. ------------------------
  298. --      DEBUGGING     --
  299. ------------------------
  300.  
  301. --run the program being edited
  302. function run(bArguments)
  303.     local tArguments = {}
  304.    
  305.     --get arugments for program execution
  306.     if bArguments then
  307.         --draw seperator dashes below argument input
  308.         term.setCursorPos(1,2)
  309.         term.write(string.rep("-", xMax))
  310.        
  311.         --clear top line and get arguments
  312.         term.setCursorPos(1,1)
  313.         term.clearLine()
  314.         term.write("Arguments: ")
  315.         sArguments = read(nil, sArguments)
  316.        
  317.         --format arguments (stolen from shell)
  318.         for match in string.gmatch(sArguments, "[^ \t]+") do --grabs each argument, space/tab delimitted
  319.             table.insert(tArguments, match)
  320.         end
  321.     end
  322.    
  323.     save() --save so its running the program in the editor
  324.     term.clear()
  325.     term.setCursorPos(1,1)
  326.     local progSuccess = shell.run(sFilepath, unpack(tArguments))
  327.    
  328.     --display whether program succeeded or failed, then prompt to continue
  329.     if progSuccess then
  330.         sMessage = "SUCCESS - Press any key"
  331.     else
  332.         sMessage = "FAILED - Press any key" end
  333.     drawMessage()
  334.    
  335.     os.pullEvent("key")
  336.     os.sleep(0) --if the key pressed produces a "char" event, this prevents it from going to the next input loop
  337.    
  338.     initializeSystem() --reset program systerm-oriented settings (in case they've been changed)
  339. end
  340.  
  341.  
  342.  
  343. ------------------------
  344. --        OTHER       --
  345. ------------------------
  346.  
  347. --inserts characters the user types in
  348. function insertText(sText)
  349.     local removeChar = 0
  350.     if not bInsert then --overwrite the next char instead of inserting if settings say so
  351.         removeChar = 1 end
  352.    
  353.     --insert/overwrite text
  354.     tFile[yFile] = string.sub(tFile[yFile], 1, xFile-1) .. sText .. string.sub(tFile[yFile], xFile + removeChar)
  355.    
  356.     shiftCursor(#sText, 0)
  357.     bUnsavedChanges = true
  358. end
  359.  
  360.  
  361.  
  362. ------------------------
  363. --        CORE        --
  364. ------------------------
  365.  
  366. --initializes system settings (this is called at startup and after program execution)
  367. function initializeSystem()
  368.     term.setCursorBlink(true)
  369.     alignCursor()
  370. end
  371.  
  372. --intialization
  373. function intialize()
  374.     load() --load file
  375.    
  376.     --setup screen
  377.     applyBoundrySettings()
  378.     draw()
  379.     initializeSystem()
  380. end
  381.  
  382. --handles user input
  383. function input()
  384.     --wait for key input
  385.     local e, p1 = os.pullEvent()
  386.    
  387.     --reset exit w/ unsaved changes confirmation if the user presses any other input than exit
  388.     if bConfirmingExit == true and e == "key" and p1 == keys.f4 then
  389.         exit()
  390.     else
  391.         bConfirmingExit = false end
  392.    
  393.     if e == "char" then --handles character input
  394.         insertText(p1)
  395.     elseif e == "key" then -- handles non-character input
  396.         --DONT USE F2, F10, F11, ALT, ESC
  397.        
  398.         --functions
  399.         if p1 == keys.f1 then --menu
  400.            
  401.         elseif p1 == keys.f3 then --save
  402.             save()
  403.         elseif p1 == keys.f4 then --exit
  404.             exit()
  405.         elseif p1 == keys.f5 then --run program in editor
  406.             run(false)
  407.         elseif p1 == keys.f6 then --run with arguments
  408.             run(true)
  409.        
  410.         --movement
  411.         elseif p1 == keys.up then --up
  412.             shiftCursor(0, -1)
  413.         elseif p1 == keys.down then --down
  414.             shiftCursor(0, 1)
  415.         elseif p1 == keys.left then --left
  416.             shiftCursor(-1, 0)
  417.         elseif p1 == keys.right then --right
  418.             shiftCursor(1, 0)
  419.         elseif p1 == keys.home then --home
  420.             shiftCursorHome()
  421.         elseif p1 == keys["end"] then --end
  422.             shiftCursorEnd()
  423.         elseif p1 == keys.pageUp then --pasge up
  424.             shiftCursor(0, -(yMax-yMin))
  425.         elseif p1 == keys.pageDown then --page down
  426.             shiftCursor(0, yMax-yMin)
  427.        
  428.         --removing characters
  429.         elseif p1 == keys.backspace then --backspace
  430.             if not (yFile == 1 and xFile == 1) then --if not at the first character in the file
  431.                 if xFile == 1 then --if at the start of a line, concatenate line above with this line
  432.                     shiftCursor(0,-1)
  433.                     shiftCursorEnd()
  434.                    
  435.                     tFile[yFile] = tFile[yFile] .. tFile[yFile+1] --concat lines
  436.                     table.remove(tFile, yFile+1) --remove second line
  437.                 else --otherwise just delete one character
  438.                     shiftCursor(-1,0)
  439.                     tFile[yFile] = string.sub(tFile[yFile], 1, xFile-1) .. string.sub(tFile[yFile], xFile+1)
  440.                 end
  441.                 bUnsavedChanges = true
  442.             end
  443.         elseif p1 == keys.delete then --delete
  444.             if not (yFile == #tFile and xFile == #tFile[yFile] + 1) then --if not at the last character in the file
  445.                 if xFile > #tFile[yFile] then --if cursor is at the end of the line, concatenate with line below
  446.                     tFile[yFile] = tFile[yFile] .. tFile[yFile+1] --concat lines
  447.                     table.remove(tFile, yFile+1) --remove second line
  448.                 else --otherwise just delete one character
  449.                     tFile[yFile] = string.sub(tFile[yFile], 1, xFile-1) .. string.sub(tFile[yFile], xFile+1)
  450.                 end
  451.                 bUnsavedChanges = true
  452.             end
  453.        
  454.         --adding whitespace
  455.         elseif p1 == keys.enter then --enter
  456.             if xFile <= #tFile[yFile] then --if cursor is between characters
  457.                 table.insert(tFile, yFile+1, string.sub(tFile[yFile], xFile)) --move remainder of characters to a new line
  458.                 tFile[yFile] = string.sub(tFile[yFile], 1, xFile-1) --and delete the moved characters from the previous line
  459.             else --cursor at the end of the line
  460.                 table.insert(tFile, yFile+1, "")
  461.             end
  462.             shiftCursorHome()
  463.             shiftCursor(0,1)
  464.             bUnsavedChanges = true
  465.         elseif p1 == keys.tab then --tab
  466.             insertText("\t")
  467.             bUnsavedChanges = true
  468.            
  469.         --insert
  470.         elseif p1 == keys.insert then
  471.             bInsert = not bInsert
  472.        
  473.         end
  474.     end
  475. end
  476.  
  477. --main loop
  478. function mainLoop()
  479.     while bRunning do
  480.         --clear debug info
  481.         sDebugText = ""
  482.        
  483.         input() --get input and respond
  484.        
  485.         --debug info
  486.         local x,y = term.getCursorPos()
  487.         sDebugText = sDebugText .. " Running:" ..tostring(bRunning)
  488.         --sDebugText = sDebugText .. " File:"..xFile..","..yFile .." Scrn:"..x..","..y
  489.        
  490.         scroll() --shift screen to show cursor
  491.         draw() --update screen
  492.     end
  493. end
  494.  
  495. --exit program
  496. function exit()
  497.     if bUnsavedChanges and not bConfirmingExit then --if there are unsaved changes, and this warning hasnt displayed yet
  498.         sMessage = "UNSAVED CHANGES - Press F4 to exit"
  499.         bConfirmingExit = true
  500.     else
  501.         bRunning = false end
  502. end
  503.  
  504. --have the program exit cleanly and tidily
  505. function exitCleanup()
  506.     term.clear()
  507.     term.setCursorPos(1,1)
  508. end
  509.  
  510. intialize()
  511. mainLoop()
  512. exitCleanup()
Advertisement
Add Comment
Please, Sign In to add comment