Sirshark10

EditMod

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