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