mirror of
				https://github.com/LDDestroier/CC/
				synced 2025-10-30 23:12:59 +00:00 
			
		
		
		
	Improved cursor navigation with keyboard
Moving/deleting with CTRL+Arrows and CTRL+Backspace/Delete should be more accurate now. Also, you can't make new cursors from within selected areas now.
This commit is contained in:
		
							
								
								
									
										213
									
								
								eldit.lua
									
									
									
									
									
								
							
							
						
						
									
										213
									
								
								eldit.lua
									
									
									
									
									
								
							| @@ -1,6 +1,18 @@ | ||||
| -- Eldit (still being made) | ||||
| -- by LDDestroier | ||||
| -- wget https://raw.githubusercontent.com/LDDestroier/CC/master/eldit.lua | ||||
| --[[ | ||||
| 	Eldit (still being made) | ||||
| 	 by LDDestroier | ||||
| 	wget https://raw.githubusercontent.com/LDDestroier/CC/master/eldit.lua | ||||
|  | ||||
| TO DO: | ||||
|  - MAJOR: Merge selections that intersect | ||||
|  - MAJOR: Allow selecting with Shift + ArrowKeys | ||||
|  - MAJOR: Fix deleting multiple selections (MUST delete selections from bottom to top) | ||||
|  - MAJOR: Add syntax highlighting | ||||
|  - Add more keyboard shortcuts | ||||
|  - Add help menu | ||||
|  - Eventually add simultaneous peer editing | ||||
|  | ||||
| --]] | ||||
|  | ||||
| local scr_x, scr_y = term.getSize() | ||||
|  | ||||
| @@ -27,9 +39,15 @@ eldit.size = { | ||||
| 	height = scr_y	-- vertical size | ||||
| } | ||||
|  | ||||
| -- made-up keys for easier use of both left and right modifier keys | ||||
| keys.shift = 127 | ||||
| keys.alt = 128 | ||||
| keys.ctrl = 129 | ||||
|  | ||||
| config.showLineNumberIndicator = false | ||||
| config.showWhitespace = true | ||||
| config.showTrailingSpace = true | ||||
| config.findExtension = true | ||||
|  | ||||
| -- minor optimizations, I think | ||||
| local concatTable = table.concat | ||||
| @@ -77,7 +95,28 @@ end | ||||
|  | ||||
| local argList = interpretArgs({...}, argData) | ||||
|  | ||||
| eldit.filename = argList[1] | ||||
| eldit.filename = argList[1] and shell.resolve(argList[1]) | ||||
|  | ||||
| if eldit.filename then | ||||
| 	if fs.isDir(eldit.filename) then | ||||
| 		error("Cannot edit a directory.", 0) | ||||
| 	end | ||||
|  | ||||
| 	if config.findExtension then | ||||
| 		if not fs.exists(eldit.filename) then | ||||
| 			local m | ||||
| 			local d = fs.list(fs.getDir(eldit.filename)) | ||||
| 			for i = 1, #d do | ||||
| 				m = d[i]:match(fs.getName(eldit.filename) .. "%....$") | ||||
| 				if m then | ||||
| 					eldit.filename = fs.combine(fs.getDir(eldit.filename), m) | ||||
| 					break | ||||
| 				end | ||||
| 			end | ||||
| 		end | ||||
| 	end | ||||
| end | ||||
|  | ||||
| eldit.cursors = {{ | ||||
| 	x = 1, | ||||
| 	y = math.max(1, argList["-l"] or 1), | ||||
| @@ -358,7 +397,7 @@ prompt = function(prebuffer, precy, maxY, _eldit) | ||||
|  | ||||
| 				if checkIfCursor(cx, cy) and isCursorBlink then | ||||
| 					if isInsert then | ||||
| 						cTxt, cBg = "0", "f" | ||||
| 						cTxt, cBg = "8", "0" | ||||
| 					else | ||||
| 						cTxt, cBg = "f", "8" | ||||
| 					end | ||||
| @@ -496,10 +535,17 @@ prompt = function(prebuffer, precy, maxY, _eldit) | ||||
| 		local xAdjList = {} | ||||
| 		local yAdj = 0 | ||||
| 		sortCursors() | ||||
|  | ||||
| 		local rowBuff	-- represents the buffer row at the current cursor's Y | ||||
| 		local startOnInterruptable | ||||
|  | ||||
| 		for id,cur in pairs(eldit.cursors) do | ||||
| 			cx = _cx or cur.x - (xAdjList[_cy or cur.y] or 0) | ||||
| 			cy = _cy or cur.y - yAdj | ||||
|  | ||||
| 			rowBuff = eldit.buffer[cy] or {} | ||||
| 			startOnInterruptable = interruptable[rowBuff[cx]] or (not rowBuff[cx]) | ||||
|  | ||||
| 			if mode == "single" or (direction == "forward" and cx == #eldit.buffer[cy] or (direction == "backward" and cx == 1)) then | ||||
| 				if direction == "forward" then | ||||
| 					if cx < #eldit.buffer[cy] then | ||||
| @@ -540,17 +586,43 @@ prompt = function(prebuffer, precy, maxY, _eldit) | ||||
| 			elseif mode == "word" then | ||||
| 				local pos = cx | ||||
| 				if direction == "forward" then | ||||
| 					repeat | ||||
| 					while true do | ||||
| 						pos = pos + 1 | ||||
| 					until interruptable[eldit.buffer[cy][pos]] or pos >= #eldit.buffer[cy] | ||||
| 						if startOnInterruptable then | ||||
| 							if (not interruptable[rowBuff[pos]]) or (not rowBuff[pos]) then | ||||
| 								startOnInterruptable = false | ||||
| 							end | ||||
| 						else | ||||
| 							if interruptable[rowBuff[pos]] or (not rowBuff[pos]) then | ||||
| 								break | ||||
| 							end | ||||
| 						end | ||||
|  | ||||
| 						if (pos + 1) < 0 or (pos + 1) > #rowBuff + 1 then | ||||
| 							break | ||||
| 						end | ||||
| 					end | ||||
| 					for i = pos, cx, -1 do | ||||
| 						xAdjList[cy] = (xAdjList[cy] or 0) + 1 | ||||
| 						table.remove(eldit.buffer[cy], i) | ||||
| 					end | ||||
| 				else | ||||
| 					repeat | ||||
| 					while true do | ||||
| 						pos = pos - 1 | ||||
| 					until interruptable[eldit.buffer[cy][pos]] or pos <= 1 | ||||
| 						if startOnInterruptable then | ||||
| 							if (not interruptable[rowBuff[pos]]) or (not rowBuff[pos]) then | ||||
| 								startOnInterruptable = false | ||||
| 							end | ||||
| 						else | ||||
| 							if interruptable[rowBuff[pos]] or (not rowBuff[pos]) then | ||||
| 								break | ||||
| 							end | ||||
| 						end | ||||
|  | ||||
| 						if (pos - 1) < 0 or (pos - 1) > #rowBuff + 1 then | ||||
| 							break | ||||
| 						end | ||||
| 					end | ||||
| 					pos = math.max(1, pos) | ||||
| 					for i = cx - 1, pos, -1 do | ||||
| 						table.remove(eldit.buffer[cy], i) | ||||
| @@ -642,14 +714,38 @@ prompt = function(prebuffer, precy, maxY, _eldit) | ||||
| 	end | ||||
|  | ||||
| 	-- moves the cursor by (xmod, ymod), and fixes its position if it's set to an invalid one | ||||
| 	local adjustCursor = function(_xmod, _ymod, setLastX, mode, doNotDelSelections) | ||||
| 	local adjustCursor = function(_xmod, _ymod, setLastX, mode, doNotDelSelections, adjustSelections, doNotTouchScroll) | ||||
| 		local step = (_xmod / math.abs(_xmod)) | ||||
| 		local rowBuff	-- represents the buffer row at the current cursor's Y | ||||
| 		local startOnInterruptable | ||||
|  | ||||
| 		local origCX, origCY | ||||
|  | ||||
| 		for id,cur in pairs(eldit.cursors) do | ||||
| 			origCX, origCY = cur.x, cur.y | ||||
| 			rowBuff = eldit.buffer[cur.y] or {} | ||||
| 			startOnInterruptable = interruptable[rowBuff[cur.x + step]] | ||||
| 			if mode == "word" then | ||||
| 				xmod = (_xmod / math.abs(_xmod)) | ||||
| 				xmod = step | ||||
| 				ymod = 0 | ||||
| 				repeat | ||||
| 					xmod = xmod + (_xmod / math.abs(_xmod)) | ||||
| 				until interruptable[eldit.buffer[cur.y][cur.x + xmod]] or cur.x + xmod >= #eldit.buffer[cur.y] or cur.x + xmod <= 1 | ||||
| 				while true do | ||||
| 					xmod = xmod + step | ||||
| 					if math.abs(xmod) > math.abs(step) then | ||||
| 						if startOnInterruptable then | ||||
| 							if (not interruptable[rowBuff[cur.x + xmod]]) or (not rowBuff[cur.x + xmod]) then | ||||
| 								startOnInterruptable = false | ||||
| 							end | ||||
| 						else | ||||
| 							if interruptable[rowBuff[cur.x + xmod]] or (not rowBuff[cur.x + xmod]) then | ||||
| 								break | ||||
| 							end | ||||
| 						end | ||||
| 					end | ||||
|  | ||||
| 					if (cur.x + xmod + step) < 0 or (cur.x + xmod + step) > #rowBuff + 1 then | ||||
| 						break | ||||
| 					end | ||||
| 				end | ||||
| 				xmod = xmod - math.min(0, math.max(xmod, -1)) | ||||
| 			else | ||||
| 				xmod = _xmod | ||||
| @@ -668,7 +764,8 @@ prompt = function(prebuffer, precy, maxY, _eldit) | ||||
| 						cur.y = cur.y - 1 | ||||
| 						cur.x = cur.x + #eldit.buffer[cur.y] + 1 | ||||
| 					elseif cur.x > #eldit.buffer[cur.y] + 1 and cur.y < #eldit.buffer then | ||||
| 						cur.x = cur.x - #eldit.buffer[cur.y] - 1 | ||||
| --						cur.x = cur.x - #eldit.buffer[cur.y] - 1 | ||||
| 						cur.x = 1 | ||||
| 						cur.y = cur.y + 1 | ||||
| 					end | ||||
| 				until (cur.x >= 1 and cur.x <= #eldit.buffer[cur.y] + 1) or ((cur.y == 1 and xmod < 0) or (cur.y == #eldit.buffer and xmod > 0)) | ||||
| @@ -684,13 +781,19 @@ prompt = function(prebuffer, precy, maxY, _eldit) | ||||
| 				cur.y = math.max(1, math.min(cur.y, #eldit.buffer)) | ||||
| 				cur.x = math.max(1, math.min(cur.x, #eldit.buffer[cur.y] + 1)) | ||||
| 			end | ||||
|  | ||||
| 			if adjustSelections then | ||||
| 				for sid, sel in pairs(eldit.selections) do | ||||
|  | ||||
| 				end | ||||
| 			end | ||||
| 		end | ||||
| 		removeRedundantCursors() | ||||
| 		if (not keysDown[keys.leftCtrl]) and not (xmod == 0 and ymod == 0) and not doNotDelSelections then | ||||
| 		if (not keysDown[keys.ctrl]) and not (xmod == 0 and ymod == 0) and not doNotDelSelections then | ||||
| 			eldit.selections = {} | ||||
| 			isSelecting = false | ||||
| 		end | ||||
| 		if not isSelecting then | ||||
| 		if (not isSelecting) and (not doNotTouchScroll) then | ||||
| 			scrollToCursor() | ||||
| 		end | ||||
| 	end | ||||
| @@ -707,6 +810,7 @@ prompt = function(prebuffer, precy, maxY, _eldit) | ||||
| 		local yAdj = 0 | ||||
| 		for id,sel in pairs(eldit.selections) do | ||||
| 			for y = sel[1].y, sel[2].y do | ||||
| 				xAdj = 0 | ||||
| 				if eldit.buffer[y] then | ||||
| 					xAdjusts[y] = xAdjusts[y] or {} | ||||
| 					if checkWithinArea(#eldit.buffer[y] + 1, y, sel[1].x, sel[1].y, sel[2].x, sel[2].y) then | ||||
| @@ -922,6 +1026,8 @@ prompt = function(prebuffer, precy, maxY, _eldit) | ||||
| 		[82] = 0, | ||||
| 	} | ||||
|  | ||||
| 	local startedSelecting = false | ||||
|  | ||||
| 	-- here we go my man | ||||
| 	scrollToCursor() | ||||
| 	while true do | ||||
| @@ -959,12 +1065,12 @@ prompt = function(prebuffer, precy, maxY, _eldit) | ||||
| 						}) | ||||
| 					end | ||||
| 				end | ||||
| 			elseif (evt[1] == "char" and not keysDown[keys.leftCtrl]) then | ||||
| 			elseif (evt[1] == "char" and not keysDown[keys.ctrl]) then | ||||
| 				placeText(evt[2]) | ||||
| 				if eldit.allowUndo then undotID = os.startTimer(eldit.undoDelay) end | ||||
| 				doRender = true | ||||
| 			elseif evt[1] == "paste" then | ||||
| 				if keysDown[keys.leftShift] then | ||||
| 				if keysDown[keys.shift] then | ||||
| 					local cb = eldit.clipboards[eldit.selectedClipboard] | ||||
| 					local cbb = {} | ||||
| 					if cb then | ||||
| @@ -1004,10 +1110,16 @@ prompt = function(prebuffer, precy, maxY, _eldit) | ||||
| 				doRender = true | ||||
| 			elseif evt[1] == "key" then | ||||
| 				keysDown[evt[2]] = true | ||||
| 				-- KEYBOARD SHORTCUTS | ||||
| 				if keysDown[keys.leftCtrl] or keysDown[keys.rightCtrl] then | ||||
|  | ||||
| 					if keysDown[keys.leftShift] or keysDown[keys.rightShift] then | ||||
| 				keysDown[keys.shift] = keysDown[keys.leftShift] or keysDown[keys.rightShift] | ||||
| 				keysDown[keys.alt] = keysDown[keys.leftAlt] or keysDown[keys.rightAlt] | ||||
| 				keysDown[keys.ctrl] = keysDown[keys.leftCtrl] or keysDown[keys.rightCtrl] | ||||
|  | ||||
|  | ||||
| 				-- KEYBOARD SHORTCUTS | ||||
| 				if keysDown[keys.ctrl] then | ||||
|  | ||||
| 					if keysDown[keys.shift] then | ||||
| 						if evt[2] == keys.c or evt[2] == keys.x then | ||||
| 							doRender = true | ||||
| 							if #eldit.selections == 0 then | ||||
| @@ -1066,7 +1178,12 @@ prompt = function(prebuffer, precy, maxY, _eldit) | ||||
| 					else | ||||
| 						if numToKey[evt[2]] then -- if that's a number then | ||||
| 							eldit.selectedClipboard = numToKey[evt[2]] | ||||
| 							barmsg = "Selected clipboard " .. eldit.selectedClipboard .. "." | ||||
| 							barmsg = "Selected clipboard " .. eldit.selectedClipboard | ||||
| 							if eldit.clipboards[eldit.selectedClipboard] then | ||||
| 								barmsg = barmsg .. ": " .. table.concat(eldit.clipboards[eldit.selectedClipboard][1][1]) | ||||
| 							else | ||||
| 								barmsg = barmsg .. "." | ||||
| 							end | ||||
| 							barlife = defaultBarLife | ||||
| 							doRender = true | ||||
|  | ||||
| @@ -1134,13 +1251,13 @@ prompt = function(prebuffer, precy, maxY, _eldit) | ||||
| 							doRender = true | ||||
|  | ||||
| 						elseif evt[2] == keys.left then | ||||
| 							adjustCursor(-1, 0, true, "word") | ||||
| 							adjustCursor(-1, 0, true, "word", false, keysDown[keys.shift]) | ||||
| 							doRender, isCursorBlink = true, true | ||||
| 							eldit.undoBuffer[eldit.undoPos].selections = eldit.selections | ||||
| 							eldit.undoBuffer[eldit.undoPos].cursors = eldit.cursors | ||||
|  | ||||
| 						elseif evt[2] == keys.right then | ||||
| 							adjustCursor(1, 0, true, "word") | ||||
| 							adjustCursor(1, 0, true, "word", false, keysDown[keys.shift]) | ||||
| 							doRender, isCursorBlink = true, true | ||||
| 							eldit.undoBuffer[eldit.undoPos].selections = eldit.selections | ||||
| 							eldit.undoBuffer[eldit.undoPos].cursors = eldit.cursors | ||||
| @@ -1165,7 +1282,7 @@ prompt = function(prebuffer, precy, maxY, _eldit) | ||||
| 				else | ||||
|  | ||||
| 					if evt[2] == keys.tab then | ||||
| 						if keysDown[keys.leftShift] then | ||||
| 						if keysDown[keys.shift] then | ||||
| 							indentLines(true) | ||||
| 						elseif #eldit.selections > 0 then | ||||
| 							indentLines(false) | ||||
| @@ -1205,10 +1322,16 @@ prompt = function(prebuffer, precy, maxY, _eldit) | ||||
|  | ||||
| 					elseif evt[2] == keys.pageUp then | ||||
| 						adjustScroll(0, -eldit.size.height) | ||||
| 						if isSelecting then | ||||
| 							os.queueEvent("mouse_drag", 1, (miceDown[1] or miceDown[2]).x, (miceDown[1] or miceDown[2]).y) | ||||
| 						end | ||||
| 						doRender = true | ||||
|  | ||||
| 					elseif evt[2] == keys.pageDown then | ||||
| 						adjustScroll(0, eldit.size.height) | ||||
| 						if isSelecting then | ||||
| 							os.queueEvent("mouse_drag", 1, (miceDown[1] or miceDown[2]).x, (miceDown[1] or miceDown[2]).y) | ||||
| 						end | ||||
| 						doRender = true | ||||
|  | ||||
| 					elseif evt[2] == keys.backspace then | ||||
| @@ -1230,25 +1353,25 @@ prompt = function(prebuffer, precy, maxY, _eldit) | ||||
| 						if eldit.allowUndo then undotID = os.startTimer(eldit.undoDelay) end | ||||
|  | ||||
| 					elseif evt[2] == keys.left then | ||||
| 						adjustCursor(-1, 0, true) | ||||
| 						adjustCursor(-1, 0, true, nil, false, keysDown[keys.shift]) | ||||
| 						eldit.undoBuffer[eldit.undoPos].selections = eldit.selections | ||||
| 						eldit.undoBuffer[eldit.undoPos].cursors = eldit.cursors | ||||
| 						doRender, isCursorBlink = true, true | ||||
|  | ||||
| 					elseif evt[2] == keys.right then | ||||
| 						adjustCursor(1, 0, true) | ||||
| 						adjustCursor(1, 0, true, nil, false, keysDown[keys.shift]) | ||||
| 						eldit.undoBuffer[eldit.undoPos].selections = eldit.selections | ||||
| 						eldit.undoBuffer[eldit.undoPos].cursors = eldit.cursors | ||||
| 						doRender, isCursorBlink = true, true | ||||
|  | ||||
| 					elseif evt[2] == keys.up then | ||||
| 						adjustCursor(0, -1, false) | ||||
| 						adjustCursor(0, -1, false, nil, false, keysDown[keys.shift]) | ||||
| 						eldit.undoBuffer[eldit.undoPos].selections = eldit.selections | ||||
| 						eldit.undoBuffer[eldit.undoPos].cursors = eldit.cursors | ||||
| 						doRender, isCursorBlink = true, true | ||||
|  | ||||
| 					elseif evt[2] == keys.down then | ||||
| 						adjustCursor(0, 1, false) | ||||
| 						adjustCursor(0, 1, false, nil, false, keysDown[keys.shift]) | ||||
| 						doRender, isCursorBlink = true, true | ||||
| 						eldit.undoBuffer[eldit.undoPos].selections = eldit.selections | ||||
| 						eldit.undoBuffer[eldit.undoPos].cursors = eldit.cursors | ||||
| @@ -1257,18 +1380,32 @@ prompt = function(prebuffer, precy, maxY, _eldit) | ||||
| 				end | ||||
| 			elseif evt[1] == "key_up" then | ||||
| 				keysDown[evt[2]] = nil | ||||
|  | ||||
| 				keysDown[keys.shift] = keysDown[keys.leftShift] or keysDown[keys.rightShift] | ||||
| 				keysDown[keys.alt] = keysDown[keys.leftAlt] or keysDown[keys.rightAlt] | ||||
| 				keysDown[keys.ctrl] = keysDown[keys.leftCtrl] or keysDown[keys.rightCtrl] | ||||
| 			elseif evt[1] == "mouse_click" then | ||||
| 				local lineNoLen = getLineNoLen() | ||||
| 				startedSelecting = false | ||||
| 				miceDown[evt[2]] = {x = evt[3], y = evt[4]} | ||||
| 				if evt[4] == -1 + eldit.size.y + eldit.size.height then | ||||
|  | ||||
| 				else | ||||
| 					if keysDown[keys.leftCtrl] then | ||||
| 					if keysDown[keys.ctrl] and ( | ||||
| 						not checkIfSelected( | ||||
| 							math.min( | ||||
| 								evt[3] + eldit.scrollX - lineNoLen, | ||||
| 								#eldit.buffer[evt[4] + eldit.scrollY] + 1 | ||||
| 							), | ||||
| 							evt[4] + eldit.scrollY | ||||
| 						) | ||||
| 					) then | ||||
| 						table.insert(eldit.cursors, { | ||||
| 							x = evt[3] + eldit.scrollX - lineNoLen, | ||||
| 							y = evt[4] + eldit.scrollY, | ||||
| 							lastX = evt[3] + eldit.scrollX - lineNoLen | ||||
| 						}) | ||||
| 						startedSelecting = true | ||||
| 					else | ||||
| 						eldit.cursors = {{ | ||||
| 							x = evt[3] + eldit.scrollX - lineNoLen, | ||||
| @@ -1283,11 +1420,11 @@ prompt = function(prebuffer, precy, maxY, _eldit) | ||||
| 						scrollX = eldit.scrollX, | ||||
| 						scrollY = eldit.scrollY, | ||||
| 						lineNoLen = lineNoLen, | ||||
| 						ctrl = keysDown[keys.leftCtrl], | ||||
| 						ctrl = keysDown[keys.ctrl], | ||||
| 						curID = #eldit.cursors, | ||||
| 					} | ||||
| 					sortSelections() | ||||
| 					adjustCursor(0, 0, true) | ||||
| 					adjustCursor(0, 0, true, nil, nil, nil, startedSelecting) | ||||
| 					eldit.undoBuffer[eldit.undoPos].selections = eldit.selections | ||||
| 					eldit.undoBuffer[eldit.undoPos].cursors = eldit.cursors | ||||
| 				end | ||||
| @@ -1340,8 +1477,8 @@ prompt = function(prebuffer, precy, maxY, _eldit) | ||||
| 						} | ||||
| 						sortSelections() | ||||
| 						eldit.cursors[lastMouse.curID] = { | ||||
| 							x = eldit.selections[selID][1].x, | ||||
| 							y = eldit.selections[selID][1].y, | ||||
| 							x = eldit.selections[selID][2].x, | ||||
| 							y = eldit.selections[selID][2].y, | ||||
| 							lastX = eldit.selections[selID][1].x | ||||
| 						} | ||||
|  | ||||
| @@ -1357,10 +1494,10 @@ prompt = function(prebuffer, precy, maxY, _eldit) | ||||
| 				isSelecting = false | ||||
| 				sortSelections() | ||||
| 			elseif evt[1] == "mouse_scroll" then | ||||
| 				if keysDown[keys.leftAlt] then | ||||
| 					adjustScroll((keysDown[keys.leftCtrl] and eldit.size.width or 1) * evt[2], 0) | ||||
| 				if keysDown[keys.alt] then | ||||
| 					adjustScroll(((keysDown[keys.ctrl] and not (isSelecting or startedSelecting)) and eldit.size.width or 1) * evt[2], 0) | ||||
| 				else | ||||
| 					adjustScroll(0, (keysDown[keys.leftCtrl] and eldit.size.height or 1) * evt[2]) | ||||
| 					adjustScroll(0, ((keysDown[keys.ctrl] and not (isSelecting or startedSelecting)) and eldit.size.height or 1) * evt[2]) | ||||
| 				end | ||||
| 				if isSelecting then | ||||
| 					os.queueEvent("mouse_drag", 1, evt[3], evt[4]) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 LDDestroier
					LDDestroier