ComputerCraft Archive

eldit

computer utility LDDestroier github

Description

A collection of all my ComputerCraft programs and the APIs they use. This is mostly just to get them the fuck off of pastebin, and also to ensure that API owners don't change things to break my precious programs...!

Installation

Copy one of these commands into your ComputerCraft terminal:

wget:wget https://raw.githubusercontent.com/LDDestroier/CC/master/eldit.lua eldit
Archive:wget https://cc.shobie.xyz/cc/get/gh-LDDestroier-CC-eldit eldit
Quick Install: wget https://cc.shobie.xyz/cc/get/gh-LDDestroier-CC-eldit eldit

Usage

Run: eldit

Tags

none

Source

View Original Source

Code Preview

--[[
	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()

local argData = {
	["-l"] = "number"
}

local eldit, config = {}, {}
eldit.buffer = {{}}			-- stores all text, organized like eldit.buffer[yPos][xPos]
eldit.undoBuffer = {{{}}}	-- stores buffers for undoing/redoing
eldit.allowUndo = true		-- whether or not to allow undoing/redoing
eldit.maxUndo = 16			-- maximum size of the undo buffer
eldit.undoPos = 1			-- current position in undo buffer
eldit.undoDelay = 0.3		-- amount of time to wait after typing, before the buffer is put in the undo buffer
eldit.clipboards = {}		-- all clipboard entries
eldit.selectedClipboard = 1	-- which clipboard to use
eldit.scrollX = 0			-- horizontal scroll
eldit.scrollY = 0			-- vertical scroll
eldit.selections = {}		-- all selected areas
eldit.size = {
	x = 1,			-- top left corner X
	y = 1,			-- top left corner Y
	width = scr_x,	-- horizontal 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
local sortTable = table.sort

-- I'm never using regular argument parsing again, this function rules
local interpretArgs = function(tInput, tArgs)
	local output = {}
	local errors = {}
	local usedEntries = {}
	for aName, aType in pairs(tArgs) do
		output[aName] = false
		for i = 1, #tInput do
			if not usedEntries[i] then
				if tInput[i] == aName and not output[aName] then
					if aType then
						usedEntries[i] = true
						if type(tInput[i+1]) == aType or type(tonumber(tInput[i+1])) == aType then
							usedEntries[i+1] = true
							if aType == "number" then
								output[aName] = tonumber(tInput[i+1])
							else
								output[aName] = tInput[i+1]
							end
						else
							output[aName] = nil
							errors[1] = errors[1] and (errors[1] + 1) or 1
							errors[aName] = "expected " .. aType .. ", got " .. type(tInput[i+1])
						end
					else
						usedEntries[i] = true
						output[aName] = true
					end
				end
			end
		end
	end
	for i = 1, #tInput do
		if not usedEntries[i] then
			output[#output+1] = tInput[i]
		end
	end
	return output, errors
end

local argList = interpretArgs({...}, argData)

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) .. "%....{{code}}quot;)
				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),
	lastX = 1
}}

local eClearLine = function(y)
	local cx, cy = term.getCursorPos()
	term.setCursorPos(eldit.size.x, y or cy)
	term.write((" "):rep(eldit.size.width))
	term.setCursorPos(cx, cy)
end

local eClear = function()
	local cx, cy = term.getCursorPos()
	for y = eldit.size.y, eldit.size.y + eldit.size.height - 1 do
		term.setCursorPos(eldit.size.x, y)
		term.write((" "):rep(eldit.size.width))
	end
	term.setCursorPos(cx, cy)
end

-- sorts all selections based on each of their (x,y) positions (top left first)
local sortSelections = function()
	for id,sel in pairs(eldit.selections) do
		sortTable(sel, function(a,b)
			return (a.y * eldit.size.width) + a.x < (b.y * eldit.size.width) + b.x
		end)
	end
end

-- sorts all cursors based on (x,y) position (top left first)
local sortCursors = function()
	sortTable(eldit.cursors, function(a,b)
		return (a.y * eldit.size.width) + a.x < (b.y * eldit.size.width) + b.x
	end)
end

local explode = function(div, str, replstr, includeDiv)
	if (div == '') then
		return false
	end
	local pos, arr = 0, {}
	for st, sp in function() return string.find(str, div, pos, false) end do
		table.insert(arr, string.sub(replstr or str, pos, st - 1 + (includeDiv and #div or 0)))
		pos = sp + 1
	end
	table.insert(arr, string.sub(replstr or str, pos))
	return arr
end

local readFile = function(path)
	if fs.exists(path) then
		local file = fs.open(path, "r")
		local contents = file.readAll()
		file.close()
		return contents
	else
		return nil
	end
end

local writeFile = function(path, contents)
	if fs.isReadOnly(path) or fs.isDir(path) then
		return false
	else
		local file = fs.open(path, "w")
		file.write(contents)
		file.close()
		return true
	end
end

local deepCopy
deepCopy = function(tbl)
	local output = {}
	for k,v in pairs(tbl) do
		if type(v) == "table" then
			output[k] = deepCopy(v)
		else
			output[k] = v
		end
	end
	return output
end

local choice = function(input, breakKeys, returnNumber)
	local fpos = 0
	repeat
		event, key = os.pullEvent("char")
		key = key or ""
		if type(breakKeys) == "table" then
			for a = 1, #breakKeys do
				if key == breakKeys[a] then
					return ""
				end
			end
		end
		fpos = string.find(input, key)
	until fpos
	return returnNumber and fpos or key
end

prompt = function(prebuffer, precy, maxY, _eldit)
	term.setCursorBlink(false)
	local keysDown = {}					-- list of all keys being pressed
	local miceDown = {}					-- list of all mouse buttons being pressed
	eldit = _eldit or eldit				-- you can replace the "eldit" table if you want I guess
	maxY = maxY or math.huge			-- limits amount of lines
	local defaultBarLife = 10			-- default amount of time bar msg will stay onscreen
	local barmsg = "Started Eldit."		-- message displayed on bottom screen
	local barlife = defaultBarLife
	local lastMouse = {}				-- last place you clicked onscreen
	local isSelecting = false			-- whether or not you are selecting text
	if type(prebuffer) == "string" then	-- enter a "prebuffer" (string or table) to set the contents
		for i = 1, #prebuffer do
			if prebuffer:sub(i,i) == "\n" then
				eldit.buffer[#eldit.buffer + 1] = {}
			else
				eldit.buffer[#eldit.buffer][#eldit.buffer[#eldit.buffer] + 1] = prebuffer:sub(i,i)
			end
		end
	elseif type(prebuffer) == "table" then
		eldit.buffer = prebuffer
	end
	eldit.undoBuffer[1] = {
		buffer = deepCopy(eldit.buffer),
		cursors = deepCopy(eldit.cursors),
		selections = deepCopy(eldit.selections)
	}
	local isCursorBlink = false			-- blinks the background color on each cursor
	local isInsert = false				-- will overwrite characters instead of appending them

	-- gets length of left line numbers, if enabled at all
	local getLineNoLen = function()
		if config.showLineNumberIndicator then
			return #tostring(#eldit.buffer)
		else
			return 0
		end
	end

	-- list of all characters that will stop a CTRL+Backspace or CTRL+Delete or CTRL+Left/Right
	local interruptable = {
		[" "] = true,
		["["] = true, ["]"] = true,
		["{"] = true, ["}"] = true,
		["("] = true, [")"] = true,
		["|"] = true,
		["/"] = true,
		["\\"] = true,
		["+"] = true,
		["-"] = true,
		["*"] = true,
		["="] = true,
		["."] = true,
		[","] = true
	}

	-- checks if (checkX, checkY) is between (x1, y1) and (x2, y2) in terms of selection (it's not a rectangular check)
	local checkWithinArea = function(checkX, checkY, x1, y1, x2, y2)
		if checkY == y1 then
			if y1 == y2 then
				return checkX >= x1 and checkX <= x2
			else
				return checkX >= x1
			end
		elseif checkY == y2 then
			if y1 == y2 then
				return checkX >= x1 and checkX <= x2
			else
				return checkX <= x2 and checkX >= 1
			end
		elseif checkY > y1 and checkY < y2 then
			return true
		else
			return false
		end
	end

	-- goes over every selection and checks if it is selected
	-- (x, y) = position on buffer
	local checkIfSelected = function(x, y)
		sortSelections()
		local fin
		if y >= 1 and y <= #eldit.buffer then
			if x >= 1 and x <= #eldit.buffer[y] + 1 then
				for id, sel in pairs(eldit.selections) do

					if checkWithinArea(x, y, sel[1].x, sel[1].y, sel[2].x, sel[2].y) then
						return id
					end

				end
			end
		end
		return false
	end

	-- goes over every cursor and checks if they are at (x, y)
	-- (x,y) = position on buffer
	local checkIfCursor = function(x, y)
		for id, cur in pairs(eldit.cursors) do
			if x == cur.x and y == cur.y then
				return id
			end
		end
		return false
	end

	-- returns character at (x, y) on the buffer
	local getChar = function(x, y)
		if eldit.buffer[y] then
			return eldit.buffer[y][x]
		else
			return nil
		end
	end

	-- all characters that count as whitespace
	local tab = {
		[" "] = true,
		["\9"] = true
	}

	-- the big boi function, draws **EVERYTHING**
	local render = function()
		local cx, cy
		local lineNoLen = getLineNoLen()
		local isHighlighted = false
		local textPoses = {math.huge, -math.huge}		-- used to identify space characters without text
		local screen = {{},{},{}}
		for y = 1, eldit.size.height - 1 do -- minus one because it reserves space for the bar
			screen[1][y] = {}
			screen[2][y] = {}
			screen[3][y] = {}
			cy = y + eldit.scrollY
			-- find text
			if eldit.buffer[cy] and (config.showWhitespace or config.showTrailingSpace) then
				textPoses = {
					#(concatTable(eldit.buffer[cy]):match("^ +") or "") + 1,
					#eldit.buffer[cy] - #(concatTable(eldit.buffer[y]):match(" +{{code}}quot;) or "")
				}
			end
			if cy <= #eldit.buffer and lineNoLen > 0 then
				isHighlighted = false
				for id,cur in pairs(eldit.cursors) do
					if cy == cur.y then
						isHighlighted = true
						break
					end
				end
				if not isHighlighted then
					for id,sel in pairs(eldit.selections) do
						if cy >= sel[1].y and cy <= sel[2].y then
							isHighlighted = true
							break
						end
					end
				end
				if isHighlighted then
					term.setBackgroundColor(colors.gray)
					term.setTextColor(colors.white)
				else
					term.setBackgroundColor(colors.black)
					term.setTextColor(colors.lightGray)
				end
				term.setCursorPos(eldit.size.x, eldit.size.y + y - 1)
				term.write(cy .. (" "):rep(lineNoLen - #tostring(y)))
			end

			-- actually draw text

			local cChar, cTxt, cBg = " ", " ", " "
			term.setCursorPos(eldit.size.x + lineNoLen, eldit.size.y + y - 1)
			for x = lineNoLen + 1, eldit.size.width do
				cx = x + eldit.scrollX - lineNoLen

				if checkIfCursor(cx, cy) and isCursorBlink then
					if isInsert then
						cTxt, cBg = "8", "0"
					else
						cTxt, cBg = "f", "8"
					end
				else
					if checkIfSelected(cx, cy) then
						cBg = "b"
					else
						cBg = "f"
					end
					cTxt = "0"
				end
				if config.showWhitespace or config.showTrailingSpace then
					if textPoses[1] and textPoses[2] and eldit.buffer[cy] then
						if cx < textPoses[1] and eldit.buffer[cy][cx] then
							cTxt = "7"
							cChar = "|"
						elseif (cx > textPoses[2] and eldit.buffer[cy][cx]) then
							cTxt = "7"
							cChar = "-"
						else
							cChar = getChar(cx, cy) or " "
						end
					else
						cChar = getChar(cx, cy) or " "
					end
				else
					cChar = getChar(cx, cy) or " "
				end
				screen[1][y][x - lineNoLen] = cChar
				screen[2][y][x - lineNoLen] = cTxt
				screen[3][y][x - lineNoLen] = cBg
			end
			term.blit(
				concatTable(screen[1][y]),
				concatTable(screen[2][y]),
				concatTable(screen[3][y])
			)
		end
		term.setCursorPos(eldit.size.x, eldit.size.y + eldit.size.height - 1)
		term.setBackgroundColor(colors.black)
		eClearLine()
		if barlife > 0 then
			term.setTextColor(colors.yellow)
			term.write(barmsg)
		else
			term.setTextColor(colors.yellow)
			for id,cur in pairs(eldit.cursors) do
				term.write("(" .. cur.x .. "," .. cur.y .. ") ")
			end
		end
	end

	-- if all cursors are offscreen, will scroll so that at least one of them is onscreen
	local scrollToCursor = function()
		lineNoLen = getLineNoLen()
		local lowCur, highCur = eldit.cursors[1], eldit.cursors[1]
		local leftCur, rightCur = eldit.cursors[1], eldit.cursors[1]
		for id,cur in pairs(eldit.cursors) do
			if cur.y < lowCur.y then
				lowCur = cur
			elseif cur.y > highCur.y then
				highCur = cur
			end
			if cur.x < leftCur.x then
				leftCur = cur
			elseif cur.y > rightCur.x then
				rightCur = cur
			end
		end
		if lowCur.y - eldit.scrollY < 1 then
			eldit.scrollY = -1 + highCur.y
		elseif highCur.y - eldit.scrollY > -1 + eldit.size.height then
			eldit.scrollY = 1 + lowCur.y - eldit.size.height
		end
		if leftCur.x - eldit.scrollX < 1 then
			eldit.scrollX = -1 + rightCur.x
		elseif rightCur.x - eldit.scrollX > eldit.size.width - lineNoLen then
			eldit.scrollX = leftCur.x - (eldit.size.width - lineNoLen)
		end
	end

	-- gets the widest line length in all the buffer
	local getMaximumWidth = function()
		local maxX = 0
		for y = 1, #eldit.buffer do
			maxX = math.max(maxX, #eldit.buffer[y])
		end
		return maxX
	end

	-- scrolls the screen, and fixes it if it's set to some weird value
	local adjustScroll = function(modx, mody)
		modx, mody = modx or 0, mody or 0
		local lineNoLen = getLineNoLen()
		if mody then
			eldit.scrollY = math.min(
				math.max(
					0,
					eldit.scrollY + mody
				),
				math.max(
					0,
					1 + #eldit.buffer - eldit.size.height
				)
			)
		end
		if modx then
			eldit.scrollX = math.min(
				math.max(
					0,
					eldit.scrollX + modx
				),
				math.max(
					0,
					1 + getMaximumWidth() - eldit.size.width - lineNoLen
				)
			)
		end
	end

	-- removes any cursors that share positions
	local removeRedundantCursors = function()
		local xes = {}
		for i = #eldit.cursors, 1, -1 do
			if xes[eldit.cursors[i].x] == eldit.cursors[i].y then
				table.remove(eldit.cursors, i)
			else
				xes[eldit.cursors[i].x] = eldit.cursors[i].y
			end
		end
	end

	-- deletes text at every cursor position, either forward or backward or neutral
	local deleteText = function(mode, direction, _cx, _cy)
		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
						xAdjList[cy] = (xAdjList[cy] or 0) + 1
						table.remove(eldit.buffer[cy], cx)
					elseif cy < #eldit.buffer then
						for i = 1, #eldit.buffer[cy + 1] do
							table.insert(eldit.buffer[cy], eldit.buffer[cy + 1][i])
						end
						table.remove(eldit.buffer, cy + 1)
						yAdj = yAdj + 1
					end
				elseif direction == "backward" then
					if cx > 1 then
						cx = cx - 1
						xAdjList[cy] = (xAdjList[cy] or 0) + 1
						table.remove(eldit.buffer[cy], cx)
					elseif cy > 1 then
						cx = #eldit.buffer[cy - 1] + 1
						for i = 1, #eldit.buffer[cy] do
							table.insert(eldit.buffer[cy - 1], eldit.buffer[cy][i])
						end
						table.remove(eldit.buffer, cy)
						yAdj = yAdj + 1
						cy = cy - 1
					end
				else
					if cx >= 1 and cx <= #eldit.buffer[cy] then
						table.remove(eldit.buffer[cy], cx)
					elseif cx == #eldit.buffer[cy] + 1 and cy < #eldit.buffer then
						for i = 1, #eldit.buffer[cy + 1] do
							table.insert(eldit.buffer[cy], eldit.buffer[cy + 1][i])
						end
						table.remove(eldit.buffer, cy + 1)
						yAdj = yAdj + 1
					end
				end
			elseif mode == "word" then
				local pos = cx
				if direction == "forward" then
					while true do
						pos = 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
					for i = pos, cx, -1 do
						xAdjList[cy] = (xAdjList[cy] or 0) + 1
						table.remove(eldit.buffer[cy], i)
					end
				else
					while true do
						pos = 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)
					end
					cx = pos
				end
			elseif mode == "line" then -- like word but is only interrupted by newline
				if direction == "forward" then
					for i = cx, #eldit.buffer[cy] do
						eldit.buffer[cy][i] = nil
					end
				else
					for i = cx, 1, -1 do
						table.remove(eldit.buffer[cy], i)
					end
				end
			end

			if _cx then
				return yAdj
			else
				cur.x = cx
				cur.y = cy
				cur.lastX = cx
			end

		end
		removeRedundantCursors()
		if not isSelecting then
			scrollToCursor()
		end
		return yAdj
	end

	local indentLines = function(goBackward)
		sortSelections()
		local safeY = {}
		for id,sel in pairs(eldit.selections) do
			for y = sel[1].y, sel[2].y do
				if not safeY[y] then
					if goBackward then
						if eldit.buffer[y][1] == "\9" or eldit.buffer[y][1] == " " then
							table.remove(eldit.buffer[y], 1)
							if y == sel[1].y then
								sel[1].x = math.max(1, -1 + sel[1].x)
							elseif y == sel[2].y then
								sel[2].x = math.max(1, -1 + sel[2].x)
							end
							for idd,cur in pairs(eldit.cursors) do
								if cur.y == y and cur.x > 1 then
									cur.x = -1 + cur.x
									cur.lastX = cur.x
								end
							end
						end
					elseif eldit.buffer[y] then
						table.insert(eldit.buffer[y], 1, "\9")
						if y == sel[1].y then
							sel[1].x = 1 + sel[1].x
						elseif y == sel[2].y then
							sel[2].x = 1 + sel[2].x
						end
						for idd,cur in pairs(eldit.cursors) do
							if cur.y == y and cur.x < #eldit.buffer[y] then
								cur.x = 1 + cur.x
								cur.lastX = cur.x
							end
						end
					end
				end
				safeY[y] = true
			end
		end
		for id,cur in pairs(eldit.cursors) do
			if not safeY[cur.y] then
				if goBackward then
					if eldit.buffer[cur.y][1] == "\9" or eldit.buffer[cur.y][1] == " " then
						table.remove(eldit.buffer[cur.y], 1)
						cur.x = -1 + cur.x
						cur.lastX = cur.x
					end
				else
					table.insert(eldit.buffer[cur.y], 1, "\9")
					cur.x = 1 + cur.x
					cur.lastX = cur.x
				end
			end
		end
	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, 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 = step
				ymod = 0
				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
				ymod = _ymod
			end
			if mode == "flip" then
				if eldit.buffer[cur.y + ymod] then
					eldit.buffer[cur.y], eldit.buffer[cur.y + ymod] = eldit.buffer[cur.y + ymod], eldit.buffer[cur.y]
				end
			end
			cur.x = cur.x + xmod
			cur.y = math.max(1, math.min(cur.y + ymod, #eldit.buffer))
			if xmod ~= 0 then
				repeat
				if cur.x < 1 and cur.y > 1 then
						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 = 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))
			end
			cur.lastX = setLastX and cur.x or cur.lastX
			if cur.y < 1 then
				cur.y = math.max(1, math.min(cur.y, #eldit.buffer))
				cur.x = 1
			elseif cur.y > #eldit.buffer then
				cur.y = math.max(1, math.min(cur.y, #eldit.buffer))
				cur.x = #eldit.buffer[cur.y] + 1
			else
				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.ctrl]) and not (xmod == 0 and ymod == 0) and not doNotDelSelections then
			eldit.selections = {}
			isSelecting = false
		end
		if (not isSelecting) and (not doNotTouchScroll) then
			scrollToCursor()
		end
	end

	-- deletes the parts of the buffer that are selected, then clears the selection list
	local deleteSelections = function()
		sortSelections()
		if #eldit.selections == 0 then
			return {}, {}
		end
		local xAdjusts = {}
		local yAdjusts = {}
		local xAdj = 0
		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
						yAdj = yAdj + 1
					end
					yAdjusts[y + 1] = math.min(yAdjusts[y + 1] or math.huge, yAdj)
					for x = 2, #eldit.buffer[y] do
						xAdjusts[y][x] = math.min(xAdjusts[y][x] or math.huge, xAdj)
						if checkWithinArea(x, y, sel[1].x, sel[1].y, sel[2].x, sel[2].y) then
							xAdj = xAdj + 1
						end
					end
				end
			end
		end
		for id,sel in pairs(eldit.selections) do
			for y = sel[2].y, sel[1].y, -1 do
				if eldit.buffer[y] then
					for x = #eldit.buffer[y] + 1, 1, -1 do
						if checkWithinArea(x, y, sel[1].x, sel[1].y, sel[2].x, sel[2].y) then
							if x == #eldit.buffer[y] + 1 then
								if eldit.buffer[y + 1] then
									for i = 1, #eldit.buffer[y + 1] do
										table.insert(eldit.buffer[y], eldit.buffer[y + 1][i])
									end
									table.remove(eldit.buffer, y + 1)
								end
							else
								deleteText("single", nil, x, y)
							end
						end
					end
				end
			end
		end
		eldit.selections = {}
		adjustCursor(0, 0, true)
		return xAdjusts, yAdjusts
	end

	-- puts text at every cursor position
	local placeText = function(text, cursorList)
		local xAdjusts, yAdjusts = deleteSelections()
		removeRedundantCursors()
		sortCursors()
		local xAdjList = {}
		for id,cur in pairs(cursorList or eldit.cursors) do
			cur.y = cur.y - (yAdjusts[cur.y] or 0)
			cur.x = cur.x - ((xAdjusts[cur.y] or {})[cur.x] or 0) + (xAdjList[cur.y] or 0)
			for i = 1, #text do
				if isInsert then
					if cur.x == #eldit.buffer[cur.y] + 1 then
						for i = 1, #eldit.buffer[cur.y + 1] do
							table.insert(eldit.buffer[cur.y], eldit.buffer[cur.y + 1][i])
						end
						table.remove(eldit.buffer, cur.y + 1)
					end
					eldit.buffer[cur.y][cur.x + i - 1] = text:sub(i,i)
				else
					table.insert(eldit.buffer[cur.y], cur.x, text:sub(i,i))
					if #xAdjusts + #yAdjusts == 0 then
						xAdjList[cur.y] = (xAdjList[cur.y] or 0) + 1
					end
				end
				cur.x = cur.x + 1
			end
			cur.lastX = cur.x
		end
		if not isSelecting then
			scrollToCursor()
		end
	end

	-- adds a new line to the buffer at every cursor position
	local makeNewLine = function(cursorList)
		for id,cur in pairs(cursorList or eldit.cursors) do
			table.insert(eldit.buffer, cur.y + 1, {})
			for i = cur.x, #eldit.buffer[cur.y] do
				if i > cur.x or not isInsert then
					table.insert(eldit.buffer[cur.y + 1], eldit.buffer[cur.y][i])
				end
				eldit.buffer[cur.y][i] = nil
			end
			cur.x = 1
			cur.y = cur.y + 1
		end
		if not isSelecting then
			scrollToCursor()
		end
	end

	local compareBuffers
	compareBuffers = function(left, right)
		for k,v in pairs(left) do
			if type(v) == "table" then
				if not compareBuffers(v, right[k]) then
					return false
				end
			elseif right then
				if left[k] ~= right[k] then
					return false
				end
			else
				return false
			end
		end
		return true
	end

	-- simulate key inputs, for pre-entering text into read()
	local simType = function(text)
		for i = 1, #text do
			os.queueEvent("key", keys[text:sub(i,i)])
			os.queueEvent("char", text:sub(i,i))
		end
	end

	-- saves to file, duhh
	local saveFile = function(preSaveAs)
		keysDown, miceDown = {}, {}
		local compiled = ""
		for y = 1, #eldit.buffer do
			compiled = compiled .. concatTable(eldit.buffer[y])
			if y < #eldit.buffer then
				compiled = compiled .. "\n"
			end
		end
		if preSaveAs or (not eldit.filename) then
			local newName, cx, cy = ""
			if type(preSaveAs) == "string" then
				simType(preSaveAs)
			end
			repeat
				render()
				term.setCursorPos(eldit.size.y, eldit.size.y + eldit.size.height - 1)
				eClearLine()
				term.setTextColor(colors.yellow)
				term.write("Save as: ")
				term.setTextColor(colors.white)
				cx, cy = term.getCursorPos()
				term.setCursorPos(cx, cy)
				newName = read()
				if tab[newName:sub(-1,-1)] then
					render()
					term.setCursorPos(eldit.size.y, eldit.size.y + eldit.size.height - 1)
					term.write("Path cannot have trailing space!")
					sleep(0.5)
					simType(newName)
				elseif fs.exists(newName) and newName ~= "" then
					render()
					term.setCursorPos(eldit.size.y, eldit.size.y + eldit.size.height - 1)
					eClearLine()
					if fs.isDir(newName) then
						term.write("Cannot overwrite a directory!")
						sleep(0.5)
						simType(newName)
					else
						term.write("Overwrite? (Y/N)")
						if choice("yn", nil, false) == "n" then
							barmsg = "Cancelled save."
							barlife = defaultBarLife
							return
						end
					end
				end
			until (
				(not fs.isDir(newName) or newName == "") and
				(#newName:gsub(" ", "") > 0 or newName == "") and
				not tab[newName:sub(-1,-1)]
			)
			if newName == "" then
				barmsg = "Cancelled save."
				barlife = defaultBarLife
				return
			else
				eldit.filename = newName
			end
		end
		writeFile(eldit.filename, compiled)
		barmsg = "Saved to '" .. eldit.filename .. "'."
		barlife = defaultBarLife
	end

	local evt
	local tID = os.startTimer(0.5)		-- timer for cursor blinking
	local bartID = os.startTimer(0.1)	-- timer for bar message to go away
	local undotID						-- timer for when the buffer is put in the undo buffer
	local doRender = true				-- if true, renders

	-- converts numerical key events to usable numbers
	local numToKey = {
		-- number bar
		[2] = 1,
		[3] = 2,
		[4] = 3,
		[5] = 4,
		[6] = 5,
		[7] = 6,
		[8] = 7,
		[9] = 8,
		[10] = 9,
		[11] = 0,
		-- number pad
		[79] = 1,
		[80] = 2,
		[81] = 3,
		[75] = 4,
		[76] = 5,
		[77] = 6,
		[71] = 7,
		[72] = 8,
		[73] = 9,
		[82] = 0,
	}

	local startedSelecting = false

	-- here we go my man
	scrollToCursor()
	while true do
		evt = {os.pullEvent()}
		repeat
			if evt[1] == "timer" then
				if evt[2] == tID then
					if isCursorBlink then
						tID = os.startTimer(0.4)
					else
						tID = os.startTimer(0.3)
					end
					isCursorBlink = not isCursorBlink
					doRender = true
				elseif evt[2] == bartID then
					bartID = os.startTimer(0.1)
					barlife = math.max(0, barlife - 1)
				elseif evt[2] == undotID then
					if not compareBuffers(eldit.buffer, eldit.undoBuffer[#eldit.undoBuffer].buffer or {}) then
						if #eldit.undoBuffer >= eldit.maxUndo then
							repeat
								table.remove(eldit.undoBuffer, 1)
							until #eldit.undoBuffer < eldit.maxUndo
						end
						if eldit.undoPos < #eldit.undoBuffer then
							repeat
								table.remove(eldit.undoBuffer, 0)
							until eldit.undoPos == #eldit.undoBuffer
						end
						eldit.undoPos = math.min(eldit.undoPos + 1, eldit.maxUndo)
						table.insert(eldit.undoBuffer, {
							buffer = deepCopy(eldit.buffer),
							cursors = deepCopy(eldit.cursors),
							selections = deepCopy(eldit.selections),
						})
					end
				end
			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.shift] then
					local cb = eldit.clipboards[eldit.selectedClipboard]
					local cbb = {}
					if cb then
						deleteSelections()
						sortCursors()
						for i = 1, math.max(#cb, #eldit.cursors) do
							cbb[i] = cb[(i % #cb) + 1]
						end
						for i = 1, #cbb do
							if eldit.cursors[i] then
								for y = 1, #cbb[i] do
									placeText(concatTable(cbb[i][y]), {eldit.cursors[i]})
									if y < #cbb[i] then
										makeNewLine({eldit.cursors[i]})
									end
								end
							else
								makeNewLine({eldit.cursors[#eldit.cursors]})
								for y = 1, #cbb[i] do
									placeText(concatTable(cbb[i][y]), {eldit.cursors[#eldit.cursors]})
									if y < #cbb[i] then
										makeNewLine({eldit.cursors[#eldit.cursors]})
									end
								end
							end
						end
						barmsg = "Pasted from clipboard " .. eldit.selectedClipboard .. "."
						if eldit.allowUndo then undotID = os.startTimer(eldit.undoDelay) end
					else
						barmsg = "Clipboard " .. eldit.selectedClipboard .. " is empty."
					end
					barlife = defaultBarLife
				else
					placeText(evt[2])
					if eldit.allowUndo then undotID = os.startTimer(eldit.undoDelay) end
				end
				doRender = true
			elseif evt[1] == "key" then
				keysDown[evt[2]] = true

				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
								barmsg = "No selections have been made."
								barlife = defaultBarLife
							else
								eldit.clipboards[eldit.selectedClipboard] = {}
								local cb = eldit.clipboards[eldit.selectedClipboard]
								sortSelections()
								local id, selY
								for y = 1, #eldit.buffer do
									for x = 1, #eldit.buffer[y] + 1 do
										id = checkIfSelected(x, y)
										if id then
											selY = y - eldit.selections[id][1].y + 1
											cb[id] = cb[id] or {}
											cb[id][selY] = cb[id][selY] or {}
											table.insert(cb[id][selY], eldit.buffer[y][x])
										end
									end
								end
								if evt[2] == keys.x then
									deleteSelections()
									barmsg = "Cut to clipboard " .. eldit.selectedClipboard .. "."
									barlife = defaultBarLife
									if eldit.allowUndo then undotID = os.startTimer(eldit.undoDelay) end
								else
									barmsg = "Copied to clipboard " .. eldit.selectedClipboard .. "."
									barlife = defaultBarLife
								end
							end

						elseif evt[2] == keys.z then
							if eldit.undoPos < #eldit.undoBuffer then
								eldit.undoPos = math.min(#eldit.undoBuffer, eldit.maxUndo, eldit.undoPos + 1)
								eldit.selections = deepCopy(eldit.undoBuffer[eldit.undoPos].selections)
								eldit.cursors = deepCopy(eldit.undoBuffer[eldit.undoPos].cursors)
								eldit.buffer = deepCopy(eldit.undoBuffer[eldit.undoPos].buffer)
								adjustCursor(0, 0, true)
								barmsg = "Redone. (" .. eldit.undoPos .. "/" .. #eldit.undoBuffer .. ")"
								barlife = defaultBarLife
							else
								barmsg = "Reached top of undo buffer. (" .. eldit.undoPos .. "/" .. #eldit.undoBuffer .. ")"
								barlife = defaultBarLife
							end
							doRender = true

						elseif evt[2] == keys.s then
							saveFile(eldit.filename)
							tID = os.startTimer(0.4)
							bartID = os.startTimer(0.1)
							doRender = true

						end
						-- In-editor pasting is done with the "paste" event!
					else
						if numToKey[evt[2]] then -- if that's a number then
							eldit.selectedClipboard = numToKey[evt[2]]
							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

						elseif evt[2] == keys.rightBracket then
							indentLines(false)
							doRender, isCursorBlink = true, true

						elseif evt[2] == keys.leftBracket then
							indentLines(true)
							doRender, isCursorBlink = true, true

						elseif evt[2] == keys.backspace then
							if #eldit.selections > 0 then
								deleteSelections()
							else
								deleteText("word", "backward")
							end
							if eldit.allowUndo then undotID = os.startTimer(eldit.undoDelay) end
							doRender, isCursorBlink = true, false

						elseif evt[2] == keys.delete then
							if #eldit.selections > 0 then
								deleteSelections()
							else
								deleteText("word", "forward")
							end
							if eldit.allowUndo then undotID = os.startTimer(eldit.undoDelay) end
							doRender, isCursorBlink = true, false

						elseif evt[2] == keys.q then
							return "exit"

						elseif evt[2] == keys.s then
							saveFile(false)
							tID = os.startTimer(0.4)
							bartID = os.startTimer(0.1)
							doRender = true

						elseif evt[2] == keys.a then
							eldit.selections = {{
								{
									x = 1,
									y = 1
								},{
									x = #eldit.buffer[#eldit.buffer],
									y = #eldit.buffer
								}
							}}
							doRender = true

						elseif evt[2] == keys.z then

							if eldit.undoPos > 1 then
								eldit.undoPos = math.max(1, eldit.undoPos - 1)
								eldit.selections = deepCopy(eldit.undoBuffer[eldit.undoPos].selections)
								eldit.cursors = deepCopy(eldit.undoBuffer[eldit.undoPos].cursors)
								eldit.buffer = deepCopy(eldit.undoBuffer[eldit.undoPos].buffer)
								adjustCursor(0, 0, true)
								barmsg = "Undone. (" .. eldit.undoPos .. "/" .. #eldit.undoBuffer .. ")"
								barlife = defaultBarLife
							else
								barmsg = "Reached back of undo buffer."
								barlife = defaultBarLife
							end
							doRender = true

						elseif evt[2] == keys.left then
							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", 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.up then
							adjustCursor(0, -1, false, "flip")
							doRender, isCursorBlink = true, true
							if eldit.allowUndo then undotID = os.startTimer(eldit.undoDelay) end
							eldit.undoBuffer[eldit.undoPos].selections = eldit.selections
							eldit.undoBuffer[eldit.undoPos].cursors = eldit.cursors

						elseif evt[2] == keys.down then
							adjustCursor(0, 1, false, "flip")
							doRender, isCursorBlink = true, true
							if eldit.allowUndo then undotID = os.startTimer(eldit.undoDelay) end
							eldit.undoBuffer[eldit.undoPos].selections = eldit.selections
							eldit.undoBuffer[eldit.undoPos].cursors = eldit.cursors

						end
					end

				else

					if evt[2] == keys.tab then
						if keysDown[keys.shift] then
							indentLines(true)
						elseif #eldit.selections > 0 then
							indentLines(false)
						else
							placeText("\9")
						end
						doRender = true
						if eldit.allowUndo then undotID = os.startTimer(eldit.undoDelay) end

					elseif evt[2] == keys.insert then
						isInsert = not isInsert
						doRender, isCursorBlink = true, true

					elseif evt[2] == keys.enter then
						deleteSelections()
						makeNewLine()
						doRender, isCursorBlink = true, true
						if eldit.allowUndo then undotID = os.startTimer(eldit.undoDelay) end

					elseif evt[2] == keys.home then
						eldit.cursors = {{
							x = 1,
							y = eldit.cursors[1].y,
							lastX = 1
						}}
						scrollToCursor()
						doRender, isCursorBlink = true, true

					elseif evt[2] == keys["end"] then
						eldit.cursors = {{
							x = 1 + #eldit.buffer[eldit.cursors[1].y],
							y = eldit.cursors[1].y,
							lastX = 1 + #eldit.buffer[eldit.cursors[1].y]
						}}
						scrollToCursor()
						doRender, isCursorBlink = true, true

					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
						if #eldit.selections > 0 then
							deleteSelections()
						else
							deleteText("single", "backward")
						end
						doRender, isCursorBlink = true, false
						if eldit.allowUndo then undotID = os.startTimer(eldit.undoDelay) end

					elseif evt[2] == keys.delete then
						if #eldit.selections > 0 then
							deleteSelections()
						else
							deleteText("single", "forward")
						end
						doRender, isCursorBlink = true, false
						if eldit.allowUndo then undotID = os.startTimer(eldit.undoDelay) end

					elseif evt[2] == keys.left then
						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, 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, 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, nil, false, keysDown[keys.shift])
						doRender, isCursorBlink = true, true
						eldit.undoBuffer[eldit.undoPos].selections = eldit.selections
						eldit.undoBuffer[eldit.undoPos].cursors = eldit.cursors
					end

				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.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,
							y = evt[4] + eldit.scrollY,
							lastX = evt[3] + eldit.scrollX - lineNoLen
						}}
						eldit.selections = {}
					end
					lastMouse = {
						x = evt[3],
						y = evt[4],
						scrollX = eldit.scrollX,
						scrollY = eldit.scrollY,
						lineNoLen = lineNoLen,
						ctrl = keysDown[keys.ctrl],
						curID = #eldit.cursors,
					}
					sortSelections()
					adjustCursor(0, 0, true, nil, nil, nil, startedSelecting)
					eldit.undoBuffer[eldit.undoPos].selections = eldit.selections
					eldit.undoBuffer[eldit.undoPos].cursors = eldit.cursors
				end
				doRender = true
			elseif evt[1] == "mouse_drag" then
				if evt[4] == -1 + eldit.size.y + eldit.size.height then

				else
					local lineNoLen = getLineNoLen()
					local lastMX, lastMY
					if miceDown[evt[2]] then
						lastMX, lastMY = miceDown[evt[2]].x, miceDown[evt[2]].y
					else
						lastMX, lastMY = evt[3], evt[4]
					end
					miceDown[evt[2]] = {x = evt[3], y = evt[4]}
					if lastMouse.x and lastMouse.y and lastMouse.curID then
						local adjMY = lastMouse.y + lastMouse.scrollY
						local adjMX = math.min(lastMouse.x + lastMouse.scrollX, #(eldit.buffer[adjMY] or "") + 1)
						local adjEY = evt[4] + eldit.scrollY
						local adjEX = math.min(evt[3] + eldit.scrollX, #(eldit.buffer[adjEY] or "") + 1)
						local selID
						local cSelID = checkIfSelected(adjMX, adjMY)
						if (lastMouse.ctrl and not isSelecting) or #eldit.selections == 0 then
							selID = cSelID or (1 + #eldit.selections)
						else
							selID = #eldit.selections
						end
						if cSelID and not (eldit.selections[cSelID][1].x == adjMX and eldit.selections[cSelID][1].y == adjMY) then
							for id,cur in pairs(eldit.cursors) do
								if cur.x == eldit.selections[cSelID][1].x and cur.y == eldit.selections[cSelID][1].y then
									table.remove(eldit.cursors, id)
									break
								end
							end
							eldit.selections[cSelID][1] = {
								x = math.min(adjMX, #(eldit.buffer[adjMY] or "") + lineNoLen) - lineNoLen,
								y = adjMY
							}
						end
						eldit.selections[selID] = {
							{
								x = math.min(adjMX, #(eldit.buffer[adjMY] or "") + lineNoLen) - lineNoLen,
								y = adjMY
							},
							{
								x = math.min(adjEX, #(eldit.buffer[adjEY] or "") + lineNoLen) - lineNoLen,
								y = adjEY
							}
						}
						sortSelections()
						eldit.cursors[lastMouse.curID] = {
							x = eldit.selections[selID][2].x,
							y = eldit.selections[selID][2].y,
							lastX = eldit.selections[selID][1].x
						}

						isSelecting = true
						adjustCursor(0, 0)
						eldit.undoBuffer[eldit.undoPos].selections = eldit.selections
						eldit.undoBuffer[eldit.undoPos].cursors = eldit.cursors
					end
					doRender = true
				end
			elseif evt[1] == "mouse_up" then
				miceDown[evt[2]] = nil
				isSelecting = false
				sortSelections()
			elseif evt[1] == "mouse_scroll" then
				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.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])
				end
				doRender = true
			end
			if doRender then
				if not (evt[1] == "mouse_scroll" and isSelecting) then
					render()
					doRender = false
				end
			end
		until true
	end
end

local contents = eldit.filename and readFile(eldit.filename) or nil

local result = {prompt(contents)}
if result[1] == "exit" then
	term.setBackgroundColor(colors.black)
	term.scroll(1)
	term.setCursorPos(1, scr_y)
end