Advertisement
fishermedders

FishOS /bin/nano

Nov 6th, 2016
160
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 13.85 KB | None | 0 0
  1. -- Get file to edit
  2. local tArgs = { ... }
  3. if #tArgs == 0 then
  4. print( "Usage: nano <path/to/file>" )
  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 bReadOnly then
  46. tMenuItems = { "Exit", "Print" }
  47. else
  48. tMenuItems = { "Save", "Exit", "Print" }
  49. end
  50.  
  51. local sStatus = "Press Ctrl to access menu"
  52. if string.len( sStatus ) > w - 5 then
  53. sStatus = "Press Ctrl for menu"
  54. end
  55.  
  56. local function load( _sPath )
  57. tLines = {}
  58. if fs.exists( _sPath ) then
  59. local file = io.open( _sPath, "r" )
  60. local sLine = file:read()
  61. while sLine do
  62. table.insert( tLines, sLine )
  63. sLine = file:read()
  64. end
  65. file:close()
  66. end
  67.  
  68. if #tLines == 0 then
  69. table.insert( tLines, "" )
  70. end
  71. end
  72.  
  73. local function save( _sPath )
  74. -- Create intervening folder
  75. local sDir = sPath:sub(1, sPath:len() - fs.getName(sPath):len() )
  76. if not fs.exists( sDir ) then
  77. fs.makeDir( sDir )
  78. end
  79.  
  80. -- Save
  81. local file = nil
  82. local function innerSave()
  83. file = fs.open( _sPath, "w" )
  84. if file then
  85. for n, sLine in ipairs( tLines ) do
  86. file.write( sLine .. "\n" )
  87. end
  88. else
  89. error( "Failed to open ".._sPath )
  90. end
  91. end
  92.  
  93. local ok = pcall( innerSave )
  94. if file then
  95. file.close()
  96. end
  97. return ok
  98. end
  99.  
  100. local tKeywords = {
  101. ["and"] = true,
  102. ["break"] = true,
  103. ["do"] = true,
  104. ["else"] = true,
  105. ["elseif"] = true,
  106. ["end"] = true,
  107. ["false"] = true,
  108. ["for"] = true,
  109. ["function"] = true,
  110. ["if"] = true,
  111. ["in"] = true,
  112. ["local"] = true,
  113. ["nil"] = true,
  114. ["not"] = true,
  115. ["or"] = true,
  116. ["repeat"] = true,
  117. ["return"] = true,
  118. ["then"] = true,
  119. ["true"] = true,
  120. ["until"]= true,
  121. ["while"] = true,
  122. }
  123.  
  124. local function tryWrite( sLine, regex, colour )
  125. local match = string.match( sLine, regex )
  126. if match then
  127. if type(colour) == "number" then
  128. term.setTextColour( colour )
  129. else
  130. term.setTextColour( colour(match) )
  131. end
  132. term.write( match )
  133. term.setTextColour( textColour )
  134. return string.sub( sLine, string.len(match) + 1 )
  135. end
  136. return nil
  137. end
  138.  
  139. local function writeHighlighted( sLine )
  140. while string.len(sLine) > 0 do
  141. sLine =
  142. tryWrite( sLine, "^%-%-%[%[.-%]%]", commentColour ) or
  143. tryWrite( sLine, "^%-%-.*", commentColour ) or
  144. tryWrite( sLine, "^\".-[^\\]\"", stringColour ) or
  145. tryWrite( sLine, "^\'.-[^\\]\'", stringColour ) or
  146. tryWrite( sLine, "^%[%[.-%]%]", stringColour ) or
  147. tryWrite( sLine, "^[%w_]+", function( match )
  148. if tKeywords[ match ] then
  149. return keywordColour
  150. end
  151. return textColour
  152. end ) or
  153. tryWrite( sLine, "^[^%w_]", textColour )
  154. end
  155. end
  156.  
  157. local function redrawText()
  158. for y=1,h-1 do
  159. term.setCursorPos( 1 - scrollX, y )
  160. term.clearLine()
  161.  
  162. local sLine = tLines[ y + scrollY ]
  163. if sLine ~= nil then
  164. writeHighlighted( sLine )
  165. end
  166. end
  167. term.setCursorPos( x - scrollX, y - scrollY )
  168. end
  169.  
  170. local function redrawLine(_nY)
  171. local sLine = tLines[_nY]
  172. term.setCursorPos( 1 - scrollX, _nY - scrollY )
  173. term.clearLine()
  174. writeHighlighted( sLine )
  175. term.setCursorPos( x - scrollX, _nY - scrollY )
  176. end
  177.  
  178. local function redrawMenu()
  179. -- Clear line
  180. term.setCursorPos( 1, h )
  181. term.clearLine()
  182.  
  183. -- Draw line numbers
  184. term.setCursorPos( w - string.len( "Ln "..y ) + 1, h )
  185. term.setTextColour( highlightColour )
  186. term.write( "Ln " )
  187. term.setTextColour( textColour )
  188. term.write( y )
  189.  
  190. term.setCursorPos( 1, h )
  191. if bMenu then
  192. -- Draw menu
  193. term.setTextColour( textColour )
  194. for nItem,sItem in pairs( tMenuItems ) do
  195. if nItem == nMenuItem then
  196. term.setTextColour( highlightColour )
  197. term.write( "[" )
  198. term.setTextColour( textColour )
  199. term.write( sItem )
  200. term.setTextColour( highlightColour )
  201. term.write( "]" )
  202. term.setTextColour( textColour )
  203. else
  204. term.write( " "..sItem.." " )
  205. end
  206. end
  207. else
  208. -- Draw status
  209. term.setTextColour( highlightColour )
  210. term.write( sStatus )
  211. term.setTextColour( textColour )
  212. end
  213.  
  214. -- Reset cursor
  215. term.setCursorPos( x - scrollX, y - scrollY )
  216. end
  217.  
  218. local tMenuFuncs = {
  219. Save=function()
  220. if bReadOnly then
  221. sStatus = "Access denied"
  222. else
  223. local ok, err = save( sPath )
  224. if ok then
  225. sStatus="Nano'd to "..sPath
  226. else
  227. sStatus="Error saving to "..sPath
  228. end
  229. end
  230. redrawMenu()
  231. end,
  232. Print=function()
  233. local printer = peripheral.find( "printer" )
  234. if not printer then
  235. sStatus = "No printer attached"
  236. return
  237. end
  238.  
  239. local nPage = 0
  240. local sName = fs.getName( sPath )
  241. if printer.getInkLevel() < 1 then
  242. sStatus = "Printer out of ink"
  243. return
  244. elseif printer.getPaperLevel() < 1 then
  245. sStatus = "Printer out of paper"
  246. return
  247. end
  248.  
  249. local screenTerminal = term.current()
  250. local printerTerminal = {
  251. getCursorPos = printer.getCursorPos,
  252. setCursorPos = printer.setCursorPos,
  253. getSize = printer.getPageSize,
  254. write = printer.write,
  255. }
  256. printerTerminal.scroll = function()
  257. if nPage == 1 then
  258. printer.setPageTitle( sName.." (page "..nPage..")" )
  259. end
  260.  
  261. while not printer.newPage() do
  262. if printer.getInkLevel() < 1 then
  263. sStatus = "Printer out of ink, please refill"
  264. elseif printer.getPaperLevel() < 1 then
  265. sStatus = "Printer out of paper, please refill"
  266. else
  267. sStatus = "Printer output tray full, please empty"
  268. end
  269.  
  270. term.redirect( screenTerminal )
  271. redrawMenu()
  272. term.redirect( printerTerminal )
  273.  
  274. local timer = os.startTimer(0.5)
  275. sleep(0.5)
  276. end
  277.  
  278. nPage = nPage + 1
  279. if nPage == 1 then
  280. printer.setPageTitle( sName )
  281. else
  282. printer.setPageTitle( sName.." (page "..nPage..")" )
  283. end
  284. end
  285.  
  286. bMenu = false
  287. term.redirect( printerTerminal )
  288. local ok, error = pcall( function()
  289. term.scroll()
  290. for n, sLine in ipairs( tLines ) do
  291. print( sLine )
  292. end
  293. end )
  294. term.redirect( screenTerminal )
  295. if not ok then
  296. print( error )
  297. end
  298.  
  299. while not printer.endPage() do
  300. sStatus = "Printer output tray full, please empty"
  301. redrawMenu()
  302. sleep( 0.5 )
  303. end
  304. bMenu = true
  305.  
  306. if nPage > 1 then
  307. sStatus = "Printed "..nPage.." Pages"
  308. else
  309. sStatus = "Printed 1 Page"
  310. end
  311. redrawMenu()
  312. end,
  313. Exit=function()
  314. bRunning = false
  315. end
  316. }
  317.  
  318. local function doMenuItem( _n )
  319. tMenuFuncs[tMenuItems[_n]]()
  320. if bMenu then
  321. bMenu = false
  322. term.setCursorBlink( true )
  323. end
  324. redrawMenu()
  325. end
  326.  
  327. local function setCursor( x, y )
  328. local screenX = x - scrollX
  329. local screenY = y - scrollY
  330.  
  331. local bRedraw = false
  332. if screenX < 1 then
  333. scrollX = x - 1
  334. screenX = 1
  335. bRedraw = true
  336. elseif screenX > w then
  337. scrollX = x - w
  338. screenX = w
  339. bRedraw = true
  340. end
  341.  
  342. if screenY < 1 then
  343. scrollY = y - 1
  344. screenY = 1
  345. bRedraw = true
  346. elseif screenY > h-1 then
  347. scrollY = y - (h-1)
  348. screenY = h-1
  349. bRedraw = true
  350. end
  351.  
  352. if bRedraw then
  353. redrawText()
  354. end
  355. term.setCursorPos( screenX, screenY )
  356.  
  357. -- Statusbar now pertains to menu, it would probably be safe to redraw the menu on every key event.
  358. redrawMenu()
  359. end
  360.  
  361. -- Actual program functionality begins
  362. load(sPath)
  363.  
  364. term.setBackgroundColour( bgColour )
  365. term.clear()
  366. term.setCursorPos(x,y)
  367. term.setCursorBlink( true )
  368.  
  369. redrawText()
  370. redrawMenu()
  371.  
  372. -- Handle input
  373. while bRunning do
  374. local sEvent, param, param2, param3 = os.pullEvent()
  375. if sEvent == "key" then
  376. if param == keys.up then
  377. -- Up
  378. if not bMenu then
  379. if y > 1 then
  380. -- Move cursor up
  381. y = y - 1
  382. x = math.min( x, string.len( tLines[y] ) + 1 )
  383. setCursor( x, y )
  384. end
  385. end
  386. elseif param == keys.down then
  387. -- Down
  388. if not bMenu then
  389. -- Move cursor down
  390. if y < #tLines then
  391. y = y + 1
  392. x = math.min( x, string.len( tLines[y] ) + 1 )
  393. setCursor( x, y )
  394. end
  395. end
  396. elseif param == keys.tab then
  397. -- Tab
  398. if not bMenu and not bReadOnly then
  399. -- Indent line
  400. tLines[y]=" "..tLines[y]
  401. x = x + 2
  402. setCursor( x, y )
  403. redrawLine(y)
  404. end
  405. elseif param == keys.pageUp then
  406. -- Page Up
  407. if not bMenu then
  408. -- Move up a page
  409. if y - (h - 1) >= 1 then
  410. y = y - (h - 1)
  411. else
  412. y = 1
  413. end
  414. x = math.min( x, string.len( tLines[y] ) + 1 )
  415. setCursor( x, y )
  416. end
  417. elseif param == keys.pageDown then
  418. -- Page Down
  419. if not bMenu then
  420. -- Move down a page
  421. if y + (h - 1) <= #tLines then
  422. y = y + (h - 1)
  423. else
  424. y = #tLines
  425. end
  426. x = math.min( x, string.len( tLines[y] ) + 1 )
  427. setCursor( x, y )
  428. end
  429. elseif param == keys.home then
  430. -- Home
  431. if not bMenu then
  432. -- Move cursor to the beginning
  433. x=1
  434. setCursor(x,y)
  435. end
  436. elseif param == keys["end"] then
  437. -- End
  438. if not bMenu then
  439. -- Move cursor to the end
  440. x = string.len( tLines[y] ) + 1
  441. setCursor(x,y)
  442. end
  443. elseif param == keys.left then
  444. -- Left
  445. if not bMenu then
  446. if x > 1 then
  447. -- Move cursor left
  448. x = x - 1
  449. elseif x==1 and y>1 then
  450. x = string.len( tLines[y-1] ) + 1
  451. y = y - 1
  452. end
  453. setCursor( x, y )
  454. else
  455. -- Move menu left
  456. nMenuItem = nMenuItem - 1
  457. if nMenuItem < 1 then
  458. nMenuItem = #tMenuItems
  459. end
  460. redrawMenu()
  461. end
  462. elseif param == keys.right then
  463. -- Right
  464. if not bMenu then
  465. if x < string.len( tLines[y] ) + 1 then
  466. -- Move cursor right
  467. x = x + 1
  468. elseif x==string.len( tLines[y] ) + 1 and y<#tLines then
  469. x = 1
  470. y = y + 1
  471. end
  472. setCursor( x, y )
  473. else
  474. -- Move menu right
  475. nMenuItem = nMenuItem + 1
  476. if nMenuItem > #tMenuItems then
  477. nMenuItem = 1
  478. end
  479. redrawMenu()
  480. end
  481. elseif param == keys.delete then
  482. -- Delete
  483. if not bMenu and not bReadOnly then
  484. if x < string.len( tLines[y] ) + 1 then
  485. local sLine = tLines[y]
  486. tLines[y] = string.sub(sLine,1,x-1) .. string.sub(sLine,x+1)
  487. redrawLine(y)
  488. elseif y<#tLines then
  489. tLines[y] = tLines[y] .. tLines[y+1]
  490. table.remove( tLines, y+1 )
  491. redrawText()
  492. redrawMenu()
  493. end
  494. end
  495. elseif param == keys.backspace then
  496. -- Backspace
  497. if not bMenu and not bReadOnly then
  498. if x > 1 then
  499. -- Remove character
  500. local sLine = tLines[y]
  501. tLines[y] = string.sub(sLine,1,x-2) .. string.sub(sLine,x)
  502. redrawLine(y)
  503.  
  504. x = x - 1
  505. setCursor( x, y )
  506. elseif y > 1 then
  507. -- Remove newline
  508. local sPrevLen = string.len( tLines[y-1] )
  509. tLines[y-1] = tLines[y-1] .. tLines[y]
  510. table.remove( tLines, y )
  511. redrawText()
  512.  
  513. x = sPrevLen + 1
  514. y = y - 1
  515. setCursor( x, y )
  516. end
  517. end
  518. elseif param == keys.enter then
  519. -- Enter
  520. if not bMenu and not bReadOnly then
  521. -- Newline
  522. local sLine = tLines[y]
  523. local _,spaces=string.find(sLine,"^[ ]+")
  524. if not spaces then
  525. spaces=0
  526. end
  527. tLines[y] = string.sub(sLine,1,x-1)
  528. table.insert( tLines, y+1, string.rep(' ',spaces)..string.sub(sLine,x) )
  529. redrawText()
  530.  
  531. x = spaces+1
  532. y = y + 1
  533. setCursor( x, y )
  534. elseif bMenu then
  535. -- Menu selection
  536. doMenuItem( nMenuItem )
  537. end
  538. elseif param == keys.leftCtrl or param == keys.rightCtrl then
  539. -- Menu toggle
  540. bMenu = not bMenu
  541. if bMenu then
  542. term.setCursorBlink( false )
  543. else
  544. term.setCursorBlink( true )
  545. end
  546. redrawMenu()
  547. end
  548.  
  549. elseif sEvent == "char" then
  550. if not bMenu and not bReadOnly then
  551. -- Input text
  552. local sLine = tLines[y]
  553. tLines[y] = string.sub(sLine,1,x-1) .. param .. string.sub(sLine,x)
  554. redrawLine(y)
  555.  
  556. x = x + 1
  557. setCursor( x, y )
  558. elseif bMenu then
  559. -- Select menu items
  560. for n,sMenuItem in ipairs( tMenuItems ) do
  561. if string.lower(string.sub(sMenuItem,1,1)) == string.lower(param) then
  562. doMenuItem( n )
  563. break
  564. end
  565. end
  566. end
  567.  
  568. elseif sEvent == "paste" then
  569. if not bMenu and not bReadOnly then
  570. -- Input text
  571. local sLine = tLines[y]
  572. tLines[y] = string.sub(sLine,1,x-1) .. param .. string.sub(sLine,x)
  573. redrawLine(y)
  574.  
  575. x = x + string.len( param )
  576. setCursor( x, y )
  577. end
  578.  
  579. elseif sEvent == "mouse_click" then
  580. if not bMenu then
  581. if param == 1 then
  582. -- Left click
  583. local cx,cy = param2, param3
  584. if cy < h then
  585. y = math.min( math.max( scrollY + cy, 1 ), #tLines )
  586. x = math.min( math.max( scrollX + cx, 1 ), string.len( tLines[y] ) + 1 )
  587. setCursor( x, y )
  588. end
  589. end
  590. end
  591.  
  592. elseif sEvent == "mouse_scroll" then
  593. if not bMenu then
  594. if param == -1 then
  595. -- Scroll up
  596. if scrollY > 0 then
  597. -- Move cursor up
  598. scrollY = scrollY - 1
  599. redrawText()
  600. end
  601.  
  602. elseif param == 1 then
  603. -- Scroll down
  604. local nMaxScroll = #tLines - (h-1)
  605. if scrollY < nMaxScroll then
  606. -- Move cursor down
  607. scrollY = scrollY + 1
  608. redrawText()
  609. end
  610.  
  611. end
  612. end
  613.  
  614. elseif sEvent == "term_resize" then
  615. w,h = term.getSize()
  616. setCursor( x, y )
  617. redrawMenu()
  618. redrawText()
  619.  
  620. end
  621. end
  622.  
  623. -- Cleanup
  624. term.clear()
  625. term.setCursorBlink( false )
  626. term.setCursorPos( 1, 1 )
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement