mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-17 21:53:46 +03:00
1055 lines
28 KiB
Lua
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)
|