Advertisement
KinoftheFlames

Untitled

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