Files
wnsrc/lua/pac3/editor/client/panels/lua_editor.lua
lifestorm 94063e4369 Upload
2024-08-04 22:55:00 +03:00

1055 lines
28 KiB
Lua

--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
-- Andreas "Syranide" Svensson's editor for Wire Expression 2
-- edited by DarKSunrise aka Assassini
-- to work with Luapad and with Lua-syntax
-- modified slightly by CapsAdmin to make it work as a mini lua editor in pac3
-- source https://code.google.com/p/lua-pad/
local PANEL = {};
PANEL.ClassName = "luapad"
PANEL.Base = "Panel"
surface.CreateFont("LuapadEditor", {font = "roboto mono", size = 16, weight = 400 } );
surface.CreateFont("LuapadEditor_Bold", {font = "roboto mono", size = 16, weight = 800});
function PANEL:Init()
self:SetCursor("beam");
surface.SetFont("LuapadEditor");
self.FontWidth, self.FontHeight = surface.GetTextSize(" ");
self.Rows = {""};
self.Caret = {1, 1};
self.Start = {1, 1};
self.Scroll = {1, 1};
self.Size = {1, 1};
self.Undo = {};
self.Redo = {};
self.PaintRows = {};
self.Blink = RealTime();
self.ScrollBar = vgui.Create("DVScrollBar", self);
self.ScrollBar:SetUp(1, 1);
self.TextEntry = vgui.Create("TextEntry", self);
self.TextEntry:SetMultiline(true);
self.TextEntry:SetSize(0, 0);
self.TextEntry.OnLoseFocus = function (self) self.Parent:_OnLoseFocus(); end
self.TextEntry.OnTextChanged = function (self) self.Parent:_OnTextChanged(); end
self.TextEntry.OnKeyCodeTyped = function (self, code) self.Parent:_OnKeyCodeTyped(code); end
self.TextEntry.Parent = self;
self.LastClick = 0;
end
function PANEL:RequestFocus()
self.TextEntry:RequestFocus();
end
function PANEL:OnGetFocus()
self.TextEntry:RequestFocus();
end
function PANEL:SetCaretPos(x, y)
self.Caret = {x, y or 0}
self.Start = {x, y or 0}
end
function PANEL:CursorToCaret()
local x, y = self:CursorPos();
x = x - (self.FontWidth * 3 + 6);
if(x < 0) then x = 0; end
if(y < 0) then y = 0; end
local line = math.floor(y / self.FontHeight);
local char = math.floor(x / self.FontWidth + 0.5);
line = line + self.Scroll[1];
char = char + self.Scroll[2];
if(line > #self.Rows) then line = #self.Rows; end
local length = string.len(self.Rows[line]);
if(char > length + 1) then char = length + 1; end
return { line, char };
end
function PANEL:OnMousePressed(code)
if(code == MOUSE_LEFT) then
if((CurTime() - self.LastClick) < 1 and self.tmp and self:CursorToCaret()[1] == self.Caret[1] and self:CursorToCaret()[2] == self.Caret[2]) then
self.Start = self:getWordStart(self.Caret)
self.Caret = self:getWordEnd(self.Caret)
self.tmp = false
return
end
self.tmp = true
self.LastClick = CurTime()
self:RequestFocus()
self.Blink = RealTime()
self.MouseDown = true
self.Caret = self:CursorToCaret()
if( not input.IsKeyDown(KEY_LSHIFT) and not input.IsKeyDown(KEY_RSHIFT)) then
self.Start = self:CursorToCaret()
end
elseif(code == MOUSE_RIGHT) then
local menu = DermaMenu()
if(self:CanUndo()) then
menu:AddOption("Undo", function()
self:DoUndo()
end)
end
if(self:CanRedo()) then
menu:AddOption("Redo", function()
self:DoRedo()
end)
end
if(self:CanUndo() or self:CanRedo()) then
menu:AddSpacer()
end
if(self:HasSelection()) then
menu:AddOption("Cut", function()
if(self:HasSelection()) then
self.clipboard = self:GetSelection()
self.clipboard = string.Replace(self.clipboard, "\n", "\r\n")
SetClipboardText(self.clipboard)
self:SetSelection()
end
end)
menu:AddOption("Copy", function()
if(self:HasSelection()) then
self.clipboard = self:GetSelection()
self.clipboard = string.Replace(self.clipboard, "\n", "\r\n")
SetClipboardText(self.clipboard)
end
end)
end
menu:AddOption("Paste", function()
if(self.clipboard) then
self:SetSelection(self.clipboard)
else
self:SetSelection()
end
end)
if(self:HasSelection()) then
menu:AddOption("Delete", function()
self:SetSelection()
end)
end
menu:AddSpacer()
menu:AddOption("Select all", function()
self:SelectAll()
end)
menu:Open()
end
end
function PANEL:OnMouseReleased(code)
if( not self.MouseDown) then return end
if(code == MOUSE_LEFT) then
self.MouseDown = nil
if( not self.tmp) then return end
self.Caret = self:CursorToCaret()
end
end
function PANEL:SetText(text)
self.Rows = string.Explode("\n", text);
if(self.Rows[#self.Rows] ~= "") then
self.Rows[#self.Rows + 1] = "";
end
self.Caret = {1, 1};
self.Start = {1, 1};
self.Scroll = {1, 1};
self.Undo = {};
self.Redo = {};
self.PaintRows = {};
self.ScrollBar:SetUp(self.Size[1], #self.Rows - 1);
end
function PANEL:GetValue()
return string.Implode("\n", self.Rows)
end
function PANEL:NextChar()
if( not self.char) then return end
self.str = self.str .. self.char
self.pos = self.pos + 1
if(self.pos <= string.len(self.line)) then
self.char = string.sub(self.line, self.pos, self.pos)
else
self.char = nil
end
end
function PANEL:SyntaxColorLine(row)
local cols = {}
local lasttable;
self.line = self.Rows[row]
self.pos = 0
self.char = ""
self.str = ""
-- TODO: Color customization?
local colors = {
["none"] = { Color(200, 200, 200, 255), false},
["number"] = { Color(218, 165, 32, 255), false},
["function"] = { Color(100, 100, 255, 255), false},
["enumeration"] = { Color(184, 134, 11, 255), false},
["metatable"] = { Color(140, 100, 90, 255), false},
["string"] = { Color(120, 120, 120, 255), false},
["expression"] = { Color(0, 0, 255, 255), false},
["operator"] = { Color(100, 100, 100, 255), false},
["comment"] = { Color(0, 120, 0, 255), false},
}
colors["string2"] = colors["string"];
self:NextChar();
while self.char do
local token = "";
self.str = "";
while self.char and self.char == " " do self:NextChar() end
if( not self.char) then break end
if(self.char >= "0" and self.char <= "9") then
while self.char and (self.char >= "0" and self.char <= "9" or self.char == "." or self.char == "_") do self:NextChar() end
token = "number"
elseif(self.char >= "a" and self.char <= "z" or self.char >= "A" and self.char <= "Z") then
while self.char and (self.char >= "a" and self.char <= "z" or self.char >= "A" and self.char <= "Z" or
self.char >= "0" and self.char <= "9" or self.char == "_") do self:NextChar(); end
local sstr = string.Trim(self.str)
if(sstr == "if" or sstr == "elseif" or sstr == "else" or sstr == "then" or sstr == "end" or sstr == "function"
or sstr == "do" or sstr == "while" or sstr == "break" or sstr == "for" or sstr == "in" or sstr == "local"
or sstr == "true" or sstr == "false" or sstr == "nil" or sstr == "NULL" or sstr == "and" or sstr == "not"
or sstr == "or" or sstr == "||" or sstr == "&&") then
token = "expression"
elseif(self:CheckGlobal(sstr) and (isfunction(self:CheckGlobal(sstr)) or self:CheckGlobal(sstr) == "f"
or self:CheckGlobal(sstr) == "e" or self:CheckGlobal(sstr) == "m" or istable(self:CheckGlobal(sstr)))
or (lasttable and lasttable[sstr])) then -- Could be better code, but what the hell; it works
if(istable(self:CheckGlobal(sstr))) then
lasttable = self:CheckGlobal(sstr);
end
if(self:CheckGlobal(sstr) == "e") then
token = "enumeration";
elseif(self:CheckGlobal(sstr) == "m") then
token = "metatable";
else
token = "function";
end
else
lasttable = nil;
token = "none"
end
elseif(self.char == "\"") then -- TODO: Fix multiline strings, and add support for [[stuff]] not
self:NextChar()
while self.char and self.char ~= "\"" do
if(self.char == "\\") then self:NextChar() end
self:NextChar()
end
self:NextChar()
token = "string"
elseif(self.char == "'") then
self:NextChar()
while self.char and self.char ~= "'" do
if(self.char == "\\") then self:NextChar() end
self:NextChar()
end
self:NextChar()
token = "string2"
elseif(self.char == "/" or self.char == "-") then -- TODO: Multiline comments!
local lastchar = self.char;
self:NextChar()
if(self.char == lastchar) then
while self.char do
self:NextChar()
end
token = "comment"
else
token = "none";
end
else
self:NextChar()
token = "operator"
end
local color = colors[token]
if(#cols > 1 and color == cols[#cols][2]) then
cols[#cols][1] = cols[#cols][1] .. self.str
else
cols[#cols + 1] = {self.str, color}
end
end
return cols;
end
function PANEL:PaintLine(row)
if(row > #self.Rows) then return end
if( not self.PaintRows[row]) then
self.PaintRows[row] = self:SyntaxColorLine(row)
end
local width, height = self.FontWidth, self.FontHeight
if(self.error_line == row or row == self.Caret[1] and self.TextEntry:HasFocus()) then
if self.error_line == row then
surface.SetDrawColor(255, 0, 0, 10)
else
surface.SetDrawColor(220, 220, 220, 5)
end
surface.DrawRect(width * 3 + 5, (row - self.Scroll[1]) * height, self:GetWide() - (width * 3 + 5), height)
end
if(self:HasSelection()) then
local start, stop = self:MakeSelection(self:Selection())
local line, char = start[1], start[2]
local endline, endchar = stop[1], stop[2]
surface.SetDrawColor(170, 170, 170, 10)
local length = string.len(self.Rows[row]) - self.Scroll[2] + 1
char = char - self.Scroll[2]
endchar = endchar - self.Scroll[2]
if(char < 0) then char = 0 end
if(endchar < 0) then endchar = 0 end
if(row == line and line == endline) then
surface.DrawRect(char * width + width * 3 + 6, (row - self.Scroll[1]) * height, width * (endchar - char), height)
elseif(row == line) then
surface.DrawRect(char * width + width * 3 + 6, (row - self.Scroll[1]) * height, width * (length - char + 1), height)
elseif(row == endline) then
surface.DrawRect(width * 3 + 6, (row - self.Scroll[1]) * height, width * endchar, height)
elseif(row > line and row < endline) then
surface.DrawRect(width * 3 + 6, (row - self.Scroll[1]) * height, width * (length + 1), height)
end
end
draw.SimpleText(tostring(row), "LuapadEditor", width * 3, (row - self.Scroll[1]) * height, Color(128, 128, 128, 255), TEXT_ALIGN_RIGHT)
local offset = -self.Scroll[2] + 1
for i,cell in ipairs(self.PaintRows[row]) do
if(offset < 0) then
if(string.len(cell[1]) > -offset) then
local line = string.sub(cell[1], -offset + 1)
offset = string.len(line)
if(cell[2][2]) then
draw.SimpleText(line, "LuapadEditorBold", width * 3 + 6, (row - self.Scroll[1]) * height, cell[2][1])
else
draw.SimpleText(line, "LuapadEditor", width * 3 + 6, (row - self.Scroll[1]) * height, cell[2][1])
end
else
offset = offset + string.len(cell[1])
end
else
if(cell[2][2]) then
draw.SimpleText(cell[1], "LuapadEditorBold", offset * width + width * 3 + 6, (row - self.Scroll[1]) * height, cell[2][1])
else
draw.SimpleText(cell[1], "LuapadEditor", offset * width + width * 3 + 6, (row - self.Scroll[1]) * height, cell[2][1])
end
offset = offset + string.len(cell[1])
end
end
if(row == self.Caret[1] and self.TextEntry:HasFocus()) then
if((RealTime() - self.Blink) % 0.8 < 0.4) then
if(self.Caret[2] - self.Scroll[2] >= 0) then
surface.SetDrawColor(255, 255, 255, 160)
surface.DrawRect((self.Caret[2] - self.Scroll[2]) * width + width * 3 + 6, (self.Caret[1] - self.Scroll[1]) * height, 1, height)
end
end
end
end
function PANEL:PerformLayout()
self.ScrollBar:SetSize(16, self:GetTall())
self.ScrollBar:SetPos(self:GetWide() - 16, 0)
self.Size[1] = math.floor(self:GetTall() / self.FontHeight) - 1
self.Size[2] = math.floor((self:GetWide() - (self.FontWidth * 3 + 6) - 16) / self.FontWidth) - 1
self.ScrollBar:SetUp(self.Size[1], #self.Rows - 1)
end
function PANEL:Paint()
if( not input.IsMouseDown(MOUSE_LEFT)) then
self:OnMouseReleased(MOUSE_LEFT)
end
if( not self.PaintRows) then
self.PaintRows = {}
end
if(self.MouseDown) then
self.Caret = self:CursorToCaret()
end
surface.SetDrawColor(20, 20, 20, 255)
surface.DrawRect(0, 0, self.FontWidth * 3 + 4, self:GetTall())
surface.SetDrawColor(23, 23, 23, 255)
surface.DrawRect(self.FontWidth * 3 + 5, 0, self:GetWide() - (self.FontWidth * 3 + 5), self:GetTall())
self.Scroll[1] = math.floor(self.ScrollBar:GetScroll() + 1)
for i=self.Scroll[1],self.Scroll[1]+self.Size[1]+1 do
self:PaintLine(i)
end
return true
end
function PANEL:SetErrorLine(i)
self.error_line = i
end
function PANEL:SetCaret(caret)
self.Caret = self:CopyPosition(caret)
self.Start = self:CopyPosition(caret)
self:ScrollCaret()
end
function PANEL:CopyPosition(caret)
return { caret[1], caret[2] }
end
function PANEL:MovePosition(caret, offset)
local caret = { caret[1], caret[2] }
if(offset > 0) then
while true do
local length = string.len(self.Rows[caret[1]]) - caret[2] + 2
if(offset < length) then
caret[2] = caret[2] + offset
break
elseif(caret[1] == #self.Rows) then
caret[2] = caret[2] + length - 1
break
else
offset = offset - length
caret[1] = caret[1] + 1
caret[2] = 1
end
end
elseif(offset < 0) then
offset = -offset
while true do
if(offset < caret[2]) then
caret[2] = caret[2] - offset
break
elseif(caret[1] == 1) then
caret[2] = 1
break
else
offset = offset - caret[2]
caret[1] = caret[1] - 1
caret[2] = string.len(self.Rows[caret[1]]) + 1
end
end
end
return caret
end
function PANEL:HasSelection()
return self.Caret[1] ~= self.Start[1] or self.Caret[2] ~= self.Start[2]
end
function PANEL:Selection()
return { { self.Caret[1], self.Caret[2] }, { self.Start[1], self.Start[2] } }
end
function PANEL:MakeSelection(selection)
local start, stop = selection[1], selection[2]
if(start[1] < stop[1] or start[1] == stop[1] and start[2] < stop[2]) then
return start, stop
else
return stop, start
end
end
function PANEL:GetArea(selection)
local start, stop = self:MakeSelection(selection)
if(start[1] == stop[1]) then
return string.sub(self.Rows[start[1]], start[2], stop[2] - 1)
else
local text = string.sub(self.Rows[start[1]], start[2])
for i=start[1]+1,stop[1]-1 do
text = text .. "\n" .. self.Rows[i]
end
return text .. "\n" .. string.sub(self.Rows[stop[1]], 1, stop[2] - 1)
end
end
function PANEL:SetArea(selection, text, isundo, isredo, before, after)
local start, stop = self:MakeSelection(selection)
local buffer = self:GetArea(selection)
if(start[1] ~= stop[1] or start[2] ~= stop[2]) then
-- clear selection
self.Rows[start[1]] = string.sub(self.Rows[start[1]], 1, start[2] - 1) .. string.sub(self.Rows[stop[1]], stop[2])
self.PaintRows[start[1]] = false
for i=start[1]+1,stop[1] do
table.remove(self.Rows, start[1] + 1)
table.remove(self.PaintRows, start[1] + 1)
self.PaintRows = {} -- TODO: fix for cache errors
end
-- add empty row at end of file (TODO!)
if(self.Rows[#self.Rows] ~= "") then
self.Rows[#self.Rows + 1] = ""
self.PaintRows[#self.Rows + 1] = false
end
end
if( not text or text == "") then
self.ScrollBar:SetUp(self.Size[1], #self.Rows - 1)
self.PaintRows = {}
self:OnTextChanged()
if(isredo) then
self.Undo[#self.Undo + 1] = { { self:CopyPosition(start), self:CopyPosition(start) }, buffer, after, before }
return before
elseif(isundo) then
self.Redo[#self.Redo + 1] = { { self:CopyPosition(start), self:CopyPosition(start) }, buffer, after, before }
return before
else
self.Redo = {}
self.Undo[#self.Undo + 1] = { { self:CopyPosition(start), self:CopyPosition(start) }, buffer, self:CopyPosition(selection[1]), self:CopyPosition(start) }
return start
end
end
-- insert text
local rows = string.Explode("\n", text)
local remainder = string.sub(self.Rows[start[1]], start[2])
self.Rows[start[1]] = string.sub(self.Rows[start[1]], 1, start[2] - 1) .. rows[1]
self.PaintRows[start[1]] = false
for i=2,#rows do
table.insert(self.Rows, start[1] + i - 1, rows[i])
table.insert(self.PaintRows, start[1] + i - 1, false)
self.PaintRows = {} -- TODO: fix for cache errors
end
local stop = { start[1] + #rows - 1, string.len(self.Rows[start[1] + #rows - 1]) + 1 }
self.Rows[stop[1]] = self.Rows[stop[1]] .. remainder
self.PaintRows[stop[1]] = false
-- add empty row at end of file (TODO!)
if(self.Rows[#self.Rows] ~= "") then
self.Rows[#self.Rows + 1] = ""
self.PaintRows[#self.Rows + 1] = false
self.PaintRows = {} -- TODO: fix for cache errors
end
self.ScrollBar:SetUp(self.Size[1], #self.Rows - 1)
self.PaintRows = {}
self:OnTextChanged()
if(isredo) then
self.Undo[#self.Undo + 1] = { { self:CopyPosition(start), self:CopyPosition(stop) }, buffer, after, before }
return before
elseif(isundo) then
self.Redo[#self.Redo + 1] = { { self:CopyPosition(start), self:CopyPosition(stop) }, buffer, after, before }
return before
else
self.Redo = {}
self.Undo[#self.Undo + 1] = { { self:CopyPosition(start), self:CopyPosition(stop) }, buffer, self:CopyPosition(selection[1]), self:CopyPosition(stop) }
return stop
end
end
function PANEL:GetSelection()
return self:GetArea(self:Selection())
end
function PANEL:SetSelection(text)
self:SetCaret(self:SetArea(self:Selection(), text))
end
function PANEL:_OnLoseFocus()
if(self.TabFocus) then
self:RequestFocus()
self.TabFocus = nil
end
end
function PANEL:_OnTextChanged()
local ctrlv = false
local text = self.TextEntry:GetValue()
self.TextEntry:SetText("")
if((input.IsKeyDown(KEY_LCONTROL) or input.IsKeyDown(KEY_RCONTROL)) and not (input.IsKeyDown(KEY_LALT) or input.IsKeyDown(KEY_RALT))) then
-- ctrl+[shift+]key
if(input.IsKeyDown(KEY_V)) then
-- ctrl+[shift+]V
ctrlv = true
else
-- ctrl+[shift+]key with key ~= V
return
end
end
if(text == "") then return end
if(not ctrlv) then
if(text == "\n") then return end
if(text == "end") then
local row = self.Rows[self.Caret[1]]
end
end
self:SetSelection(text)
end
function PANEL:SetScrollPosition(pos)
if(pos < 1) then pos = 1 end
if(pos > #self.Rows) then pos = #self.Rows end
self.ScrollBar:SetScroll(pos - 1)
self.Scroll[1] = pos
end
function PANEL:GetScrollPosition()
return self.Scroll[1]
end
function PANEL:OnMouseWheeled(delta)
self:SetScrollPosition(self:GetScrollPosition() - 4 * delta)
end
function PANEL:ScrollCaret()
if(self.Caret[1] - self.Scroll[1] < 2) then
self.Scroll[1] = self.Caret[1] - 2
if(self.Scroll[1] < 1) then self.Scroll[1] = 1 end
end
if(self.Caret[1] - self.Scroll[1] > self.Size[1] - 2) then
self.Scroll[1] = self.Caret[1] - self.Size[1] + 2
if(self.Scroll[1] < 1) then self.Scroll[1] = 1 end
end
if(self.Caret[2] - self.Scroll[2] < 4) then
self.Scroll[2] = self.Caret[2] - 4
if(self.Scroll[2] < 1) then self.Scroll[2] = 1 end
end
if(self.Caret[2] - 1 - self.Scroll[2] > self.Size[2] - 4) then
self.Scroll[2] = self.Caret[2] - 1 - self.Size[2] + 4
if(self.Scroll[2] < 1) then self.Scroll[2] = 1 end
end
self.ScrollBar:SetScroll(self.Scroll[1] - 1)
end
local function unindent(line)
local i = line:find("%S")
if(i == nil or i > 5) then i = 5 end
return line:sub(i)
end
function PANEL:CanUndo()
return #self.Undo > 0
end
function PANEL:DoUndo()
if(#self.Undo > 0) then
local undo = self.Undo[#self.Undo]
self.Undo[#self.Undo] = nil
self:SetCaret(self:SetArea(undo[1], undo[2], true, false, undo[3], undo[4]))
end
end
function PANEL:CanRedo()
return #self.Redo > 0
end
function PANEL:DoRedo()
if(#self.Redo > 0) then
local redo = self.Redo[#self.Redo]
self.Redo[#self.Redo] = nil
self:SetCaret(self:SetArea(redo[1], redo[2], false, true, redo[3], redo[4]))
end
end
function PANEL:SelectAll()
self.Caret = {#self.Rows, string.len(self.Rows[#self.Rows]) + 1}
self.Start = {1, 1}
self:ScrollCaret()
end
function PANEL:_OnKeyCodeTyped(code)
self.Blink = RealTime()
local alt = input.IsKeyDown(KEY_LALT) or input.IsKeyDown(KEY_RALT)
local shift = input.IsKeyDown(KEY_LSHIFT) or input.IsKeyDown(KEY_RSHIFT)
local control = input.IsKeyDown(KEY_LCONTROL) or input.IsKeyDown(KEY_RCONTROL)
if(alt) then return end
if(control) then
if(code == KEY_A) then
self:SelectAll()
elseif(code == KEY_Z) then
self:DoUndo()
elseif(code == KEY_Y) then
self:DoRedo()
elseif(code == KEY_X) then
if(self:HasSelection()) then
self.clipboard = self:GetSelection()
self.clipboard = string.Replace(self.clipboard, "\n", "\r\n")
SetClipboardText(self.clipboard)
self:SetSelection()
end
elseif(code == KEY_C) then
if(self:HasSelection()) then
self.clipboard = self:GetSelection()
self.clipboard = string.Replace(self.clipboard, "\n", "\r\n")
SetClipboardText(self.clipboard)
end
elseif(code == KEY_UP) then
self.Scroll[1] = self.Scroll[1] - 1
if(self.Scroll[1] < 1) then self.Scroll[1] = 1 end
elseif(code == KEY_DOWN) then
self.Scroll[1] = self.Scroll[1] + 1
elseif(code == KEY_LEFT) then
if(self:HasSelection() and not shift) then
self.Start = self:CopyPosition(self.Caret)
else
self.Caret = self:getWordStart(self:MovePosition(self.Caret, -2))
end
self:ScrollCaret()
if( not shift) then
self.Start = self:CopyPosition(self.Caret)
end
elseif(code == KEY_RIGHT) then
if(self:HasSelection() and not shift) then
self.Start = self:CopyPosition(self.Caret)
else
self.Caret = self:getWordEnd(self:MovePosition(self.Caret, 1))
end
self:ScrollCaret()
if( not shift) then
self.Start = self:CopyPosition(self.Caret)
end
elseif(code == KEY_HOME) then
self.Caret[1] = 1
self.Caret[2] = 1
self:ScrollCaret()
if( not shift) then
self.Start = self:CopyPosition(self.Caret)
end
elseif(code == KEY_END) then
self.Caret[1] = #self.Rows
self.Caret[2] = 1
self:ScrollCaret()
if( not shift) then
self.Start = self:CopyPosition(self.Caret)
end
end
else
if(code == KEY_ENTER) then
local row = self.Rows[self.Caret[1]]:sub(1,self.Caret[2]-1)
local diff = (row:find("%S") or (row:len()+1))-1
local tabs = string.rep(" ", math.floor(diff / 4))
self:SetSelection("\n" .. tabs)
elseif(code == KEY_UP) then
if(self.Caret[1] > 1) then
self.Caret[1] = self.Caret[1] - 1
local length = string.len(self.Rows[self.Caret[1]])
if(self.Caret[2] > length + 1) then
self.Caret[2] = length + 1
end
end
self:ScrollCaret()
if( not shift) then
self.Start = self:CopyPosition(self.Caret)
end
elseif(code == KEY_DOWN) then
if(self.Caret[1] < #self.Rows) then
self.Caret[1] = self.Caret[1] + 1
local length = string.len(self.Rows[self.Caret[1]])
if(self.Caret[2] > length + 1) then
self.Caret[2] = length + 1
end
end
self:ScrollCaret()
if( not shift) then
self.Start = self:CopyPosition(self.Caret)
end
elseif(code == KEY_LEFT) then
if(self:HasSelection() and not shift) then
self.Start = self:CopyPosition(self.Caret)
else
self.Caret = self:MovePosition(self.Caret, -1)
end
self:ScrollCaret()
if( not shift) then
self.Start = self:CopyPosition(self.Caret)
end
elseif(code == KEY_RIGHT) then
if(self:HasSelection() and not shift) then
self.Start = self:CopyPosition(self.Caret)
else
self.Caret = self:MovePosition(self.Caret, 1)
end
self:ScrollCaret()
if( not shift) then
self.Start = self:CopyPosition(self.Caret)
end
elseif(code == KEY_PAGEUP) then
self.Caret[1] = self.Caret[1] - math.ceil(self.Size[1] / 2)
self.Scroll[1] = self.Scroll[1] - math.ceil(self.Size[1] / 2)
if(self.Caret[1] < 1) then self.Caret[1] = 1 end
local length = string.len(self.Rows[self.Caret[1]])
if(self.Caret[2] > length + 1) then self.Caret[2] = length + 1 end
if(self.Scroll[1] < 1) then self.Scroll[1] = 1 end
self:ScrollCaret()
if( not shift) then
self.Start = self:CopyPosition(self.Caret)
end
elseif(code == KEY_PAGEDOWN) then
self.Caret[1] = self.Caret[1] + math.ceil(self.Size[1] / 2)
self.Scroll[1] = self.Scroll[1] + math.ceil(self.Size[1] / 2)
if(self.Caret[1] > #self.Rows) then self.Caret[1] = #self.Rows end
if(self.Caret[1] == #self.Rows) then self.Caret[2] = 1 end
local length = string.len(self.Rows[self.Caret[1]])
if(self.Caret[2] > length + 1) then self.Caret[2] = length + 1 end
self:ScrollCaret()
if( not shift) then
self.Start = self:CopyPosition(self.Caret)
end
elseif(code == KEY_HOME) then
local row = self.Rows[self.Caret[1]]
local first_char = row:find("%S") or row:len()+1
if(self.Caret[2] == first_char) then
self.Caret[2] = 1
else
self.Caret[2] = first_char
end
self:ScrollCaret()
if( not shift) then
self.Start = self:CopyPosition(self.Caret)
end
elseif(code == KEY_END) then
local length = string.len(self.Rows[self.Caret[1]])
self.Caret[2] = length + 1
self:ScrollCaret()
if( not shift) then
self.Start = self:CopyPosition(self.Caret)
end
elseif(code == KEY_BACKSPACE) then
if(self:HasSelection()) then
self:SetSelection()
else
local buffer = self:GetArea({self.Caret, {self.Caret[1], 1}})
if(self.Caret[2] % 4 == 1 and string.len(buffer) > 0 and string.rep(" ", string.len(buffer)) == buffer) then
self:SetCaret(self:SetArea({self.Caret, self:MovePosition(self.Caret, -4)}))
else
self:SetCaret(self:SetArea({self.Caret, self:MovePosition(self.Caret, -1)}))
end
end
elseif(code == KEY_DELETE) then
if(self:HasSelection()) then
self:SetSelection()
else
local buffer = self:GetArea({{self.Caret[1], self.Caret[2] + 4}, {self.Caret[1], 1}})
if(self.Caret[2] % 4 == 1 and string.rep(" ", string.len(buffer)) == buffer and string.len(self.Rows[self.Caret[1]]) >= self.Caret[2] + 4 - 1) then
self:SetCaret(self:SetArea({self.Caret, self:MovePosition(self.Caret, 4)}))
else
self:SetCaret(self:SetArea({self.Caret, self:MovePosition(self.Caret, 1)}))
end
end
end
end
if(code == KEY_TAB or (control and (code == KEY_I or code == KEY_O))) then
if(code == KEY_O) then shift = not shift end
if(code == KEY_TAB and control) then shift = not shift end
if(self:HasSelection()) then
self:Indent(shift)
else
if(shift) then
local newpos = self.Caret[2]-4
if(newpos < 1) then newpos = 1 end
self.Start = { self.Caret[1], newpos }
if(self:GetSelection():find("%S")) then
self.Start = self:CopyPosition(self.Caret)
else
self:SetSelection("")
end
else
local count = (self.Caret[2] + 2) % 4 + 1
self:SetSelection(string.rep(" ", count))
end
end
self.TabFocus = true
end
if(control) then
self:OnShortcut(code)
end
end
function PANEL:getWordStart(caret)
local line = string.ToTable(self.Rows[caret[1]])
if(#line < caret[2]) then return caret end
for i=0,caret[2] do
if( not line[caret[2]-i]) then return {caret[1],caret[2]-i+1} end
if(line[caret[2]-i] >= "a" and line[caret[2]-i] <= "z" or line[caret[2]-i] >= "A" and line[caret[2]-i] <= "Z" or line[caret[2]-i] >= "0" and line[caret[2]-i] <= "9") then else return {caret[1],caret[2]-i+1} end
end
return {caret[1],1}
end
function PANEL:getWordEnd(caret)
local line = string.ToTable(self.Rows[caret[1]])
if(#line < caret[2]) then return caret end
for i=caret[2],#line do
if( not line[i]) then return {caret[1],i} end
if(line[i] >= "a" and line[i] <= "z" or line[i] >= "A" and line[i] <= "Z" or line[i] >= "0" and line[i] <= "9") then else return {caret[1],i} end
end
return {caret[1],#line+1}
end
function PANEL:Indent(shift)
local tab_scroll = self:CopyPosition(self.Scroll)
local tab_start, tab_caret = self:MakeSelection(self:Selection())
tab_start[2] = 1
if(tab_caret[2] ~= 1) then
tab_caret[1] = tab_caret[1] + 1
tab_caret[2] = 1
end
self.Caret = self:CopyPosition(tab_caret)
self.Start = self:CopyPosition(tab_start)
if (self.Caret[2] == 1) then
self.Caret = self:MovePosition(self.Caret, -1)
end
if(shift) then
local tmp = self:GetSelection():gsub("\n ? ? ? ?", "\n")
self:SetSelection(unindent(tmp))
else
self:SetSelection(" " .. self:GetSelection():gsub("\n", "\n "))
end
self.Caret = self:CopyPosition(tab_caret)
self.Start = self:CopyPosition(tab_start)
self.Scroll = self:CopyPosition(tab_scroll)
self:ScrollCaret()
end
function PANEL:OnTextChanged()
end
function PANEL:OnShortcut()
end
function PANEL:CheckGlobal(func)
end
pace.RegisterPanel(PANEL)