Advertisement
ComputerMan123

HouseScript Editor [BETA]

Nov 27th, 2016
85
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 18.77 KB | None | 0 0
  1. -- Get file to edit
  2. local tArgs = { ... }
  3. if #tArgs == 0 then
  4. print( "Usage: edit <path>" )
  5. return
  6. end
  7.  
  8. -- Error checking
  9. local sPath = shell.resolve( tArgs[1] )
  10. local bReadOnly = fs.isReadOnly( sPath )
  11. if fs.exists( sPath ) and fs.isDir( sPath ) then
  12. print( "Cannot edit a directory." )
  13. return
  14. end
  15.  
  16. local x,y = 1,1
  17. local w,h = term.getSize()
  18. local scrollX, scrollY = 0,0
  19.  
  20. local tLines = {}
  21. local bRunning = true
  22.  
  23. -- Colours
  24. local highlightColour, keywordColour, commentColour, textColour, bgColour
  25. if term.isColour() then
  26. bgColour = colours.black
  27. textColour = colours.white
  28. highlightColour = colours.yellow
  29. keywordColour = colours.yellow
  30. commentColour = colours.green
  31. stringColour = colours.red
  32. else
  33. bgColour = colours.black
  34. textColour = colours.white
  35. highlightColour = colours.white
  36. keywordColour = colours.white
  37. commentColour = colours.white
  38. stringColour = colours.white
  39. end
  40.  
  41. -- Menus
  42. local bMenu = false
  43. local nMenuItem = 1
  44. local tMenuItems = {}
  45. if not bReadOnly then
  46. table.insert( tMenuItems, "Save" )
  47. end
  48. if shell.openTab then
  49. table.insert( tMenuItems, "Run" )
  50. end
  51. if peripheral.find( "printer" ) then
  52. table.insert( tMenuItems, "Print" )
  53. end
  54. table.insert( tMenuItems, "Exit" )
  55.  
  56. local sStatus = "Press Ctrl to access menu"
  57. if string.len( sStatus ) > w - 5 then
  58. sStatus = "Press Ctrl for menu"
  59. end
  60.  
  61. local function load( _sPath )
  62. tLines = {}
  63. if fs.exists( _sPath ) then
  64. local file = io.open( _sPath, "r" )
  65. local sLine = file:read()
  66. while sLine do
  67. table.insert( tLines, sLine )
  68. sLine = file:read()
  69. end
  70. file:close()
  71. end
  72.  
  73. if #tLines == 0 then
  74. table.insert( tLines, "" )
  75. end
  76. end
  77.  
  78. local function save( _sPath )
  79. -- Create intervening folder
  80. local sDir = _sPath:sub(1, _sPath:len() - fs.getName(_sPath):len() )
  81. if not fs.exists( sDir ) then
  82. fs.makeDir( sDir )
  83. end
  84.  
  85. -- Save
  86. local file = nil
  87. local function innerSave()
  88. file = fs.open( _sPath, "w" )
  89. if file then
  90. for n, sLine in ipairs( tLines ) do
  91. file.write( sLine .. "\n" )
  92. end
  93. else
  94. error( "Failed to open ".._sPath )
  95. end
  96. end
  97.  
  98. local ok, err = pcall( innerSave )
  99. if file then
  100. file.close()
  101. end
  102. return ok, err
  103. end
  104.  
  105. local tKeywords = {
  106. ["and"] = true,
  107. ["break"] = true,
  108. ["do"] = true,
  109. ["else"] = true,
  110. ["elseif"] = true,
  111. ["end"] = true,
  112. ["false"] = true,
  113. ["for"] = true,
  114. ["function"] = true,
  115. ["if"] = true,
  116. ["in"] = true,
  117. ["str"] = true,
  118. ["num"] = true,
  119. ["nil"] = true,
  120. ["not"] = true,
  121. ["or"] = true,
  122. ["repeat"] = true,
  123. ["return"] = true,
  124. ["then"] = true,
  125. ["true"] = true,
  126. ["until"]= true,
  127. ["while"] = true,
  128. }
  129.  
  130. local highlight = {
  131. ["sys.print"] = true,
  132. }
  133.  
  134. local function tryWrite( sLine, regex, colour )
  135. local match = string.match( sLine, regex )
  136. if match then
  137. if type(colour) == "number" then
  138. term.setTextColour( colour )
  139. else
  140. term.setTextColour( colour(match) )
  141. end
  142. term.write( match )
  143. term.setTextColour( textColour )
  144. return string.sub( sLine, string.len(match) + 1 )
  145. end
  146. return nil
  147. end
  148.  
  149. local function writeHighlighted( sLine )
  150. while string.len(sLine) > 0 do
  151. sLine =
  152. tryWrite( sLine, "^%-%-%[%[.-%]%]", commentColour ) or
  153. tryWrite( sLine, "^%-%-.*", commentColour ) or
  154. tryWrite( sLine, "^\".-[^\\]\"", stringColour ) or
  155. tryWrite( sLine, "^\'.-[^\\]\'", stringColour ) or
  156. tryWrite( sLine, "^%[%[.-%]%]", stringColour ) or
  157. tryWrite( sLine, "^[%w_]+", function( match )
  158. if tKeywords[ match ] then
  159. return keywordColour
  160. end
  161. return textColour
  162. end ) or
  163. tryWrite( sLine, "^[^%w_]", textColour )
  164. end
  165. while string.len(sLine) > 0 do
  166. sLine =
  167. tryWrite( sLine, "^%-%-%[%[.-%]%]", commentColour ) or
  168. tryWrite( sLine, "^%-%-.*", commentColour ) or
  169. tryWrite( sLine, "^\".-[^\\]\"", stringColour ) or
  170. tryWrite( sLine, "^\'.-[^\\]\'", stringColour ) or
  171. tryWrite( sLine, "^%[%[.-%]%]", stringColour ) or
  172. tryWrite( sLine, "^[%w_]+", function( match )
  173. if highlight[ match ] then
  174. return keywordColour
  175. end
  176. return textColour
  177. end ) or
  178. tryWrite( sLine, "^[^%w_]", color.pink )
  179. end
  180. end
  181.  
  182. local tCompletions
  183. local nCompletion
  184.  
  185. local tCompleteEnv = _ENV
  186. local function complete( sLine )
  187. local nStartPos = string.find( sLine, "[a-zA-Z0-9_%.]+$" )
  188. if nStartPos then
  189. sLine = string.sub( sLine, nStartPos )
  190. end
  191. if #sLine > 0 then
  192. return textutils.complete( sLine, tCompleteEnv )
  193. end
  194. return nil
  195. end
  196.  
  197. local function recomplete()
  198. local sLine = tLines[y]
  199. if not bMenu and not bReadOnly and x == string.len(sLine) + 1 then
  200. tCompletions = complete( sLine )
  201. if tCompletions and #tCompletions > 0 then
  202. nCompletion = 1
  203. else
  204. nCompletion = nil
  205. end
  206. else
  207. tCompletions = nil
  208. nCompletion = nil
  209. end
  210. end
  211.  
  212. local function writeCompletion( sLine )
  213. if nCompletion then
  214. local sCompletion = tCompletions[ nCompletion ]
  215. term.setTextColor( colours.white )
  216. term.setBackgroundColor( colours.grey )
  217. term.write( sCompletion )
  218. term.setTextColor( textColour )
  219. term.setBackgroundColor( bgColour )
  220. end
  221. end
  222.  
  223. local function redrawText()
  224. local cursorX, cursorY = x, y
  225. for y=1,h-1 do
  226. term.setCursorPos( 1 - scrollX, y )
  227. term.clearLine()
  228.  
  229. local sLine = tLines[ y + scrollY ]
  230. if sLine ~= nil then
  231. writeHighlighted( sLine )
  232. if cursorY == y and cursorX == #sLine + 1 then
  233. writeCompletion()
  234. end
  235. end
  236. end
  237. term.setCursorPos( x - scrollX, y - scrollY )
  238. end
  239.  
  240. local function redrawLine(_nY)
  241. local sLine = tLines[_nY]
  242. if sLine then
  243. term.setCursorPos( 1 - scrollX, _nY - scrollY )
  244. term.clearLine()
  245. writeHighlighted( sLine )
  246. if _nY == y and x == #sLine + 1 then
  247. writeCompletion()
  248. end
  249. term.setCursorPos( x - scrollX, _nY - scrollY )
  250. end
  251. end
  252.  
  253. local function redrawMenu()
  254. -- Clear line
  255. term.setCursorPos( 1, h )
  256. term.clearLine()
  257.  
  258. -- Draw line numbers
  259. term.setCursorPos( w - string.len( "Ln "..y ) + 1, h )
  260. term.setTextColour( highlightColour )
  261. term.write( "Ln " )
  262. term.setTextColour( textColour )
  263. term.write( y )
  264.  
  265. term.setCursorPos( 1, h )
  266. if bMenu then
  267. -- Draw menu
  268. term.setTextColour( textColour )
  269. for nItem,sItem in pairs( tMenuItems ) do
  270. if nItem == nMenuItem then
  271. term.setTextColour( highlightColour )
  272. term.write( "[" )
  273. term.setTextColour( textColour )
  274. term.write( sItem )
  275. term.setTextColour( highlightColour )
  276. term.write( "]" )
  277. term.setTextColour( textColour )
  278. else
  279. term.write( " "..sItem.." " )
  280. end
  281. end
  282. else
  283. -- Draw status
  284. term.setTextColour( highlightColour )
  285. term.write( sStatus )
  286. term.setTextColour( textColour )
  287. end
  288.  
  289. -- Reset cursor
  290. term.setCursorPos( x - scrollX, y - scrollY )
  291. end
  292.  
  293. local tMenuFuncs = {
  294. Save = function()
  295. if bReadOnly then
  296. sStatus = "Access denied"
  297. else
  298. local ok, err = save( sPath )
  299. if ok then
  300. sStatus="Saved to "..sPath
  301. else
  302. sStatus="Error saving to "..sPath
  303. end
  304. end
  305. redrawMenu()
  306. end,
  307. Print = function()
  308. local printer = peripheral.find( "printer" )
  309. if not printer then
  310. sStatus = "No printer attached"
  311. return
  312. end
  313.  
  314. local nPage = 0
  315. local sName = fs.getName( sPath )
  316. if printer.getInkLevel() < 1 then
  317. sStatus = "Printer out of ink"
  318. return
  319. elseif printer.getPaperLevel() < 1 then
  320. sStatus = "Printer out of paper"
  321. return
  322. end
  323.  
  324. local screenTerminal = term.current()
  325. local printerTerminal = {
  326. getCursorPos = printer.getCursorPos,
  327. setCursorPos = printer.setCursorPos,
  328. getSize = printer.getPageSize,
  329. write = printer.write,
  330. }
  331. printerTerminal.scroll = function()
  332. if nPage == 1 then
  333. printer.setPageTitle( sName.." (page "..nPage..")" )
  334. end
  335.  
  336. while not printer.newPage() do
  337. if printer.getInkLevel() < 1 then
  338. sStatus = "Printer out of ink, please refill"
  339. elseif printer.getPaperLevel() < 1 then
  340. sStatus = "Printer out of paper, please refill"
  341. else
  342. sStatus = "Printer output tray full, please empty"
  343. end
  344.  
  345. term.redirect( screenTerminal )
  346. redrawMenu()
  347. term.redirect( printerTerminal )
  348.  
  349. local timer = os.startTimer(0.5)
  350. sleep(0.5)
  351. end
  352.  
  353. nPage = nPage + 1
  354. if nPage == 1 then
  355. printer.setPageTitle( sName )
  356. else
  357. printer.setPageTitle( sName.." (page "..nPage..")" )
  358. end
  359. end
  360.  
  361. bMenu = false
  362. term.redirect( printerTerminal )
  363. local ok, error = pcall( function()
  364. term.scroll()
  365. for n, sLine in ipairs( tLines ) do
  366. print( sLine )
  367. end
  368. end )
  369. term.redirect( screenTerminal )
  370. if not ok then
  371. print( error )
  372. end
  373.  
  374. while not printer.endPage() do
  375. sStatus = "Printer output tray full, please empty"
  376. redrawMenu()
  377. sleep( 0.5 )
  378. end
  379. bMenu = true
  380.  
  381. if nPage > 1 then
  382. sStatus = "Printed "..nPage.." Pages"
  383. else
  384. sStatus = "Printed 1 Page"
  385. end
  386. redrawMenu()
  387. end,
  388. Exit = function()
  389. bRunning = false
  390. end,
  391. Run = function()
  392. local sTempPath = "/.temp"
  393. local ok, err = save( sTempPath )
  394. if ok then
  395. local nTask = shell.openTab( sTempPath )
  396. if nTask then
  397. shell.switchTab( nTask )
  398. else
  399. sStatus="Error starting Task"
  400. end
  401. fs.delete( sTempPath )
  402. else
  403. sStatus="Error saving to "..sTempPath
  404. end
  405. redrawMenu()
  406. end
  407. }
  408.  
  409. local function doMenuItem( _n )
  410. tMenuFuncs[tMenuItems[_n]]()
  411. if bMenu then
  412. bMenu = false
  413. term.setCursorBlink( true )
  414. end
  415. redrawMenu()
  416. end
  417.  
  418. local function setCursor( newX, newY )
  419. local oldX, oldY = x, y
  420. x, y = newX, newY
  421. local screenX = x - scrollX
  422. local screenY = y - scrollY
  423.  
  424. local bRedraw = false
  425. if screenX < 1 then
  426. scrollX = x - 1
  427. screenX = 1
  428. bRedraw = true
  429. elseif screenX > w then
  430. scrollX = x - w
  431. screenX = w
  432. bRedraw = true
  433. end
  434.  
  435. if screenY < 1 then
  436. scrollY = y - 1
  437. screenY = 1
  438. bRedraw = true
  439. elseif screenY > h-1 then
  440. scrollY = y - (h-1)
  441. screenY = h-1
  442. bRedraw = true
  443. end
  444.  
  445. recomplete()
  446. if bRedraw then
  447. redrawText()
  448. elseif y ~= oldY then
  449. redrawLine( oldY )
  450. redrawLine( y )
  451. else
  452. redrawLine( y )
  453. end
  454. term.setCursorPos( screenX, screenY )
  455.  
  456. redrawMenu()
  457. end
  458.  
  459. -- Actual program functionality begins
  460. load(sPath)
  461.  
  462. term.setBackgroundColour( bgColour )
  463. term.clear()
  464. term.setCursorPos(x,y)
  465. term.setCursorBlink( true )
  466.  
  467. recomplete()
  468. redrawText()
  469. redrawMenu()
  470.  
  471. local function acceptCompletion()
  472. if nCompletion then
  473. -- Find the common prefix of all the other suggestions which start with the same letter as the current one
  474. local sCompletion = tCompletions[ nCompletion ]
  475. local sFirstLetter = string.sub( sCompletion, 1, 1 )
  476. local sCommonPrefix = sCompletion
  477. for n=1,#tCompletions do
  478. local sResult = tCompletions[n]
  479. if n ~= nCompletion and string.find( sResult, sFirstLetter, 1, true ) == 1 then
  480. while #sCommonPrefix > 1 do
  481. if string.find( sResult, sCommonPrefix, 1, true ) == 1 then
  482. break
  483. else
  484. sCommonPrefix = string.sub( sCommonPrefix, 1, #sCommonPrefix - 1 )
  485. end
  486. end
  487. end
  488. end
  489.  
  490. -- Append this string
  491. tLines[y] = tLines[y] .. sCommonPrefix
  492. setCursor( x + string.len( sCommonPrefix ), y )
  493. end
  494. end
  495.  
  496. -- Handle input
  497. while bRunning do
  498. local sEvent, param, param2, param3 = os.pullEvent()
  499. if sEvent == "key" then
  500. local oldX, oldY = x, y
  501. if param == keys.up then
  502. -- Up
  503. if not bMenu then
  504. if nCompletion then
  505. -- Cycle completions
  506. nCompletion = nCompletion - 1
  507. if nCompletion < 1 then
  508. nCompletion = #tCompletions
  509. end
  510. redrawLine(y)
  511.  
  512. elseif y > 1 then
  513. -- Move cursor up
  514. setCursor(
  515. math.min( x, string.len( tLines[y - 1] ) + 1 ),
  516. y - 1
  517. )
  518. end
  519. end
  520.  
  521. elseif param == keys.down then
  522. -- Down
  523. if not bMenu then
  524. -- Move cursor down
  525. if nCompletion then
  526. -- Cycle completions
  527. nCompletion = nCompletion + 1
  528. if nCompletion > #tCompletions then
  529. nCompletion = 1
  530. end
  531. redrawLine(y)
  532.  
  533. elseif y < #tLines then
  534. -- Move cursor down
  535. setCursor(
  536. math.min( x, string.len( tLines[y + 1] ) + 1 ),
  537. y + 1
  538. )
  539. end
  540. end
  541.  
  542. elseif param == keys.tab then
  543. -- Tab
  544. if not bMenu and not bReadOnly then
  545. if nCompletion and x == string.len(tLines[y]) + 1 then
  546. -- Accept autocomplete
  547. acceptCompletion()
  548. else
  549. -- Indent line
  550. local sLine = tLines[y]
  551. tLines[y] = string.sub(sLine,1,x-1) .. " " .. string.sub(sLine,x)
  552. setCursor( x + 2, y )
  553. end
  554. end
  555.  
  556. elseif param == keys.pageUp then
  557. -- Page Up
  558. if not bMenu then
  559. -- Move up a page
  560. local newY
  561. if y - (h - 1) >= 1 then
  562. newY = y - (h - 1)
  563. else
  564. newY = 1
  565. end
  566. setCursor(
  567. math.min( x, string.len( tLines[newY] ) + 1 ),
  568. newY
  569. )
  570. end
  571.  
  572. elseif param == keys.pageDown then
  573. -- Page Down
  574. if not bMenu then
  575. -- Move down a page
  576. local newY
  577. if y + (h - 1) <= #tLines then
  578. newY = y + (h - 1)
  579. else
  580. newY = #tLines
  581. end
  582. local newX = math.min( x, string.len( tLines[newY] ) + 1 )
  583. setCursor( newX, newY )
  584. end
  585.  
  586. elseif param == keys.home then
  587. -- Home
  588. if not bMenu then
  589. -- Move cursor to the beginning
  590. if x > 1 then
  591. setCursor(1,y)
  592. end
  593. end
  594.  
  595. elseif param == keys["end"] then
  596. -- End
  597. if not bMenu then
  598. -- Move cursor to the end
  599. local nLimit = string.len( tLines[y] ) + 1
  600. if x < nLimit then
  601. setCursor( nLimit, y )
  602. end
  603. end
  604.  
  605. elseif param == keys.left then
  606. -- Left
  607. if not bMenu then
  608. if x > 1 then
  609. -- Move cursor left
  610. setCursor( x - 1, y )
  611. elseif x==1 and y>1 then
  612. setCursor( string.len( tLines[y-1] ) + 1, y - 1 )
  613. end
  614. else
  615. -- Move menu left
  616. nMenuItem = nMenuItem - 1
  617. if nMenuItem < 1 then
  618. nMenuItem = #tMenuItems
  619. end
  620. redrawMenu()
  621. end
  622.  
  623. elseif param == keys.right then
  624. -- Right
  625. if not bMenu then
  626. local nLimit = string.len( tLines[y] ) + 1
  627. if x < nLimit then
  628. -- Move cursor right
  629. setCursor( x + 1, y )
  630. elseif nCompletion and x == string.len(tLines[y]) + 1 then
  631. -- Accept autocomplete
  632. acceptCompletion()
  633. elseif x==nLimit and y<#tLines then
  634. -- Go to next line
  635. setCursor( 1, y + 1 )
  636. end
  637. else
  638. -- Move menu right
  639. nMenuItem = nMenuItem + 1
  640. if nMenuItem > #tMenuItems then
  641. nMenuItem = 1
  642. end
  643. redrawMenu()
  644. end
  645.  
  646. elseif param == keys.delete then
  647. -- Delete
  648. if not bMenu and not bReadOnly then
  649. local nLimit = string.len( tLines[y] ) + 1
  650. if x < nLimit then
  651. local sLine = tLines[y]
  652. tLines[y] = string.sub(sLine,1,x-1) .. string.sub(sLine,x+1)
  653. recomplete()
  654. redrawLine(y)
  655. elseif y<#tLines then
  656. tLines[y] = tLines[y] .. tLines[y+1]
  657. table.remove( tLines, y+1 )
  658. recomplete()
  659. redrawText()
  660. end
  661. end
  662.  
  663. elseif param == keys.backspace then
  664. -- Backspace
  665. if not bMenu and not bReadOnly then
  666. if x > 1 then
  667. -- Remove character
  668. local sLine = tLines[y]
  669. tLines[y] = string.sub(sLine,1,x-2) .. string.sub(sLine,x)
  670. setCursor( x - 1, y )
  671. elseif y > 1 then
  672. -- Remove newline
  673. local sPrevLen = string.len( tLines[y-1] )
  674. tLines[y-1] = tLines[y-1] .. tLines[y]
  675. table.remove( tLines, y )
  676. setCursor( sPrevLen + 1, y - 1 )
  677. redrawText()
  678. end
  679. end
  680.  
  681. elseif param == keys.enter then
  682. -- Enter
  683. if not bMenu and not bReadOnly then
  684. -- Newline
  685. local sLine = tLines[y]
  686. local _,spaces=string.find(sLine,"^[ ]+")
  687. if not spaces then
  688. spaces=0
  689. end
  690. tLines[y] = string.sub(sLine,1,x-1)
  691. table.insert( tLines, y+1, string.rep(' ',spaces)..string.sub(sLine,x) )
  692. setCursor( spaces + 1, y + 1 )
  693. redrawText()
  694.  
  695. elseif bMenu then
  696. -- Menu selection
  697. doMenuItem( nMenuItem )
  698.  
  699. end
  700.  
  701. elseif param == keys.leftCtrl or param == keys.rightCtrl or param == keys.rightAlt then
  702. -- Menu toggle
  703. bMenu = not bMenu
  704. if bMenu then
  705. term.setCursorBlink( false )
  706. else
  707. term.setCursorBlink( true )
  708. end
  709. redrawMenu()
  710.  
  711. end
  712.  
  713. elseif sEvent == "char" then
  714. if not bMenu and not bReadOnly then
  715. -- Input text
  716. local sLine = tLines[y]
  717. tLines[y] = string.sub(sLine,1,x-1) .. param .. string.sub(sLine,x)
  718. setCursor( x + 1, y )
  719.  
  720. elseif bMenu then
  721. -- Select menu items
  722. for n,sMenuItem in ipairs( tMenuItems ) do
  723. if string.lower(string.sub(sMenuItem,1,1)) == string.lower(param) then
  724. doMenuItem( n )
  725. break
  726. end
  727. end
  728. end
  729.  
  730. elseif sEvent == "paste" then
  731. if not bMenu and not bReadOnly then
  732. -- Input text
  733. local sLine = tLines[y]
  734. tLines[y] = string.sub(sLine,1,x-1) .. param .. string.sub(sLine,x)
  735. setCursor( x + string.len( param ), y )
  736. end
  737.  
  738. elseif sEvent == "mouse_click" then
  739. if not bMenu then
  740. if param == 1 then
  741. -- Left click
  742. local cx,cy = param2, param3
  743. if cy < h then
  744. local newY = math.min( math.max( scrollY + cy, 1 ), #tLines )
  745. local newX = math.min( math.max( scrollX + cx, 1 ), string.len( tLines[newY] ) + 1 )
  746. setCursor( newX, newY )
  747. end
  748. end
  749. end
  750.  
  751. elseif sEvent == "mouse_scroll" then
  752. if not bMenu then
  753. if param == -1 then
  754. -- Scroll up
  755. if scrollY > 0 then
  756. -- Move cursor up
  757. scrollY = scrollY - 1
  758. redrawText()
  759. end
  760.  
  761. elseif param == 1 then
  762. -- Scroll down
  763. local nMaxScroll = #tLines - (h-1)
  764. if scrollY < nMaxScroll then
  765. -- Move cursor down
  766. scrollY = scrollY + 1
  767. redrawText()
  768. end
  769.  
  770. end
  771. end
  772.  
  773. elseif sEvent == "term_resize" then
  774. w,h = term.getSize()
  775. setCursor( x, y )
  776. redrawMenu()
  777. redrawText()
  778.  
  779. end
  780. end
  781.  
  782. -- Cleanup
  783. term.clear()
  784. term.setCursorBlink( false )
  785. term.setCursorPos( 1, 1 )
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement