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