mirevall

roedit

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